package com.capgemini.reports.snecmaobjects;

import com.capgemini.framework.teamcenter.authent.TcSession;
import com.capgemini.framework.teamcenter.object.pom.status.StatusEnum;
import com.capgemini.framework.teamcenter.object.pom.status.TcReleaseStatus;
import com.capgemini.framework.teamcenter.resolution.filtering.ITcTypeFilter;
import com.capgemini.reports.documentationobjects.AbstractDoc;
import com.capgemini.reports.documentationobjects.DocFactory;
import com.capgemini.reports.releasestatus.RevisionSelector;
import com.capgemini.reports.releasestatus.RevisionStatusAndDate;
import com.teamcenter.soa.client.model.ModelObject;
import com.teamcenter.soa.client.model.strong.Form;
import com.teamcenter.soa.client.model.strong.Item;
import com.teamcenter.soa.client.model.strong.ItemRevision;
import com.teamcenter.soa.client.model.strong.ReleaseStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

import static com.capgemini.framework.teamcenter.TeamcenterObjectProperties.TC_IS_REPRESENTED_BY;
import static com.capgemini.framework.teamcenter.object.SnecmaObjectProperties.*;

/**
 * Abstract class which centralize the methods of all the SnecmaObject.
 */
public abstract class AbstractSnecmaObject implements ISnecmaObject {
    private static final long serialVersionUID = -9057538534616767099L;
    protected final TcSession session;
    protected final ItemRevision itemRevision;

    private Form itemMasterForm;
    private List<TcReleaseStatus> statuses;
    private Form itemMasterFormRev;
    private static final Logger LOGGER = LoggerFactory.getLogger(AbstractSnecmaObject.class);

    public AbstractSnecmaObject(final TcSession session, final ItemRevision itemRevision) {
        this.session = session;
        this.itemRevision = itemRevision;
    }

    @Override
    public String getType() {
        return itemRevision.getTypeObject().getName();
    }

    @Override
    public String getItemId() {
        return itemRevision.get_item_id();
    }

    @Override
    public String getRevisionId() {
        return itemRevision.get_item_revision_id();
    }

    @Override
    public String getName() {
        return itemRevision.get_object_name();
    }

    protected Form getMasterForm() {
        if (itemMasterForm == null) {
            session.loadProperties(itemRevision, "items_tag");
            Item item = itemRevision.get_items_tag();
            session.loadProperties(item, "item_master_tag");
            itemMasterForm = item.get_item_master_tag();
            session.loadProperties(itemMasterForm);
        }
        return itemMasterForm;
    }

    protected Form getMasterFormRev() {
        if (itemMasterFormRev == null) {
            itemMasterFormRev = itemRevision.get_item_master_tag();
            session.loadProperties(itemMasterFormRev);
        }
        return itemMasterFormRev;
    }

    /**
     * Method to get the modelObject representing the 'Spare Part' Form if it existed.
     * Else return null
     * */
    public ModelObject getSparePartsForm() {
        session.loadProperties(itemRevision, "IMAN_specification");
        ModelObject[] formArray = itemRevision.get_IMAN_specification();
        session.loadProperties(formArray);
        for (ModelObject form : formArray) {
            if (form != null && "Spare Parts".equals(form.getTypeObject().getName())) {
                return form;
            }
        }
        return null;
    }

  public boolean hasStatus(final StatusEnum... statuses) {
    for (TcReleaseStatus releaseStatus : getStatusList()) {
      if (releaseStatus.isOneOf(statuses)) {
        return true;
      }
    }
    return false;
  }

    public List<TcReleaseStatus> getStatusList() {
        if (statuses == null) {
            statuses = new ArrayList<>();
            session.loadProperties(itemRevision, "release_status_list");
            ReleaseStatus[] releaseStatuses = itemRevision.get_release_status_list();
            session.loadProperties(releaseStatuses);
            for (ReleaseStatus releaseStatus : releaseStatuses) {
                TcReleaseStatus tcReleaseStatus = new TcReleaseStatus(session, releaseStatus);
                statuses.add(tcReleaseStatus);
            }
        }
        return statuses;
    }

