/* FichothequeLib_API - Copyright (c) 2019-2024 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.text.ParseException;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.RandomAccess;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.fichotheque.exportation.balayage.BalayageConstants;
import net.fichotheque.exportation.balayage.BalayageContentDescription;
import net.fichotheque.exportation.balayage.BalayageDef;
import net.fichotheque.exportation.balayage.BalayageDescription;
import net.fichotheque.exportation.balayage.BalayageMode;
import net.fichotheque.exportation.balayage.BalayageUnit;
import net.fichotheque.namespaces.BalayageSpace;
import net.mapeadores.util.attr.Attribute;
import net.mapeadores.util.exceptions.SwitchException;
import net.mapeadores.util.text.AccoladePattern;
import net.fichotheque.exportation.balayage.BalayageOutput;


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

    public final static AccoladePattern UNIQUE_PATTERN;
    public final static AccoladePattern CORPUS_PATTERN;
    public final static AccoladePattern FICHE_PATTERN;
    public final static AccoladePattern THESAURUS_PATTERN;
    public final static AccoladePattern MOTCLE_PATTERN;
    public final static AccoladePattern SPHERE_PATTERN;
    public final static AccoladePattern REDACTEUR_PATTERN;
    public final static AccoladePattern DOCUMENT_PATTERN;
    public final static AccoladePattern ILLUSTRATION_PATTERN;
    public final static List<BalayageContentDescription> EMPTY_BALAYAGECONTENTDESCRIPTIONLIST = Collections.emptyList();
    public final static List<BalayageMode> EMPTY_BALAYAGEMODELIST = Collections.emptyList();
    public final static List<BalayageDef> EMPTY_BALAYAGEDEFLIST = Collections.emptyList();
    public final static List<BalayageUnit> EMPTY_UNITLIST = Collections.emptyList();
    public final static Pattern XSLTDIR_NAME_PATTERN = Pattern.compile("^[a-zA-Z]([-_a-zA-Z0-9]*)\\.(xml|xsl)$");
    public final static Pattern EXTRACTIONDIR_NAME_PATTERN = Pattern.compile("^[a-zA-Z]([-_a-zA-Z0-9]*)\\.xml$");

    static {
        try {
            UNIQUE_PATTERN = new AccoladePattern("extraction");
            CORPUS_PATTERN = new AccoladePattern("corpus-{corpus}");
            FICHE_PATTERN = new AccoladePattern("fiche-{corpus}-{id}");
            THESAURUS_PATTERN = new AccoladePattern("thesaurus-{thesaurus}");
            MOTCLE_PATTERN = new AccoladePattern("motcle-{thesaurus}-{id}");
            SPHERE_PATTERN = new AccoladePattern("sphere-{sphere}");
            REDACTEUR_PATTERN = new AccoladePattern("redacteur-{sphere}-{id}");
            DOCUMENT_PATTERN = new AccoladePattern("{basename}");
            ILLUSTRATION_PATTERN = new AccoladePattern("{album}-{id}");
        } catch (ParseException pe) {
            throw new IllegalStateException(pe);
        }
    }

    private BalayageUtils() {

    }

    /**
     *
     * @throws IllegalArgumentException si le type est incorrect.
     */
    public static AccoladePattern getDefaultPattern(String type) {
        switch (type) {
            case BalayageConstants.UNIQUE_TYPE:
                return UNIQUE_PATTERN;
            case BalayageConstants.CORPUS_TYPE:
                return CORPUS_PATTERN;
            case BalayageConstants.FICHE_TYPE:
                return FICHE_PATTERN;
            case BalayageConstants.THESAURUS_TYPE:
                return THESAURUS_PATTERN;
            case BalayageConstants.MOTCLE_TYPE:
                return MOTCLE_PATTERN;
            case BalayageConstants.SPHERE_TYPE:
                return SPHERE_PATTERN;
            case BalayageConstants.REDACTEUR_TYPE:
                return REDACTEUR_PATTERN;
            case BalayageConstants.DOCUMENT_TYPE:
                return DOCUMENT_PATTERN;
            case BalayageConstants.ILLUSTRATION_TYPE:
                return ILLUSTRATION_PATTERN;
            default:
                throw new SwitchException("wrong type = " + type);
        }
    }

    public static AccoladePattern getPattern(BalayageOutput output, String type) {
        AccoladePattern pattern = output.getPattern();
        if (pattern == null) {
            return getDefaultPattern(type);
        }
        return pattern;
    }

    public static boolean isValidContentName(String name, String subdir) {
        if (subdir.isEmpty()) {
            switch (name) {
                case "balayage.xml":
                case "include-catalog.xml":
                    return true;
                default:
                    return false;
            }
        }
        Pattern pattern;
        switch (subdir) {
            case "xslt":
                pattern = XSLTDIR_NAME_PATTERN;
                break;
            case "extraction":
                pattern = EXTRACTIONDIR_NAME_PATTERN;
                break;
            default:
                return false;

        }
        Matcher matcher = pattern.matcher(name);
        return matcher.matches();
    }

    public static String getDefaultContent(String name, String subdir) {
        StringBuilder buf = new StringBuilder();
        buf.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
        int minLength = buf.length();
        if (subdir.isEmpty()) {
            switch (name) {
                case "balayage.xml":
                    buf.append("<balayage>\n\n</balayage>");
                    break;
                case "include-catalog.xml":
                    buf.append("<include-catalog>\n\n</include-catalog>");
            }
        } else if (subdir.equals("extraction")) {
            buf.append("<extraction>\n\n</extraction>");
        } else if ((subdir.equals("xslt")) && (name.endsWith(".xsl"))) {
            buf.append("<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"1.0\">\n\n</xsl:stylesheet>");
        }
        if (buf.length() == minLength) {
            buf.append("<xml>\n</xml>");
        }
        return buf.toString();
    }

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

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

    public static List<BalayageOutput.Param> wrap(BalayageOutput.Param[] array) {
        return new OutputParamList(array);
    }

    public static BalayageContentDescription getBalayageContentDescription(BalayageDescription balayageDescription, String contentPath) {
        for (BalayageContentDescription description : balayageDescription.getBalayageContentDescriptionList()) {
            if (description.getPath().equals(contentPath)) {
                return description;
            }
        }
        return null;
    }

    public static List<BalayageMode> getBalayageModeList(BalayageDef balayageDef) {
        Attribute attribute = balayageDef.getAttributes().getAttribute(BalayageSpace.MODES_KEY);
        if (attribute == null) {
            return EMPTY_BALAYAGEMODELIST;
        }
        List<BalayageMode> result = new ArrayList<BalayageMode>();
        for (String value : attribute) {
            String name;
            boolean withCurrentDateLimit = false;
            int idx = value.indexOf('|');
            if (idx > 0) {
                name = value.substring(0, idx).trim();
                String option = value.substring(idx + 1).trim();
                if (option.equals("current")) {
                    withCurrentDateLimit = true;
                }
            } else {
                name = value;
            }
            result.add(toBalayageMode(name, withCurrentDateLimit));
        }
        return result;
    }

    public static boolean containsMode(BalayageDef balayageDef, String modeName) {
        for (BalayageMode balayageMode : getBalayageModeList(balayageDef)) {
            if (balayageMode.getName().equals(modeName)) {
                return true;
            }
        }
        return false;
    }

    public static BalayageMode getMode(BalayageDef balayageDef, String modeName) {
        for (BalayageMode balayageMode : getBalayageModeList(balayageDef)) {
            if (balayageMode.getName().equals(modeName)) {
                return balayageMode;
            }
        }
        return null;
    }

    public static BalayageMode toBalayageMode(String name, boolean currentDateLimit) {
        return new InternalBalayageMode(name, currentDateLimit);
    }

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

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

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

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


    private static class BalayageUnitList extends AbstractList<BalayageUnit> implements RandomAccess {

        private final BalayageUnit[] array;

        private BalayageUnitList(BalayageUnit[] array) {
            this.array = array;
        }

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

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

    }


    private static class BalayageDescriptionList extends AbstractList<BalayageDescription> implements RandomAccess {

        private final BalayageDescription[] array;

        private BalayageDescriptionList(BalayageDescription[] array) {
            this.array = array;
        }

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

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

    }


    private static class BalayageDefList extends AbstractList<BalayageDef> implements RandomAccess {

        private final BalayageDef[] array;

        private BalayageDefList(BalayageDef[] array) {
            this.array = array;
        }

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

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

    }


    private static class BalayageContentDescriptionList extends AbstractList<BalayageContentDescription> implements RandomAccess {

        private final BalayageContentDescription[] array;

        private BalayageContentDescriptionList(BalayageContentDescription[] array) {
            this.array = array;
        }

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

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

    }


    private static class InternalBalayageMode implements BalayageMode {

        private final String name;
        private final boolean withCurrentDateLimit;

        InternalBalayageMode(String name, boolean withCurrentDateLimit) {
            this.name = name;
            this.withCurrentDateLimit = withCurrentDateLimit;
        }

        @Override
        public String getName() {
            return name;
        }

        @Override
        public boolean isWithCurrentDateLimit() {
            return withCurrentDateLimit;
        }

    }


    private static class BalayageModeList extends AbstractList<BalayageMode> implements RandomAccess {

        private final BalayageMode[] array;

        private BalayageModeList(BalayageMode[] array) {
            this.array = array;
        }

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

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

    }


    private static class BalayageOutputList extends AbstractList<BalayageOutput> implements RandomAccess {

        private final BalayageOutput[] array;

        private BalayageOutputList(BalayageOutput[] array) {
            this.array = array;
        }

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

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

    }


    private static class OutputParamList extends AbstractList<BalayageOutput.Param> implements RandomAccess {

        private final BalayageOutput.Param[] array;

        private OutputParamList(BalayageOutput.Param[] array) {
            this.array = array;
        }

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

        @Override
        public BalayageOutput.Param get(int index) {
            return array[index];
        }

    }

}
