package com.capgemini.reports.bomlineholder;

import com.capgemini.framework.teamcenter.authent.TcSession;
import com.capgemini.framework.teamcenter.exceptions.EmptyNomenclatureException;
import com.capgemini.reports.bomlineholder.BOMLineHolder.BOMLineHolderState;
import com.capgemini.reports.exceptions.NotSupportedException;
import com.capgemini.reports.filter.IFilterExplosionNomenclature;
import com.capgemini.reports.snecmaobjects.ISnecmaObject;
import com.capgemini.reports.snecmaobjects.SnecmaObjectFactory;
import com.teamcenter.soa.client.model.ModelObject;
import com.teamcenter.soa.client.model.Type;
import com.teamcenter.soa.client.model.strong.ItemRevision;
import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * Build a BOMLineHolder.
 */
@Slf4j
public final class BOMLineHolderFactory {

    private BOMLineHolderFactory() {
        // Hiding this constructor to make the class static.
    }

    /**
     * Build an expansion of BOM.
     */
    public static BOMLineHolder buildTopBom(final TcSession session, final BOMLineHolder aTopBomLine,
                                            final IFilterExplosionNomenclature filter) throws EmptyNomenclatureException {
        // Map containing all the SnecmaObject ever built (starting empty)
        Map<String, ISnecmaObject> snecmaObjectMap = new HashMap<>();
        log.debug("Starting to build top Item.");
        try {
            buildExpansionBomChildRec(session, aTopBomLine, snecmaObjectMap, filter);
        } catch (NotSupportedException e) {
            log.error("Unable to build the topLine of the expansion BOM", e);
        }
        return aTopBomLine;
    }

    /**
     * Builds a {@link BOMLineHolder}. Its {@link ISnecmaObject} will be created using the provided {@link ItemRevision}.
     */
    public static BOMLineHolder buildTopBom(final TcSession session, final BOMLineHolder aTopBomLine) {
        try {
            loadItemProperties(session, aTopBomLine, new HashMap<String, ISnecmaObject>());
        } catch (NotSupportedException e) {
            log.error("Unable to build the Snecmaobject: Type Not Supported", e);
        }
        return aTopBomLine;
    }

    /**
     * Build recursively an expansion of BOM.
     */
    private static void buildExpansionBomChildRec(final TcSession session, final BOMLineHolder aBomLineHolder, final Map<String, ISnecmaObject> snecmaObjectMap, final IFilterExplosionNomenclature filter) throws NotSupportedException {
        buildThisBOMLineHolder(session, aBomLineHolder, snecmaObjectMap);
        // Build the children
        for (Iterator<BOMLineHolder> bomIterator = aBomLineHolder.getBomChilds().iterator(); bomIterator.hasNext(); ) {
            BOMLineHolder child = bomIterator.next();
            ItemRevision childItemRev = child.getItemRevision();
            if (childItemRev == null) {
                continue;
            }
            Type objectType = childItemRev.getTypeObject();
            if (objectType != null && !filter.isExcluded(session, child)) {
                try {
                    buildExpansionBomChildRec(session, child, snecmaObjectMap, filter);
                    child.setParent(aBomLineHolder);
                } catch (NotSupportedException e) {
                    log.error("Unable to build the Snecmaobject: Type Not Supported. Removing the object from the BOM...", e);
                    bomIterator.remove();
                }
            } else {
                bomIterator.remove(); // remove unwanted objects or objects which are null (no access rights etc.)
            }
        }
    }

    /**
     * Builds a BOMLineHolder by creating a {@link ISnecmaObject} for every TC {@link ModelObject} found in this {@link } and its children.<br>
     * The BOMLineHolder will therefore be <i>{@link BOMLineHolderState#PROCESSING}</i>.
     */
    private static void buildThisBOMLineHolder(final TcSession session, final BOMLineHolder aTopBomLine,
                                               final Map<String, ISnecmaObject> snecmaObjectMap) throws NotSupportedException {
        loadItemProperties(session, aTopBomLine, snecmaObjectMap);
        //aTopBomLine.retrieveAltOrDetPseudoFolder(session, aTopBomLine.getItemRevision());
    }


    /**
     * Loads properties that are specific to the {@link ItemRevision} carried by the bomLine.<br>
     * Will attach a {@link ISnecmaObject} to the {@link BOMLineHolder} and make its state {@link BOMLineHolderState#PROCESSING}.
     */
    private static void loadItemProperties(final TcSession session, final BOMLineHolder aTopBomLine,
                                           final Map<String, ISnecmaObject> snecmaObjectMap) throws NotSupportedException {
        ItemRevision theTopItemRev = aTopBomLine.getItemRevision();
        // Try to retrieve an already built SnecmaObject in the cache
        ISnecmaObject theSnecmaObject = snecmaObjectMap.get(theTopItemRev.getUid());

        if (theSnecmaObject == null) {
            // The object did not exist in the cache, now build it and update the cache
            theSnecmaObject = SnecmaObjectFactory.build(session, theTopItemRev);
            snecmaObjectMap.put(theTopItemRev.getUid(), theSnecmaObject);
        }

        // The object has been obtained (either with the cache or from scratch), now put it in its BOMLineHolder
        aTopBomLine.setSnecmaObject(theSnecmaObject);
    }
}
