/* FichothequeLib_API - Copyright (c) 2007-2022 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;

import java.util.AbstractList;
import java.util.Collections;
import java.util.List;
import java.util.RandomAccess;
import java.util.function.Predicate;
import net.fichotheque.SubsetItem;
import net.fichotheque.corpus.SubfieldValue;
import net.fichotheque.corpus.fiche.FicheItem;
import net.fichotheque.extraction.ExtractionContext;
import net.fichotheque.extraction.def.ExtractionDef;
import net.fichotheque.format.FormatContext;
import net.fichotheque.format.FormatSource;
import net.fichotheque.format.PatternDef;
import net.fichotheque.format.Tokenizer;
import net.fichotheque.format.Tokens;
import net.fichotheque.pointeurs.SubsetItemPointeur;
import net.mapeadores.util.instruction.Instruction;
import net.mapeadores.util.instruction.InstructionResolver;
import net.mapeadores.util.instruction.Instructions;
import net.mapeadores.util.instruction.InstructionsParser;
import net.mapeadores.util.logging.ErrorMessageException;
import net.fichotheque.exportation.table.CellConverterProvider;


/**
 *
 * @author Vincent Calame
 */
public final class FormatterUtils {

    public final static Tokens EMPTY_TOKENS = new EmptyTokens();
    public final static Tokenizer EMPTY_TOKENIZER = new EmptyTokenizer();
    public final static FormatSource.History EMPTY_FORMATSOURCEHISTORY = new EmptyHistory();

    private FormatterUtils() {
    }

    public static Object[] parsePattern(InstructionResolver instructionResolver, String s) throws ErrorMessageException {
        Instructions instructions = InstructionsParser.parse(s);
        int partSize = instructions.size();
        Object[] result = new Object[partSize];
        for (int i = 0; i < partSize; i++) {
            Instructions.Part part = instructions.get(i);
            if (part instanceof Instructions.LiteralPart) {
                result[i] = ((Instructions.LiteralPart) part).getText();
            } else {
                Instruction instruction = ((Instructions.InstructionPart) part).getInstruction();
                Object resultObject = instructionResolver.resolve(instruction);
                if (resultObject == null) {
                    throw new ErrorMessageException("_ error.unknown.pattern", "{" + instruction.get(0).getKey() + "}");
                }
                result[i] = resultObject;
            }
        }
        return result;
    }

    public static SubfieldValue toSubfieldValue(String s) {
        return new InternalSubfieldValue(s);
    }

    public static SubfieldValue toSubfieldValue(FicheItem ficheItem) {
        return new InternalSubfieldValue(ficheItem);
    }

    public static FormatSource toFormatSource(SubsetItemPointeur subsetItemPointeur, ExtractionContext extractionContext, Predicate<SubsetItem> predicate, FormatContext formatContext) {
        if (subsetItemPointeur == null) {
            throw new IllegalArgumentException("subsetItemPointeur is null");
        }
        return new InternalFormatSource(subsetItemPointeur, extractionContext, predicate, formatContext, TableExportUtils.EMPTY_CELLCONVERTERPROVIDER, EMPTY_FORMATSOURCEHISTORY, null);
    }

    public static FormatSource toFormatSource(SubsetItemPointeur subsetItemPointeur, ExtractionContext extractionContext, Predicate<SubsetItem> predicate, FormatContext formatContext, CellConverterProvider cellConverterProvider, FormatSource.History history, FormatSource.ExtractionInfo extractionInfo) {
        if (subsetItemPointeur == null) {
            throw new IllegalArgumentException("subsetItemPointeur is null");
        }
        return new InternalFormatSource(subsetItemPointeur, extractionContext, predicate, formatContext, cellConverterProvider, history, extractionInfo);
    }

    public static Tokenizer initConstantTokenizer(String pattern) {
        if (pattern == null) {
            return EMPTY_TOKENIZER;
        }
        SingletonTokens constantSourceTokens = new SingletonTokens(pattern);
        return new ConstantTokenizer(constantSourceTokens);
    }

    public static List<PatternDef> wrap(PatternDef[] array) {
        return new PatternDefList(array);
    }

    public static List<Tokenizer> wrap(Tokenizer[] array) {
        return new TokenizerList(array);
    }

    public static Tokens toTokens(String value) {
        if (value == null) {
            throw new IllegalArgumentException("value is null");
        }
        return new SingletonTokens(value);
    }

    public static Tokens toTokens(List<String> valueList) {
        return new ListTokens(valueList);
    }

    public static FormatSource.ExtractionInfo toExtractionInfo(String errorLog) {
        return new InternalExtractionInfo(null, errorLog);
    }

    public static FormatSource.ExtractionInfo toExtractionInfo(ExtractionDef extractionDef) {
        return new InternalExtractionInfo(extractionDef, null);
    }


