package com.capgemini.reports.definition.r24report;

import com.capgemini.framework.common.LanguageHelper;
import com.capgemini.framework.common.StringHelper;
import com.capgemini.framework.teamcenter.authent.TcSession;
import com.capgemini.framework.teamcenter.exceptions.KurtException;
import com.capgemini.reports.definition.r15r16r17reports.AbstractExecutionR15R16R17;
import com.capgemini.reports.definition.r15r16r17reports.CommunR15R16R17ComponentVisitor;
import com.capgemini.reports.definition.r15r16r17reports.R15R16R17R24R25Keys;
import com.capgemini.reports.definition.r15r16r17reports.R15R16R17R24R25Keys.Page2;
import com.capgemini.reports.documentationobjects.DocNorm;
import com.capgemini.reports.documentationobjects.DocNormStandard;
import com.capgemini.reports.documentationobjects.DocTechnic;
import com.capgemini.reports.execution.mapconstructor.AbstractStaticTemplateMapConstructor;
import com.capgemini.reports.execution.mapconstructor.IMapConstructor;
import com.capgemini.reports.printing.PrintParameters;
import com.capgemini.reports.snecmaobjects.part.PartAeroRevision;
import com.capgemini.reports.snecmaobjects.visitor.IComponentVisitor;
import lombok.extern.slf4j.Slf4j;

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

/**
 * This class is able to put the data gathered by an {@link ExecutionR24} into some {@link PrintParameters} for printing.
 */
@Slf4j
public class R24Printer extends AbstractStaticTemplateMapConstructor<ExecutionR24, ExecutionR24> implements IMapConstructor<ExecutionR24, ExecutionR24> {
    private static final long serialVersionUID = -789205459094400840L;

    private static final String N_A = "N/A";

    private static final String EMPTY_STRING = StringHelper.EMPTY;
    private static final String UNKNOWN_STR = "???";
    private static final String SANS_OBJET = "Sans objet";
    private static final String NOT_APPLICABLE = "Not applicable ";

    private static String french(final String string) {
        return LanguageHelper.french(string);
    }

    private static String english(final String string) {
        return LanguageHelper.english(string);
    }

    /**
     * Puts the data gathered by an {@link AbstractExecutionR15R16R17} into some {@link PrintParameters} for printing.
     *
     * @param execution  the data-bearing {@link AbstractExecutionR15R16R17}
     * @param mainReport the {@link PrintParameters}
     */
    private void print(final ExecutionR24 execution, final PrintParameters mainReport) {
        // first put generic data
        DataReportPreparatorR24 preparator = (DataReportPreparatorR24) execution.getDataPreparator();
        mainReport.addReportParameters(setupStringParameters(execution));
      mainReport.addReportParameter(R15R16R17R24R25Keys.EMPTY_TABLE.toString(), preparator.isEmptyTable(execution));
        mainReport.addReportDataFromEnum(setupData(execution));

        // Now add the specific stuff
        addOptionalParameters(execution, mainReport);
    }

  private Map<R15R16R17R24R25Keys, String> setupStringParameters(final ExecutionR24 execution) {
    Map<R15R16R17R24R25Keys, String> params = new EnumMap<>(R15R16R17R24R25Keys.class);
    params.put(R15R16R17R24R25Keys.CLAUSE_PROPRIETE, execution.getClausePropriete());
    params.put(R15R16R17R24R25Keys.PRODUCT_TYPE, execution.getTypeProduit());
    params.put(R15R16R17R24R25Keys.REPORT_TITLE, getTitle());
    params.put(R15R16R17R24R25Keys.ITEM_REVISION_ID, execution.getRevisionArticle());
        String dateReleased = execution.getDateReleased();
    params.put(R15R16R17R24R25Keys.RELEASE_DATE, execution.isFrench() ? french(dateReleased) : english(dateReleased));
        return params;
    }

    private List<Map<Page2, ?>> setupData(final ExecutionR24 execution) {
        List<Map<Page2, ?>> dataTable = new ArrayList<>();
        boolean isFrench = execution.isFrench();
        for (DocNorm norm : execution.getDocNormList()) {
            dataTable.add(getDocNormLine(norm, isFrench));
        }
        for (DocNormStandard normStd : execution.getDocNormStandardList()) {
            dataTable.add(getDocNormStandardLine(normStd, isFrench));
        }
        for (DocTechnic technic : execution.getDocTechnicList()) {
            dataTable.add(getDocTechnicLine(technic, isFrench));
        }

        // If there are no documents, add an empty line to provoke band printing
        if (dataTable.isEmpty()) {
            Map<Page2, String> line = new EnumMap<>(Page2.class);
            line.put(Page2.DOC_NUMBER, "(No document found)");
            line.put(Page2.ISSUE, N_A);
            line.put(Page2.DOC_TYPE, N_A);
            line.put(Page2.DOC_TITLE, N_A);
            dataTable.add(line);
        }
        return dataTable;
    }

    private Map<Page2, String> getDocNormLine(final DocNorm docNorm, final boolean isFrench) {
        String idOrigine = docNorm.getIdOrigine();
        Map<Page2, String> line = new EnumMap<>(Page2.class);
        line.put(Page2.DOC_NUMBER, (idOrigine == null || idOrigine.isEmpty()) ? docNorm.getDocId() : idOrigine);
        line.put(Page2.ISSUE, "-");
        String typeDocNorm = docNorm.getTypeDocNorm();
        line.put(Page2.DOC_TYPE, isFrench ? french(typeDocNorm) : english(typeDocNorm));
        line.put(Page2.DOC_TITLE, isFrench ? docNorm.getItemName() : docNorm.getDesignationEn());
        return line;
    }

