package com.capgemini.framework.teamcenter.resolution.structureservice;

import com.capgemini.framework.teamcenter.ServiceDataHelper;
import com.capgemini.framework.teamcenter.authent.TcSession;
import com.capgemini.framework.teamcenter.exceptions.EmptyNomenclatureException;
import com.capgemini.framework.teamcenter.resolution.filtering.ITcTypeFilter;
import com.capgemini.reports.bomlineholder.BOMLineHolder;
import com.teamcenter.services.strong.cad._2008_06.StructureManagement.*;
import com.teamcenter.soa.client.model.strong.BOMLine;
import com.teamcenter.soa.client.model.strong.ItemRevision;
import lombok.extern.slf4j.Slf4j;

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

/**
 * Helper dedicated to single level resolution of configurations.
 **/
@Slf4j
public class SingleStructureServiceCallerRaw implements StructureServiceRaw {
    private static final String NO_FILTER = "None2";
    private static final String ERROR_MSG_NULL_BOM_LINE_PROVIDED = "Unable to resolve from a null BOMLine.";
    private static final String ERROR_MSG_NO_RESULT_FOR_EXPLOSION = "No result for explosion";
    private final TcSession session;

    /**
     * Constructor for single level resolution.
     */
    public SingleStructureServiceCallerRaw(final TcSession session) {
        this.session = session;
    }


    /**
     * Run a resolution from a {@link BOMLine}.<br>
     * In this SINGLE mode, the explosion is performed only 1 link.<br/>
     * The entry {@link BOMLine} cannot have a null TC {@link BOMLine}.
     */
    @Override
    public BOMLineHolder resolve(final BOMLine aTopBom, final ITcTypeFilter filter)
            throws EmptyNomenclatureException {
        if (aTopBom != null) {
            return expandBom(aTopBom, filter);
        } else {
            throw new EmptyNomenclatureException(ERROR_MSG_NULL_BOM_LINE_PROVIDED);
        }
    }

    /**
     * Expands a {@link BOMLine} object, by exactly one BOM link. <br>
     */
    private BOMLineHolder expandBom(final BOMLine topObject, final ITcTypeFilter filter)
            throws EmptyNomenclatureException {
        // Expand one level
        List<BOMLine> topObjectList = new ArrayList<>();
        topObjectList.add(topObject);
        ExpandPSOneLevelOutput[] expand = expandOneLevel(topObjectList);

        // Expecting only one response because only one BOMLine was provided
        if (expand == null || expand.length == 0) {
            throw new EmptyNomenclatureException(ERROR_MSG_NO_RESULT_FOR_EXPLOSION);
        }
        ExpandPSOneLevelOutput output = expand[0];

        // Browse this BOMLine's children
        return extractAllChildren(output, filter);
    }

    /**
     * Expand ONE level of configuration.<br>
     *
     * @param allTopBomLine Top item {@link BOMLine}
     * @return a table of raw {@link ExpandPSOneLevelOutput} each containing parent and its children
     */
    private ExpandPSOneLevelOutput[] expandOneLevel(final Collection<BOMLine> allTopBomLine) {
        if (allTopBomLine == null || allTopBomLine.isEmpty()) {
            // Early return
            return new ExpandPSOneLevelOutput[0];
        }
        log.debug("Start expand one level...");
        BOMLine[] bomLines = allTopBomLine.toArray(new BOMLine[allTopBomLine.size()]);

        ExpandPSOneLevelInfo expandInfo = new ExpandPSOneLevelInfo();
        expandInfo.parentBomLines = bomLines;
        expandInfo.excludeFilter = NO_FILTER;

        ExpandPSOneLevelPref expandPref = new ExpandPSOneLevelPref();
        expandPref.expItemRev = false;

        ExpandPSOneLevelResponse2 response = session.getStructureManagementService().expandPSOneLevel(expandInfo, expandPref);
        ServiceDataHelper.displayErrors(response.serviceData);
        log.debug("End expand one level");

        return response.output;
    }

    /**
     * Expands all the topObjects provided, using the {@link ITcTypeFilter}.
     */
    public final List<BOMLineHolder> expandEach(final Collection<BOMLine> topObjects, final ITcTypeFilter filter)
            throws EmptyNomenclatureException {
        if (topObjects.isEmpty()) {
            return new ArrayList<>();
        }
        // Expand one level
        ExpandPSOneLevelOutput[] expand = expandOneLevel(topObjects);

        if (expand == null || expand.length == 0) {
            throw new EmptyNomenclatureException(ERROR_MSG_NO_RESULT_FOR_EXPLOSION);
        }

        List<BOMLineHolder> result = new ArrayList<>();
        // Browse each BOMLine's children
        for (ExpandPSOneLevelOutput output : expand) {
            result.add(extractAllChildren(output, filter));
        }
        return result;
    }


    private BOMLineHolder extractAllChildren(final ExpandPSOneLevelOutput output, final ITcTypeFilter filter) {
        List<BOMLineHolder> childList = new ArrayList<>();
        for (ExpandPSData currentChildData : output.children) {
            BOMLine bomLine = currentChildData.bomLine;
            ItemRevision theObject = (ItemRevision) currentChildData.objectOfBOMLine;
            if (bomLine == null || theObject == null) {
                continue;
            }
            if (filter.isIncluded(theObject.getTypeObject().getName())) {
                childList.add(new BOMLineHolder(session, bomLine, theObject, new ArrayList<BOMLineHolder>()));
            }
        }
        return new BOMLineHolder(session, output.parent.bomLine, (ItemRevision) output.parent.objectOfBOMLine, childList);
    }
}
