package com.capgemini.reports.definition.r07report.data.sorter;

import com.capgemini.reports.bomlineholder.flattener.line.SnecmaLine;
import com.capgemini.reports.definition.r07report.data.TransformerR07;
import com.capgemini.reports.definition.r07report.data.filter.ConfigurationFilterR07;
import com.capgemini.reports.definition.r07report.data.filter.ConfigurationFilterR07.Diff;
import com.capgemini.reports.definition.r07report.printing.EnumMapDelta;
import lombok.AllArgsConstructor;
import lombok.Getter;

import java.util.*;

/**
 * Sorts the various differences spotted between the two configuration: puts the lines in the original nomenclature's order.
 */
public class R07DataSorter {

    private static final boolean CONF1 = true;
    private static final boolean CONF2 = false;
    private static final boolean EXCLU = true;
    private static final boolean BOTH_CONF = false;

    /**
     * Returns true when there is a diff between the two objects.
     */
    private static <T extends Comparable<T>> Boolean safeDiff(final T obj1, final T obj2) {
        if (obj1 == null && obj2 == null) {
            return false;
        }
        if (obj1 != null && obj2 != null) {
            return obj1.compareTo(obj2) != 0;
        }
        return true;
    }

    /**
     * Main method of the class: sorts the filter's unique diff content back into the original nombenclature's order.
     *
     * @param filter the filter containging the diff data
     * @return a collection of {@link ConfLine}s
     */
    public List<ConfLine> sort(final ConfigurationFilterR07 filter) {
        List<ConfLine> list = new ArrayList<>();
        for (SnecmaLine<String, TransformerR07> line : filter.getExclusiveConf1()) {
            list.add(new ConfLine(line, line.getLineNumber(), CONF1, EXCLU, null));
        }
        for (SnecmaLine<String, TransformerR07> line : filter.getExclusiveConf2()) {
            list.add(new ConfLine(line, line.getLineNumber(), CONF2, EXCLU, null));
        }
        for (Diff diff : filter.getPropDiff()) {
            SnecmaLine<String, TransformerR07> conf1 = diff.getFirst();
            SnecmaLine<String, TransformerR07> conf2 = diff.getSecond();

            // Create the line comparison map to know which cells to highlight in yellow
            Map<EnumMapDelta, Boolean> delta = new EnumMap<>(EnumMapDelta.class);
            delta.put(EnumMapDelta.ALT_ECART, safeDiff(conf1.getALT(), conf2.getALT()));
            delta.put(EnumMapDelta.ARTICLE_GESTION_ECART, safeDiff(conf1.getArticleGestion(), conf2.getArticleGestion()));
            delta.put(EnumMapDelta.CLASSEMENT_ECART, safeDiff(conf1.getClassement(), conf2.getClassement()));
            delta.put(EnumMapDelta.ID_RECEPTEUR_ECART, safeDiff(conf1.getIDReceptor(), conf2.getIDReceptor()));
            delta.put(EnumMapDelta.LISTE_FOURNISSEUR_ECART, compareArray(conf1.getSuppliersList(), conf2.getSuppliersList()));
            delta.put(EnumMapDelta.NOM_RECEPTEUR_ECART, safeDiff(conf1.getNameReceptor(), conf2.getNameReceptor()));
            delta.put(EnumMapDelta.NOM_REFERENCE_ECART, safeDiff(conf1.getNameReference(), conf2.getNameReference()));
            delta.put(EnumMapDelta.PANACHAGE_ECART, safeDiff(conf1.getPanachage(), conf2.getPanachage()));
            delta.put(EnumMapDelta.QUANTITE_CUMULEE_ECART, safeDiff(conf1.getQuantiteCumulee(), conf2.getQuantiteCumulee()));
            delta.put(EnumMapDelta.QUANTITE_ECART, safeDiff(conf1.getQuantite(), conf2.getQuantite()));
            delta.put(EnumMapDelta.REF_FABRICANT_ECART, safeDiff(conf1.getIDComponent(), conf2.getIDComponent()));
            delta.put(EnumMapDelta.REFERENCE_ASSOCIEE_ECART, safeDiff(conf1.getReferenceAssociated(), conf2.getReferenceAssociated()));
            delta.put(EnumMapDelta.REFERENCE_CONCEPTEUR_ECART, safeDiff(conf1.getReferenceConceptor(), conf2.getReferenceConceptor()));
            delta.put(EnumMapDelta.REFERENCE_TRANSPOSEE_ECART, safeDiff(conf1.getReferenceTransposed(), conf2.getReferenceTransposed()));
            delta.put(EnumMapDelta.REPERE_FONCTIONNEL_ECART, safeDiff(conf1.getRepereName(), conf2.getRepereName()));
            delta.put(EnumMapDelta.RESPONSABLE_REPERE_ECART, safeDiff(conf1.getResponsableRepere(), conf2.getResponsableRepere()));
            delta.put(EnumMapDelta.RESPONSABLE_CONCEPTION_ECART, safeDiff(conf1.getResponsableConception(), conf2.getResponsableConception()));
            delta.put(EnumMapDelta.SUIVI_BESOIN_ECART, safeDiff(conf1.getSB(), conf2.getSB()));
            delta.put(EnumMapDelta.SUIVI_DUREE_VIE_ECART, safeDiff(conf1.getSuiviDureeVie(), conf2.getSuiviDureeVie()));
            delta.put(EnumMapDelta.SUIVI_MONTAGE_ECART, safeDiff(conf1.getSuiviMontage(), conf2.getSuiviMontage()));
            delta.put(EnumMapDelta.SUIVI_UTILISATION_ECART, safeDiff(conf1.getSuiviUtilisation(), conf2.getSuiviUtilisation()));
            delta.put(EnumMapDelta.TYPE_REFERENCE_ECART, safeDiff(conf1.getTypeReference(), conf2.getTypeReference()));

            // Both have the same line number: they will appear side-by-side in the report!
            int lineNumber = Math.min(conf1.getLineNumber(), conf2.getLineNumber());
            list.add(new ConfLine(conf1, lineNumber, CONF1, BOTH_CONF, delta));
            list.add(new ConfLine(conf2, lineNumber, CONF2, BOTH_CONF, delta));
        }

        // Do the sorting now
        Collections.sort(list, new Comparator<ConfLine>() {

            @Override
            public int compare(final ConfLine line1, final ConfLine line2) {
                return line1.getNum() - line2.getNum();
            }

        });
        return list;
    }

    /**
     * Compares two lists, returns true if there is a DIFFERENCE.
     */
    private boolean compareArray(final String[] conf1, final String[] conf2) {
        boolean result = false;
        if (conf1.length != conf2.length) {
            result = true;
        } else {
            for (int i = 0; i < conf1.length; i++) {
                if (conf1[i] != null && conf2[i] != null) {
                    return conf1[i].compareTo(conf2[i]) != 0;
                } else if (conf1[i] != null || conf2[i] != null) {
                    return true;
                }
            }
        }
        return result;
    }

    /**
     * Regroups all the data regarding the comparison between two.
     */
    @AllArgsConstructor
    @Getter
    public static class ConfLine {
        private final SnecmaLine<?, ?> line;
        int num;
        boolean conf1;
        boolean exclusive;
        Map<EnumMapDelta, Boolean> hasMod;

        @Override
        public String toString() {
            return String.format("%03d", num) + (exclusive ? " EXCLU " : "  CONF ") + (conf1 ? "1 " : "2 ") + line.toString();
        }
    }
}
