/* FichothequeLib_API - Copyright (c) 2013-2020 Vincent Calame - Exemole
 * Logiciel libre donné sous triple licence :
 * 1) selon les termes de la CeCILL V2
 * 2) selon les termes de l’EUPL V.1.1
 * 3) selon les termes de la GNU GPLv3
 * Voir le fichier licences.txt
 */


package net.fichotheque.utils.selection;

import net.fichotheque.corpus.fiche.DateItem;
import net.fichotheque.corpus.fiche.FicheItem;
import net.fichotheque.corpus.fiche.FicheItems;
import net.fichotheque.corpus.fiche.NumberItem;
import net.fichotheque.corpus.metadata.FieldKey;
import net.fichotheque.pointeurs.FichePointeur;
import net.fichotheque.selection.FieldTest;
import net.fichotheque.selection.SelectionContext;
import net.fichotheque.utils.PointeurUtils;
import net.mapeadores.util.conditions.ConditionsConstants;
import net.mapeadores.util.conditions.ConditionsUtils;
import net.mapeadores.util.conditions.TextTestEngine;
import net.mapeadores.util.exceptions.SwitchException;
import net.mapeadores.util.primitives.DateFilter;
import net.mapeadores.util.primitives.DateFilterUtils;
import net.mapeadores.util.primitives.Ranges;
import net.mapeadores.util.conditions.TextCondition;


/**
 *
 * @author Vincent Calame
 */
public class FieldTestFactory {

    public static final FieldTest FALSE_FIELDTEST = new ConstantFieldTest(false);
    public static final FieldTest TRUE_FIELDTEST = new ConstantFieldTest(true);

    private FieldTestFactory() {
    }

    public static FieldTest newFieldTest(TextCondition fieldCondition, SelectionContext selectionContext, FieldKey[] fieldKeyArray) {
        short conditionType = ConditionsUtils.getConditionType(fieldCondition);
        switch (conditionType) {
            case ConditionsConstants.IMPOSSIBLE_CONDITION:
                return FALSE_FIELDTEST;
            case ConditionsConstants.NEUTRAL_CONDITION:
            case ConditionsConstants.UNKNOWN_CONDITION:
                return TRUE_FIELDTEST;
            case ConditionsConstants.NOTEMPTY_CONDITION:
                return new EmptyFieldTest(false, fieldKeyArray);
            case ConditionsConstants.EMPTY_CONDITION:
                return new EmptyFieldTest(true, fieldKeyArray);
            case ConditionsConstants.PARTIAL_CONDITION:
            case ConditionsConstants.PARTIALOREMPTY_CONDITION:
                return new ContentFieldTest(fieldCondition, selectionContext, fieldKeyArray);
            default:
                throw new SwitchException("state = " + conditionType);
        }
    }

    public static FieldTest newDateFieldTest(DateFilter dateIntegerFilter, FieldKey[] fieldKeyArray) {
        return new DateIntegerFilterFieldTest(dateIntegerFilter, fieldKeyArray);
    }

    public static FieldTest newEmptyFieldTest(boolean isEmpty, FieldKey[] fieldKeyArray) {
        return new EmptyFieldTest(isEmpty, fieldKeyArray);
    }

    public static FieldTest newRangesFieldTest(Ranges ranges, FieldKey[] fieldKeyArray) {
        return new RangeListFieldTest(ranges, fieldKeyArray);
    }


    private static class EmptyFieldTest implements FieldTest {

        private final FieldKey[] fieldKeyArray;
        private final boolean isEmpty;

        private EmptyFieldTest(boolean isEmpty, FieldKey[] fieldKeyArray) {
            this.fieldKeyArray = fieldKeyArray;
            this.isEmpty = isEmpty;
        }

        @Override
        public boolean success(FichePointeur fichePointeur) {
            int length = fieldKeyArray.length;
            for (int i = 0; i < length; i++) {
                FieldKey fieldKey = fieldKeyArray[i];
                Object obj = fichePointeur.getValue(fieldKey);
                if (obj != null) {
                    return (!isEmpty);
                }
            }
            return isEmpty;
        }

    }


    private static class ConstantFieldTest implements FieldTest {

        private final boolean constantAnswer;

        private ConstantFieldTest(boolean constantAnswer) {
            this.constantAnswer = constantAnswer;
        }

