package com.capgemini.reports.teamcenter.queries;

import com.capgemini.framework.teamcenter.authent.TcSession;
import com.teamcenter.schemas.soa._2006_03.exceptions.ServiceException;
import com.teamcenter.services.strong.query._2007_06.Finder.FindWorkspaceObjectsResponse;
import com.teamcenter.services.strong.query._2007_06.Finder.WSOFindCriteria;
import com.teamcenter.services.strong.query._2007_06.Finder.WSOFindSet;
import com.teamcenter.soa.client.model.ModelObject;
import com.teamcenter.soa.client.model.strong.*;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;

/**
 * Searches for a specific item by its name, with its type specified by the Type Parameter.
 *
 * @param <T> The type of object to look for. T must extends {@link ModelObject}. <br>
 *            Example: {@link Item}, {@link ItemRevision}, {@link Form}, {@link RevisionRule}.
 */
@Slf4j
public class TypeQuery<T extends ModelObject> {

    private final TcSession session;

    private final Class<T> typeParameterClass;

    /**
     * Basic constructor.
     *
     * @param session            required to perform the query.
     * @param typeParameterClass the Teamcenter {@code class} of the requested object.
     */
    private TypeQuery(final TcSession session, final Class<T> typeParameterClass) {
        this.session = session;
        this.typeParameterClass = typeParameterClass;
    }

    /**
     * Searches for a specific {@link RevisionRule} by its name.
     *
     * @param session    an active session
     * @param objectName the name of the Revision Rule
     * @return a {@link List} of results
     */
    public static List<RevisionRule> getRevisionRule(final TcSession session, final String objectName) {
        List<RevisionRule> list = new TypeQuery<>(session, RevisionRule.class).get(objectName);
        List<RevisionRule> output = new ArrayList<>();
        for (ModelObject foundObject : list) {
            output.add((RevisionRule) foundObject);
        }
        return output;
    }

    /**
     * Perform a {@link TypeQuery} using the specified object name.<br>
     * Usage: new TypeQuery<ItemRevision>(getSession(), ItemRevision.class).findAll(itemID);
     *
     * @param objectName the name of the object
     * @return a List containing all matching elements.
     */
    private List<T> get(final String objectName) {
        WSOFindCriteria criteria = new WSOFindCriteria();
        criteria.objectType = typeParameterClass.getSimpleName();
        criteria.objectName = objectName;
        criteria.scope = "WSO_scope_All";

        WSOFindSet findSet = new WSOFindSet();
        findSet.criterias = new WSOFindCriteria[]{criteria};
        FindWorkspaceObjectsResponse findWorkspaceObjects;
        try {
            findWorkspaceObjects = session.getFinderService().findWorkspaceObjects(new WSOFindSet[]{findSet});
        } catch (ServiceException e) {
            log.warn("finderService failed to deliver WorkspaceObjects", e);
            return new ArrayList<>();
        }
        if (findWorkspaceObjects.outputList.length > 0) {
            // Need to cast each sub-element of the list
            List<T> list = new ArrayList<>();
            for (WorkspaceObject workspaceObject : findWorkspaceObjects.outputList[0].foundObjects) {
                list.add(typeParameterClass.cast(workspaceObject));
            }
            return list;
        }
        // Nothing found
        return new ArrayList<>();
    }
}