    public List<AbstractDoc> getDefinitionDocumentsOfTypes(final ITcTypeFilter typesFilter, final Map<String, AbstractDoc> docCache) {
        List<AbstractDoc> definitionDocuments = new ArrayList<>();
        session.loadProperties(itemRevision, PV_DEFINITION_DOCS);
        ModelObject[] definitionDocObjects = itemRevision.getPropertyObject(PV_DEFINITION_DOCS).getModelObjectArrayValue();
        session.loadProperties(definitionDocObjects);
        for (ModelObject definitionDocObject : definitionDocObjects) {
            String docType = definitionDocObject.getTypeObject().getName();
            if (typesFilter.isIncluded(docType)) {
                definitionDocuments.add(getAbstractDoc((ItemRevision) definitionDocObject, docCache));
            }
        }

        return definitionDocuments;
    }

    public List<AbstractDoc> getRepresentedDocumentsOfTypes(final ITcTypeFilter typesFilter, final Map<String, AbstractDoc> docCache) {
        List<AbstractDoc> representedDocuments = new ArrayList<>();
        session.loadProperties(itemRevision, TC_IS_REPRESENTED_BY);
        ModelObject[] representedDocObjects = itemRevision.getPropertyObject(TC_IS_REPRESENTED_BY).getModelObjectArrayValue();
        session.loadProperties(representedDocObjects);
        for (ModelObject representedDocObject : representedDocObjects) {
            String docType = representedDocObject.getTypeObject().getName();
            if (typesFilter.isIncluded(docType)) {
                representedDocuments.add(getAbstractDoc((ItemRevision) representedDocObject, docCache));
            }
        }

        return representedDocuments;
    }

    /**
     * Returns all the ChangeRequests of the CR pseudo-folder as a list of ModelObjects
     */
    public List<ModelObject> getCRPseudoFolderContent() {
        List<ModelObject> crRelatedList = new ArrayList<>();
        session.loadProperties(itemRevision, PV4_RUNTIME_CR_RELATED);
        ModelObject[] crRelatedArray = itemRevision.getPropertyObject(PV4_RUNTIME_CR_RELATED).getModelObjectArrayValue();
        session.loadProperties(crRelatedArray);
        for (ModelObject cr : crRelatedArray) {
            crRelatedList.add(cr);
        }
        return crRelatedList;
    }

    /**
     * Returns all the ChangeNotices of the CN pseudo-folder as a list of ModelObjects
     */
    public List<ModelObject> getCNPseudoFolderContent() {
        List<ModelObject> cnRelatedList = new ArrayList<>();
        session.loadProperties(itemRevision, PV4_RUNTIME_CN_RELATED);
        ModelObject[] cnRelatedArray = itemRevision.getPropertyObject(PV4_RUNTIME_CN_RELATED).getModelObjectArrayValue();
        session.loadProperties(cnRelatedArray);
        for (ModelObject cr : cnRelatedArray) {
            cnRelatedList.add(cr);
        }
        return cnRelatedList;
    }

    /**
     * Returns all the Reference documents.
     */
    public List<AbstractDoc> getReferenceDocumentsOfTypes(final ITcTypeFilter typesFilter, final RevisionSelector revisionSelector, final Map<String, AbstractDoc> docCache) {
        List<AbstractDoc> referencedDocuments = new ArrayList<>();
        session.loadProperties(itemRevision, PV_REFERENCE_DOCS);
        ModelObject[] referencedDocObjects = itemRevision.getPropertyObject(PV_REFERENCE_DOCS).getModelObjectArrayValue();
        session.loadProperties(referencedDocObjects);
        for (ModelObject representedDocObject : referencedDocObjects) {
            String docType = representedDocObject.getTypeObject().getName();
            if (typesFilter.isIncluded(docType)) {
                referencedDocuments.add(getAbstractDoc(revisionSelector, (Item) representedDocObject, docCache));
            }
        }
        return referencedDocuments;
    }

