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

import com.capgemini.framework.teamcenter.authent.TcSession;
import com.capgemini.framework.teamcenter.exceptions.EmptyNomenclatureException;
import com.capgemini.framework.teamcenter.exceptions.NoSuchBomViewException;
import com.capgemini.framework.teamcenter.resolution.filtering.ITcTypeFilter;
import com.capgemini.framework.teamcenter.resolution.structureservice.SingleStructureServiceCallerRaw;
import com.capgemini.reports.bomlineholder.BOMLineHolder;
import com.capgemini.reports.exceptions.InvalidInputException;
import com.capgemini.reports.exceptions.MalformedNomenclatureException;
import com.capgemini.reports.filter.IFilterExplosionNomenclature;
import com.capgemini.reports.teamcenter.resolution.IBOMLineExplorer;
import com.teamcenter.soa.client.model.strong.BOMLine;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

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

/**
 * Helper dedicated to single level resolution of configurations.
 **/
@Slf4j
public class SingleStructureServiceCaller implements IStructureServiceCaller {

    private final TcSession session;

    /**
     * Constructor for single level resolution.
     *
     * @param session {@link TcSession} - Teamcenter session
     */
    public SingleStructureServiceCaller(final TcSession session) {
        this.session = session;
    }

    @Override
    public BOMLineHolder resolve(final BOMLine topBomLine, final IFilterExplosionNomenclature filter) throws EmptyNomenclatureException, NoSuchBomViewException {
        return new SingleStructureServiceCallerRaw(session).resolve(topBomLine, filter);
    }

    /**
     * This method allows the caller to finely explode a nomenclature under a certain configuration.<br>
     * It allows fine control of which BoLines are opened, through the use of an {@link IBOMLineExplorer} which can open a BomLine one at a time.<br>
     * All the work will be performed using a single BomWindow.
     */
    public BOMLineHolder resolve(final BOMLine topBomLine, final IBOMLineExplorer bomLineExplorer) throws EmptyNomenclatureException, NoSuchBomViewException, InvalidInputException, MalformedNomenclatureException {
        SingleStructureServiceCallerRaw impl = new SingleStructureServiceCallerRaw(session);
        // Do the exploration while the window is open
        return bomLineExplorer.explore(session, new SingleLevelExpander(impl), new BOMLineHolder(topBomLine));
    }

    /**
     * This class exists in the context of a bomWindow, and can expand a bom one level below, with a type filter.<br>
     * It is only able to expand a Bom while said window is open.
     */
    @AllArgsConstructor(access = AccessLevel.PRIVATE)
    public final class SingleLevelExpander {

        private final SingleStructureServiceCallerRaw impl;

        /**
         * Expands a bom one level below the provided {@link BOMLineHolder}.
         *
         * @param target the parent {@link BOMLineHolder} whose children will be loaded
         * @param filter a {@link ITcTypeFilter} to keep only the wanted objects types.
         * @throws EmptyNomenclatureException if the nomenclature is empty
         */
        public BOMLineHolder expand(final BOMLineHolder target, final ITcTypeFilter filter) throws EmptyNomenclatureException {
            return impl.resolve(target.getBomLineObject(), filter);
        }

        /**
         * Expands a bom one level below the provided {@link BOMLineHolder}.
         *
         * @param targetBomLineHolders the parent {@link BOMLineHolder} whose children will be loaded
         * @param filter               a {@link ITcTypeFilter} to keep only the wanted objects types.
         * @throws EmptyNomenclatureException if the nomenclature is empty
         */
        public List<BOMLineHolder> expandEach(final List<BOMLineHolder> targetBomLineHolders, final ITcTypeFilter filter) throws EmptyNomenclatureException {
            List<BOMLineHolder> bomLines = new ArrayList<>(targetBomLineHolders.size());
            for (BOMLineHolder holder : targetBomLineHolders) {
                bomLines.add(impl.resolve(holder.getBomLineObject(), filter));
            }
            return bomLines;
        }
    }
}