    private Map<Page2, String> getDocNormStandardLine(final DocNormStandard normStd, final boolean isFrench) {
        Map<Page2, String> line = new EnumMap<>(Page2.class);
        String idOrigine = normStd.getIdOrigine();
        line.put(Page2.DOC_NUMBER, (idOrigine == null || idOrigine.isEmpty()) ? normStd.getDocId() : idOrigine);
        line.put(Page2.ISSUE, "-");
        line.put(Page2.DOC_TYPE, isFrench ? "Norme pour articles standards" : "Norm for standards items");
        line.put(Page2.DOC_TITLE, isFrench ? normStd.getItemName() : normStd.getDesignationEn());
        return line;
    }

    private Map<Page2, String> getDocTechnicLine(final DocTechnic technic, final boolean isFrench) {
        Map<Page2, String> line = new EnumMap<>(Page2.class);
        String idOrigine = technic.getIdOrigine();
        line.put(Page2.DOC_NUMBER, (idOrigine == null || idOrigine.isEmpty()) ? technic.getDocId() : idOrigine);
        line.put(Page2.ISSUE, technic.getIndice());

        String typeDocTechnic1 = technic.getTypeDocTechnic1();
        String type1 = isFrench ? french(typeDocTechnic1) : english(typeDocTechnic1);
        String typeDocTechnic2 = technic.getTypeDocTechnic2();
        String type2 = isFrench ? french(typeDocTechnic2) : english(typeDocTechnic2);
        line.put(Page2.DOC_TYPE, type1 + " / " + type2);

        line.put(Page2.DOC_TITLE, isFrench ? technic.getName() : technic.getDesignationEn());
        return line;
    }

    private void addOptionalParameters(final ExecutionR24 execution, final PrintParameters mainReport) {
      Map<R15R16R17R24R25Keys, Object> params = new EnumMap<>(R15R16R17R24R25Keys.class);
        List<PartAeroRevision> partNumberRevList = execution.getPartNumberRevList();
        if (partNumberRevList != null && !partNumberRevList.isEmpty()) {
          Map<R15R16R17R24R25Keys, String> map = new EnumMap<>(R15R16R17R24R25Keys.class);
            if (partNumberRevList.size() == 1) {
                map = partNumberRevList.get(0).accept(getVisitor(execution.isFrench()));
              map.remove(R15R16R17R24R25Keys.PART_VERSION);
            } else {
              map.put(R15R16R17R24R25Keys.ITEM_ID, UNKNOWN_STR);
              map.put(R15R16R17R24R25Keys.PART_NUMBER, UNKNOWN_STR);
              map.put(R15R16R17R24R25Keys.MANUFACTURER_CODE, UNKNOWN_STR);
              map.put(R15R16R17R24R25Keys.FRENCH_DESIGNATION, UNKNOWN_STR);
              map.put(R15R16R17R24R25Keys.ENGLISH_DESIGNATION, UNKNOWN_STR);
              map.put(R15R16R17R24R25Keys.OBTURATION, execution.isFrench() ? SANS_OBJET : NOT_APPLICABLE);
              map.put(R15R16R17R24R25Keys.CLASSIFICATION, "-");
              map.put(R15R16R17R24R25Keys.FIELD_OPN_SERIALISATION, UNKNOWN_STR);
              map.put(R15R16R17R24R25Keys.LOG_DOCUMENT, execution.isFrench() ? SANS_OBJET : NOT_APPLICABLE);
            }
          map.put(R15R16R17R24R25Keys.PART_VERSION, EMPTY_STRING);
            params.putAll(map);
        } else {
            params.putAll(execution.getGenericRev().accept(getVisitor(execution.isFrench())));
        }
      params.put(R15R16R17R24R25Keys.MT_NUMBER, EMPTY_STRING);
      params.put(R15R16R17R24R25Keys.PREVIOUS_RELEASE_DATE, EMPTY_STRING);
      params.put(R15R16R17R24R25Keys.ITEM_ID, execution.getGenericItem().get_item_id());
        mainReport.addReportParameters(params);

      Map<R15R16R17R24R25Keys.SpecificR15R16, Object> map = new EnumMap<>(R15R16R17R24R25Keys.SpecificR15R16.class);
        boolean isFrench = execution.isFrench();
        String frontiereAvion = execution.getFrontiereAvion();
      map.put(R15R16R17R24R25Keys.SpecificR15R16.INSTALLATION_INTERFACE, isFrench ? french(frontiereAvion) : english(frontiereAvion));
        String frontiereCooperant = execution.getFrontiereCooperant();
      map.put(R15R16R17R24R25Keys.SpecificR15R16.PARTNER_INTERFACE, isFrench ? french(frontiereCooperant) : english(frontiereCooperant));
      map.put(R15R16R17R24R25Keys.SpecificR15R16.PRODUCT_UNIT, execution.getProductUnit().toString());
        mainReport.addReportParameters(map);
      mainReport.addReportParameter(R15R16R17R24R25Keys.SpecificR15R16.IS_PREVIOUS_DEF.toString(), false);
    }

    private String getTitle() {
        return StringHelper.EMPTY;
    }

  private IComponentVisitor<Map<R15R16R17R24R25Keys, String>> getVisitor(final boolean isFrench) {
        return new CommunR15R16R17ComponentVisitor(isFrench);
    }

    @Override
    public void constructDataMaps(TcSession session, final PrintParameters printParameters, final ExecutionR24 data) throws KurtException {
        long start = System.currentTimeMillis();
        this.print(data, printParameters);
        log.trace("Execution time : {} ms for {}", System.currentTimeMillis() - start, "data filtering");
    }
}