    private static class InternalSubfieldValue implements SubfieldValue {

        private final Object value;

        public InternalSubfieldValue(Object value) {
            this.value = value;
        }

        @Override
        public Object getValue() {
            return value;
        }

    }


    private static class InternalFormatSource implements FormatSource {

        private final SubsetItemPointeur subsetItemPointeur;
        private final ExtractionContext extractionContext;
        private final Predicate<SubsetItem> predicate;
        private final CellConverterProvider cellConverterProvider;
        private final FormatContext formatContext;
        private final FormatSource.History history;
        private final FormatSource.ExtractionInfo extractionInfo;

        private InternalFormatSource(SubsetItemPointeur subsetItemPointeur, ExtractionContext extractionContext, Predicate<SubsetItem> predicate, FormatContext formatContext, CellConverterProvider cellConverterProvider, FormatSource.History history, FormatSource.ExtractionInfo extractionInfo) {
            this.subsetItemPointeur = subsetItemPointeur;
            this.extractionContext = extractionContext;
            this.predicate = predicate;
            this.formatContext = formatContext;
            this.cellConverterProvider = cellConverterProvider;
            this.history = history;
            this.extractionInfo = extractionInfo;
        }

        @Override
        public SubsetItemPointeur getSubsetItemPointeur() {
            return subsetItemPointeur;
        }

        @Override
        public ExtractionContext getExtractionContext() {
            return extractionContext;
        }

        @Override
        public Predicate<SubsetItem> getGlobalPredicate() {
            return predicate;
        }

        @Override
        public CellConverterProvider getCellConverterProvider() {
            return cellConverterProvider;
        }

        @Override
        public FormatContext getFormatContext() {
            return formatContext;
        }

        @Override
        public FormatSource.History getHistory() {
            return history;
        }

        @Override
        public FormatSource.ExtractionInfo getExtractionInfo() {
            return extractionInfo;
        }

    }


    private static class InternalExtractionInfo implements FormatSource.ExtractionInfo {

        private final ExtractionDef extractionDef;
        private final String errorLog;

        private InternalExtractionInfo(ExtractionDef extractionDef, String errorLog) {
            this.extractionDef = extractionDef;
            this.errorLog = errorLog;

        }

        @Override
        public ExtractionDef getExtractionDef() {
            return extractionDef;
        }

        @Override
        public String getErrorLog() {
            return errorLog;
        }

    }


    private static class EmptyTokenizer implements Tokenizer {

        private EmptyTokenizer() {
        }

        @Override
        public Tokens tokenize(FormatSource formatSource) {
            return EMPTY_TOKENS;
        }

    }


    private static class EmptyTokens extends AbstractList<String> implements Tokens {

        private EmptyTokens() {
        }

        @Override
        public int size() {
            return 0;
        }

        @Override
        public String get(int i) {
            throw new IndexOutOfBoundsException("size = 0");
        }

    }


    private static class ConstantTokenizer implements Tokenizer {

        private final Tokens tokens;

        private ConstantTokenizer(Tokens tokens) {
            this.tokens = tokens;
        }

        @Override
        public Tokens tokenize(FormatSource formatSource) {
            return tokens;
        }

    }


    private static class SingletonTokens extends AbstractList<String> implements Tokens {

        private final String value;

        private SingletonTokens(String value) {
            this.value = value;
        }

        @Override
        public int size() {
            return 1;
        }

        @Override
        public String get(int i) {
            return value;
        }

    }


    private static class ListTokens extends AbstractList<String> implements Tokens {

        private final List<String> valueList;

        private ListTokens(List<String> valueList) {
            this.valueList = valueList;
        }

        @Override
        public int size() {
            return valueList.size();
        }

        @Override
        public String get(int i) {
            return valueList.get(i);
        }

    }


    private static class PatternDefList extends AbstractList<PatternDef> implements RandomAccess {

        private final PatternDef[] array;

        private PatternDefList(PatternDef[] array) {
            this.array = array;
        }

        @Override
        public int size() {
            return array.length;
        }

        @Override
        public PatternDef get(int index) {
            return array[index];
        }

    }


    private static class TokenizerList extends AbstractList<Tokenizer> implements RandomAccess {

        private final Tokenizer[] array;

        private TokenizerList(Tokenizer[] array) {
            this.array = array;
        }

        @Override
        public int size() {
            return array.length;
        }

        @Override
        public Tokenizer get(int index) {
            return array[index];
        }

    }


    private static class EmptyHistory implements FormatSource.History {

        private final List<String> emptyList = Collections.emptyList();

        private EmptyHistory() {

        }

        @Override
        public List<String> getPreviousFormatList() {
            return emptyList;
        }

    }

}