        @Override
        public boolean success(FichePointeur fichePointeur) {
            return constantAnswer;
        }

    }


    private static class ContentFieldTest implements FieldTest {

        private final SelectionContext selectionContext;
        private final FieldKey[] fieldKeyArray;
        private final TextTestEngine textTestEngine;

        private ContentFieldTest(TextCondition contentCondition, SelectionContext selectionContext, FieldKey[] fieldKeyArray) {
            this.selectionContext = selectionContext;
            this.fieldKeyArray = fieldKeyArray;
            this.textTestEngine = TextTestEngine.newInstance(contentCondition, selectionContext.getWorkingLang());
        }

        @Override
        public boolean success(FichePointeur fichePointeur) {
            textTestEngine.start();
            for (FieldKey fieldKey : fieldKeyArray) {
                Object obj = fichePointeur.getValue(fieldKey);
                if (obj == null) {
                    obj = "";
                }
                PointeurUtils.testContent(selectionContext, textTestEngine, obj);
                if (textTestEngine.canStop()) {
                    break;
                }
            }
            return textTestEngine.getResult();
        }

    }


    private static class DateIntegerFilterFieldTest implements FieldTest {

        private final FieldKey[] fieldKeyArray;
        private final DateFilter dateFilter;

        private DateIntegerFilterFieldTest(DateFilter dateFilter, FieldKey[] fieldKeyArray) {
            this.dateFilter = dateFilter;
            this.fieldKeyArray = fieldKeyArray;
        }

        @Override
        public boolean success(FichePointeur fichePointeur) {
            int length = fieldKeyArray.length;
            for (int i = 0; i < length; i++) {
                FieldKey fieldKey = fieldKeyArray[i];
                Object obj = fichePointeur.getValue(fieldKey);
                if (obj == null) {
                    continue;
                }
                if (obj instanceof DateItem) {
                    if (dateFilter.containsDate(((DateItem) obj).getDate())) {
                        return true;
                    }
                } else if (obj instanceof FicheItems) {
                    FicheItems ficheItems = (FicheItems) obj;
                    int size = ficheItems.size();
                    Object obj1 = ficheItems.get(0);
                    if (!(obj1 instanceof DateItem)) {
                        continue;
                    }
                    DateItem startDateItem = (DateItem) obj1;
                    if (size == 1) {
                        if (DateFilterUtils.intersects(startDateItem.getDate(), dateFilter)) {
                            return true;
                        }
                    } else {
                        Object obj2 = ficheItems.get(1);
                        if (!(obj2 instanceof DateItem)) {
                            continue;
                        }
                        DateItem endDateItem = (DateItem) obj2;
                        if (DateFilterUtils.intersects(startDateItem.getDate(), endDateItem.getDate(), dateFilter)) {
                            return true;
                        }
                    }
                }
            }
            return false;
        }

    }


    private static class RangeListFieldTest implements FieldTest {

        private final FieldKey[] fieldKeyArray;
        private final Ranges ranges;

        private RangeListFieldTest(Ranges ranges, FieldKey[] fieldKeyArray) {
            this.ranges = ranges;
            this.fieldKeyArray = fieldKeyArray;
        }

        @Override
        public boolean success(FichePointeur fichePointeur) {
            int length = fieldKeyArray.length;
            for (int i = 0; i < length; i++) {
                FieldKey fieldKey = fieldKeyArray[i];
                Object obj = fichePointeur.getValue(fieldKey);
                if (obj == null) {
                    continue;
                }
                if (obj instanceof NumberItem) {
                    int intg = (int) ((NumberItem) obj).getDecimal().getPartieEntiere();
                    if (ranges.contains(intg)) {
                        return true;
                    }
                } else if (obj instanceof FicheItems) {
                    FicheItems ficheItems = (FicheItems) obj;
                    int size = ficheItems.size();
                    for (int j = 0; j < size; j++) {
                        FicheItem ficheItem = ficheItems.get(j);
                        if (ficheItem instanceof NumberItem) {
                            int intg = (int) ((NumberItem) ficheItem).getDecimal().getPartieEntiere();
                            if (ranges.contains(intg)) {
                                return true;
                            }
                        }
                    }
                }
            }
            return false;
        }

    }

}
