package com.capgemini.reports.definition.r15r16r17reports;

import com.capgemini.framework.common.LanguageHelper;
import com.capgemini.framework.common.ResourceHelper;
import com.capgemini.framework.common.SettingsHelper;
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.exceptions.FileLoadingException;
import com.capgemini.reports.execution.AbstractExecutionReport;
import com.capgemini.reports.execution.mapconstructor.AbstractStaticTemplateMapConstructor;
import com.capgemini.reports.printing.PrintParameters;
import com.capgemini.reports.snecmaobjects.visitor.IComponentVisitor;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;

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

/**
 * This class allows printing of severel elements that are found in both the R15, R16 and R17 reports.
 *
 * @param <T> describes which report's {@link AbstractExecutionReport} this printer will print. Must be a subclass of {@link AbstractExecutionR15R16R17}.
 */
@Slf4j
@EqualsAndHashCode(callSuper = true)
public abstract class AbstractR15R16R17Printer<T extends AbstractExecutionR15R16R17<T>> extends AbstractStaticTemplateMapConstructor<T, T> {
    private static final long serialVersionUID = 6114330530182125707L;

    private static final String SNECMA_LOGO_PNG = "/Jasper/images/Snecma_logo.png";
    private static final String CONFIDENTIEL_INDUSTRIE_JPG = "/Jasper/images/confidentiel_industrie.jpg";
    private static final String N_A = "N/A";
    private static final int INT_3 = 3;
    private static final List<String> PRODUCT_TYPES_LIST = SettingsHelper.getProductTypes();

    /**
     * Returns true if the product is in the product types list.<br>
     * It means that the DocDrawing id could'nt be available in the report.<br>
     * Their id values should be replaced by the id of the Part.
     *
     * @param productType {@link String} - the product type
     * @return true if it is not authorized, false otherwise
     */
    protected static boolean isNotAuthorized(final String productType) {
        if (productType == null || productType.isEmpty()) {
            return false;
        }

        // Take the three letters of the left side of the product type value
        String type =(productType.length()<INT_3 )? productType: productType.substring(0, INT_3);

        // Compare to
        for (String notAuthorisedType : PRODUCT_TYPES_LIST) {
            if (notAuthorisedType.contains(type)) {
                return true;
            }
        }
        return false;
    }

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

    protected 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}
     */
    public void print(final T execution, final PrintParameters mainReport) {
        // first put generic data, that is common for R15-R16-R17
        AbstractDataReportPreparatorR15R16R17<T> preparator = (AbstractDataReportPreparatorR15R16R17<T>) execution.getDataPreparator();
        mainReport.addReportParameters(setupStringParameters(execution));
      mainReport.addReportParameter(R15R16R17R24R25Keys.EMPTY_TABLE.toString(), preparator.isEmptyTable(execution));
        try {
          mainReport.addReportParameter(R15R16R17R24R25Keys.SNECMA_LOGO, ResourceHelper.loadImage(SNECMA_LOGO_PNG));
        } catch (FileLoadingException e) {
            log.warn("Could not read the Snecma logo", e);
        }

        if (preparator.isConfidential(execution)) {
            try {
              mainReport.addReportParameter(R15R16R17R24R25Keys.WATERMARK, ResourceHelper.loadImage(CONFIDENTIEL_INDUSTRIE_JPG));
            } catch (FileLoadingException e) {
                log.warn("Could not read the 'confidentiel industrie' watermark", e);
            }
        }
        mainReport.addReportDataFromEnum(setupData(execution));

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

  protected Map<R15R16R17R24R25Keys, String> setupStringParameters(final T 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(execution));
    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 T 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));
        }

        dataTable.addAll(addExtraDocLines(execution));

        // 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;
    }

    protected abstract List<Map<Page2, String>> addExtraDocLines(final T execution);

    protected 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;
    }

    protected 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;
    }

    protected 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;
    }

    /**
     * Adds parameters that are not common to R15-R16-R17 reports.
     *
     * @param execution  the data-bearing {@link AbstractExecutionR15R16R17}
     * @param mainReport the {@link PrintParameters}
     */
    protected abstract void addOptionalParameters(final T execution, final PrintParameters mainReport);

    protected abstract String getTitle(final T execution);

  protected abstract IComponentVisitor<Map<R15R16R17R24R25Keys, String>> getVisitor(final boolean isFrench);
}
