package com.capgemini.reports.bomlineholder;

import com.capgemini.framework.teamcenter.object.types.SnecmaObjectTypes;
import com.capgemini.reports.bomlineholder.BOMLineHolder.BOMLineHolderState;
import com.capgemini.reports.exceptions.InvalidInputException;
import com.capgemini.reports.exceptions.MalformedNomenclatureException;
import com.capgemini.reports.exceptions.NotFoundException;
import com.capgemini.reports.snecmaobjects.admin.RFAdminAPRevision;
import com.capgemini.reports.snecmaobjects.admin.RFAdminARevision;
import com.capgemini.reports.snecmaobjects.admin.RFAdminRevision;

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

/**
 * Helps find data inside a {@link BOMLineHolder}.
 */
public final class BOMLineHolderHelper {

    private BOMLineHolderHelper() {
        // Nothing
    }

    /**
     * Finds the first instance of the requested type, amongst a {@link BOMLineHolder}'s children.
     *
     * @param bomLineHolder the BOMLineHolder. If null an {@link InvalidInputException} will be raised
     * @param requestedType the requested Child Type
     * @return the first child matching the requested type
     * @throws NotFoundException     when no child of the type could be found
     * @throws InvalidInputException if the input BOMLineHolder was null
     */
    public static BOMLineHolder findFirst(final BOMLineHolder bomLineHolder, final String requestedType) throws NotFoundException,
            InvalidInputException {
        if (bomLineHolder == null) {
            throw nullBomLineProvided(requestedType);
        }
        if (bomLineHolder.getState() == BOMLineHolderState.RAW) {
            for (BOMLineHolder child : bomLineHolder.getBomChilds()) {
                if (requestedType.equals(child.getItemRevision().getTypeObject().getName())) {
                    return child;
                }
            }
            // If we findAll here, no object was found, so we'll raise an exception
            String fatherType = bomLineHolder.getObjectType();
            String fatherID = bomLineHolder.getItemRevision().get_item_id();
            throw new NotFoundException("No " + requestedType + " found below RAW BOMLineHolder: " + fatherType + " (" + fatherID + ")");
        } else {
            for (BOMLineHolder child : bomLineHolder.getBomChilds()) {
                if (requestedType.equals(child.getSnecmaObject().getType())) {
                    return child;
                }
            }
            throw new NotFoundException("No " + requestedType + " found below : " + bomLineHolder.getSnecmaObject().getItemId());
        }
    }

    /**
     * Finds the all the instances of the requested type, amongst a {@link BOMLineHolder}'s children.
     *
     * @param bomLineHolder the BOMLineHolder. If null an {@link InvalidInputException} will be raised
     * @param requestedType the requested Child Type
     * @return a {@link List} containing every Child whose type is the one searched for
     * @throws InvalidInputException if the input BOMLineHolder was null
     */
    public static List<BOMLineHolder> findAll(final BOMLineHolder bomLineHolder, final String requestedType) throws InvalidInputException {
        if (bomLineHolder == null) {
            throw nullBomLineProvided(requestedType);
        }
        List<BOMLineHolder> list = new ArrayList<>();
        for (BOMLineHolder child : bomLineHolder.getBomChilds()) {
            if (requestedType.equals(child.getSnecmaObject().getType())) {
                list.add(child);
            }
        }
        return list;
    }

    /**
     * Finds and returns the first child which is a 'RF Admin A', amongst a RF's children.
     *
     * @param aRF the RF
     * @return the first child which is a 'RF Admin A'
     * @throws NotFoundException     when no child of the type could be found
     * @throws InvalidInputException if the input BOMLineHolder was null
     */
    public static RFAdminARevision findRfAdminA(final BOMLineHolder aRF) throws NotFoundException, InvalidInputException {
        return (RFAdminARevision) findFirst(aRF, SnecmaObjectTypes.RF_ADMIN_A_REVISION).getSnecmaObject();
    }

    /**
     * Finds and returns the first child which is a 'RF Admin AP', amongst a RF's children.
     *
     * @param aRF the RF
     * @return the first child which is a 'RF Admin AP'
     * @throws NotFoundException     when no child of the type could be found
     * @throws InvalidInputException if the input BOMLineHolder was null
     */
    public static RFAdminAPRevision findRfAdminAP(final BOMLineHolder aRF) throws NotFoundException, InvalidInputException {
        return (RFAdminAPRevision) findFirst(aRF, SnecmaObjectTypes.RF_ADMIN_AP_REVISION).getSnecmaObject();
    }

    /**
     * Finds and returns the first child which is a 'RF Admin', amongst a RF's children.
     *
     * @param aRF the RF
     * @return the first child which is a 'RF Admin'
     * @throws NotFoundException     when no child of the type could be found
     * @throws InvalidInputException if the input BOMLineHolder was null
     */
    public static RFAdminRevision findRfAdmin(final BOMLineHolder aRF) throws NotFoundException, InvalidInputException {
        return (RFAdminRevision) findFirst(aRF, SnecmaObjectTypes.RF_ADMIN_REVISION).getSnecmaObject();
    }

    /**
     * Finds and returns the 'RF Generic', of a Generic.
     *
     * @param aGeneric the Generic
     * @return the first child which is a 'RF Generic'
     * @throws MalformedNomenclatureException when no child of the type could be found (there SHOULD be one)
     * @throws InvalidInputException          if the input BOMLineHolder was null
     */
    public static BOMLineHolder findRfGenericBL(final BOMLineHolder aGeneric) throws InvalidInputException, MalformedNomenclatureException {
        try {
            return findFirst(aGeneric, SnecmaObjectTypes.RF_GENERIC_REVISION);
        } catch (NotFoundException e) {
            if (aGeneric.getState() == BOMLineHolderState.RAW) {
                throw new MalformedNomenclatureException("No RF Generic found below RAW Generic: " + aGeneric.getBomLineObject().getUid(), e);
            } else {
                throw new MalformedNomenclatureException("No RF Generic found below Generic: " + aGeneric.getSnecmaObject().getItemId(), e);
            }
        }
    }

    private static InvalidInputException nullBomLineProvided(final String requestedType) {
        return new InvalidInputException("Unable to find a '" + requestedType + "' because the BomLineHolder was null");
    }
}