    private AbstractDoc getAbstractDoc(final RevisionSelector revisionSelector, final Item docItem, final Map<String, AbstractDoc> docCache) {
        AbstractDoc cachedDoc = docCache.get(docItem.getUid());
        if (cachedDoc == null) {
            RevisionStatusAndDate revisionStatusAndDate = revisionSelector.selectRevision(session, docItem);
            AbstractDoc abstractDoc = DocFactory.build(session, revisionStatusAndDate.getItemRevision());
            docCache.put(docItem.getUid(), abstractDoc);
            return abstractDoc;
        } else {
            return cachedDoc;
        }
    }

    private AbstractDoc getAbstractDoc(final ItemRevision docRev, final Map<String, AbstractDoc> docCache) {
        AbstractDoc cachedDoc = docCache.get(docRev.getUid());
        if (cachedDoc == null) {
            AbstractDoc abstractDoc = DocFactory.build(session, docRev);
            docCache.put(docRev.getUid(), abstractDoc);
            return abstractDoc;
        } else {
            return cachedDoc;
        }
    }

    public String getCRPseudoFolderContentAsString() {
        StringBuilder stringBuilder = new StringBuilder();
        try {
            List<ModelObject> CRList = getCRPseudoFolderContent();
            for (ModelObject CR : CRList) {
                if (stringBuilder.length() != 0) {
                    stringBuilder.append(";");
                }
                stringBuilder.append(CR.getPropertyObject(ITEM_ID).getStringValue());
            }
        } catch (Exception e) {
            LOGGER.debug("Error during Searching CR of component Id [{}] Revision [{}] : [{}]", getItemId(), getRevisionId(), e.getMessage());
        }
        return stringBuilder.toString();
    }

    public String getCNPseudoFolderContentAsString() {
        StringBuilder stringBuilder = new StringBuilder();
        try {
            List<ModelObject> CNList = getCNPseudoFolderContent();
            for (ModelObject CN : CNList) {
                if (stringBuilder.length() != 0) {
                    stringBuilder.append(";");
                }
                stringBuilder.append(CN.getPropertyObject(ITEM_ID).getStringValue());
            }
        } catch (Exception e) {
            LOGGER.debug("Error during Searching CN of component Id [{}] Revision [{}] : [{}]", getItemId(), getRevisionId(), e.getMessage());
        }
        return stringBuilder.toString();
    }

    /**
     * Return true if it is a Admin.
     *
     * @return the boolean
     */
    @Override
    public boolean isAdmin() {
        return false;
    }

    /**
     * Return true if it is a Generic.
     *
     * @return the boolean
     */
    @Override
    public boolean isGeneric() {
        return false;
    }


    /**
     * Return true if it is a Super Model.
     *
     * @return the boolean
     */
    @Override
    public boolean isSuperModel() {
        return false;
    }

    /**
     * Return true if it is a Part.
     *
     * @return the boolean
     */
    @Override
    public boolean isPart() {
        return false;
    }

    /**
     * Return true if it is a RF.
     *
     * @return the boolean
     */
    @Override
    public boolean isRF() {
        return false;
    }

    /**
     * Return true if it is a RF Generic.
     *
     * @return the boolean
     */
    @Override
    public boolean isRFGeneric() {
        return false;
    }

    /**
     * Return true if it is a RF Part or RF Part Assemb.
     *
     * @return the boolean
     */
    @Override
    public boolean isRFPartOrPartAssemb() {
        return false;
    }

    /**
     * Return true if it is a MockUp.
     *
     * @return the boolean
     */
    @Override
    public boolean isMockup() {
        return false;
    }

    /**
     * Return true if it is a Product.
     *
     * @return the boolean
     */
    @Override
    public boolean isProduct() {
        return false;
    }

    @Override
    public boolean isDescriptif() {
        return false;
    }

    @Override
    public String toString() {
        return getType() + " with ID= " + getItemId() + " with REVISION= " + getRevisionId();
    }
}
