/* FichothequeLib_API - Copyright (c) 2006-2021 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.Normalizer;
import java.text.ParseException;
import java.util.AbstractList;
import java.util.Collections;
import java.util.List;
import java.util.RandomAccess;
import net.fichotheque.Fichotheque;
import net.fichotheque.SubsetKey;
import net.fichotheque.addenda.Addenda;
import net.fichotheque.addenda.Document;
import net.fichotheque.addenda.Version;


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

    public final static List<Document> EMPTY_DOCUMENTLIST = Collections.emptyList();

    private AddendaUtils() {
    }

    public static String toTitle(Document document) {
        List<Version> versionList = document.getVersionList();
        switch (versionList.size()) {
            case 0:
                return document.getBasename();
            default:
                StringBuilder buf = new StringBuilder();
                buf.append(document.getBasename());
                buf.append(" [ ");
                boolean next = false;
                for (Version version : versionList) {
                    if (next) {
                        buf.append(" / ");
                    } else {
                        next = true;
                    }
                    buf.append(".");
                    buf.append(version.getExtension());
                }
                buf.append(" ]");
                return buf.toString();
        }
    }

    public static String toVersionKey(Version version) {
        Document document = version.getDocument();
        return document.getGlobalId() + "." + version.getExtension();
    }

    public static Version parseVersionKey(String versionKey, Fichotheque fichotheque) throws ParseException {
        int idx = versionKey.indexOf('/');
        if (idx == -1) {
            throw new ParseException("mising / character: " + versionKey, 0);
        }
        SubsetKey addendaKey = SubsetKey.parse(SubsetKey.CATEGORY_ADDENDA, versionKey.substring(0, idx));
        Addenda addenda = (Addenda) fichotheque.getSubset(addendaKey);
        if (addenda == null) {
            throw new ParseException("unknown addenda: " + versionKey, 0);
        }
        int idx2 = versionKey.indexOf('.');
        if (idx2 == -1) {
            throw new ParseException("mising . character: " + versionKey, idx + 1);
        }
        int documentid;
        try {
            documentid = Integer.parseInt(versionKey.substring(idx + 1, idx2));
        } catch (NumberFormatException nfe) {
            throw new ParseException("documentid not integer: " + versionKey, idx + 1);
        }
        Document document = addenda.getDocumentById(documentid);
        if (document == null) {
            throw new ParseException("unknwon documentid: " + versionKey, idx + 1);
        }
        Version version = document.getVersionByExtension(versionKey.substring(idx2 + 1));
        if (version == null) {
            throw new ParseException("unknwon extension: " + versionKey, idx2 + 1);
        }
        return version;
    }


    /**
     * Vérifie la validité du nom ainsi que son existence dans la base. Les
     * caractères accentués sont remplacés par des « _ », un numéro est rajouté
     * si un document du même nom existe déjà.
     */
    public static String checkBasename(String basename, Addenda addenda) {
        basename = checkNormalization(basename);
        if (basename == null) {
            basename = "document";
        }
        if (addenda.getDocumentByBasename(basename) == null) {
            return basename;
        }
        NameIncrement nameIncrement = new NameIncrement(basename, 2);
        String result;
        while (true) {
            result = nameIncrement.toString();
            if (addenda.getDocumentByBasename(result) == null) {
                break;
            }
            nameIncrement.incremente();
        }
        return result;
    }

    private static String checkNormalization(String s) {
        s = s.toLowerCase();
        s = Normalizer.normalize(s, Normalizer.Form.NFD).replaceAll("\\p{InCombiningDiacriticalMarks}+", "");
        StringBuilder buf = new StringBuilder();
        int length = s.length();
        boolean previousBlank = false;
        int nonLatinCount = 0;
        for (int i = 0; i < length; i++) {
            char carac = s.charAt(i);
            if (testChar(carac)) {
                buf.append(carac);
                previousBlank = false;
            } else if (Character.isLetter(carac)) {
                nonLatinCount++;
            } else {
                if (!previousBlank) {
                    buf.append('_');
                    previousBlank = true;
                }
            }
        }
        if (nonLatinCount > (length / 2)) {
            return null;
        }
        return buf.toString();
    }

    /**
     * Indique si l'extension est valide. Une extension est valide si elle ne
     * contient que des caractères de 0 à 9 ou de a à z (uniquement des
     * minuscules).
     */
    public static boolean testExtension(String s) {
        if ((s == null) || (s.length() == 0)) {
            return false;
        }
        for (int i = 0; i < s.length(); i++) {
            char carac = s.charAt(i);
            if ((carac >= '0') && (carac <= '9')) {
                continue;
            }
            if (((carac >= 'a') && (carac <= 'z'))) {
                continue;
            }
            return false;
        }
        return true;
    }

    /**
     * Un nom de document doit contenir que des minuscules de 'a' à 'z', il peut
     * contenir '.', '_', '-' ainsi que des chiffres à condition de ne pas
     * commencer par eux.
     *
     * Ne retourne pas d'exception mais false si basename est incorrect
     */
    public static boolean testBasename(String basename) {
        if (basename == null) {
            return false;
        }
        if (basename.length() == 0) {
            return false;
        }
        int length = basename.length();
        for (int i = 0; i < length; i++) {
            char carac = basename.charAt(i);
            if (!testChar(carac)) {
                return false;
            }
        }
        return true;
    }

    private static boolean testChar(char carac) {
        if ((carac >= '0') && (carac <= '9')) {
            return true;
        }
        if (((carac >= 'a') && (carac <= 'z'))) {
            return true;
        }
        switch (carac) {
            case '-':
            case '_':
            case '.':
                return true;
        }
        return false;
    }

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

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


    private static class NameIncrement {

        private final String basename;
        private int val;

        private NameIncrement(String basename, int val) {
            this.basename = basename;
            this.val = val;
        }

        private void incremente() {
            val++;
        }

        @Override
        public String toString() {
            return basename + "-" + val;
        }

    }


    private static class DocumentList extends AbstractList<Document> implements RandomAccess {

        private final Document[] array;

        private DocumentList(Document[] array) {
            this.array = array;
        }

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

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

    }


    private static class VersionList extends AbstractList<Version> implements RandomAccess {

        private final Version[] array;

        private VersionList(Version[] array) {
            this.array = array;
        }

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

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

    }

}
