/* FichothequeLib_API - Copyright (c) 2007-2023 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.List;
import java.util.RandomAccess;
import net.fichotheque.Fichotheque;
import net.fichotheque.SubsetKey;
import net.fichotheque.corpus.Corpus;
import net.fichotheque.corpus.FicheMeta;
import net.fichotheque.corpus.fiche.FicheAPI;
import net.fichotheque.corpus.fiche.FicheItem;
import net.fichotheque.corpus.fiche.FicheItems;
import net.fichotheque.corpus.fiche.PersonItem;
import net.fichotheque.corpus.metadata.CorpusField;
import net.fichotheque.corpus.metadata.FieldKey;
import net.fichotheque.sphere.LoginKey;
import net.fichotheque.sphere.Redacteur;
import net.fichotheque.sphere.Sphere;
import net.mapeadores.util.annotation.Nullable;
import net.mapeadores.util.models.PersonCore;
import net.mapeadores.util.models.PersonCoreUtils;


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

    public final static short MALFORMED_KEY = 1;
    public final static short UNKNOWN_SPHERE = 2;
    public final static short UNKNOWN_LOGIN = 3;

    private SphereUtils() {
    }

    /**
     * Un identifiant de connexion (login) doit répondre à l'expression
     * rationnelle : [a-zA-Z][a-zA-Z0-9]*.
     */
    public static boolean testLogin(String login) {
        try {
            LoginKey.checkLogin(login);
            return true;
        } catch (ParseException pe) {
            return false;
        }
    }

    public static Redacteur getRedacteur(Fichotheque fichotheque, String redacteurGlobalId) {
        try {
            return (Redacteur) FichothequeUtils.parseGlobalId(redacteurGlobalId, fichotheque, SubsetKey.CATEGORY_SPHERE, null);
        } catch (ParseException pe) {
            return null;
        }
    }

    public static PersonCore toPersonCore(Fichotheque fichotheque, PersonItem personne) {
        String redacteurGlobalId = personne.getRedacteurGlobalId();
        if (redacteurGlobalId != null) {
            Redacteur redacteur = getRedacteur(fichotheque, redacteurGlobalId);
            if (redacteur != null) {
                return redacteur.getPersonCore();
            } else {
                return PersonCoreUtils.toPersonCore(redacteurGlobalId, "", "", false);
            }
        } else {
            return personne.getPersonCore();
        }
    }

    public static boolean testRedacteurs(Corpus corpus, Redacteur redacteur) {
        String globalId = redacteur.getGlobalId();
        for (FicheMeta ficheMeta : corpus.getFicheMetaList()) {
            if (ficheMeta.belongsTo(globalId)) {
                return true;
            }
        }
        return false;
    }

    public static boolean testRedacteursAndFields(Corpus corpus, Redacteur redacteur, List<CorpusField> personneFieldList) {
        boolean redacTest = testRedacteurs(corpus, redacteur);
        if (redacTest) {
            return true;
        } else if (personneFieldList.isEmpty()) {
            return false;
        }
        FieldKey[] fieldKeyArray = CorpusMetadataUtils.toFieldKeyArray(personneFieldList);
        for (FicheMeta ficheMeta : corpus.getFicheMetaList()) {
            FicheAPI fiche = corpus.getFicheAPI(ficheMeta, false);
            for (FieldKey fieldKey : fieldKeyArray) {
                Object value = fiche.getValue(fieldKey);
                if (value != null) {
                    switch (fieldKey.getCategory()) {
                        case FieldKey.PROP_CATEGORY: {
                            boolean here = testFicheItem(redacteur, (FicheItem) value);
                            if (here) {
                                return true;
                            }
                            break;
                        }
                        case FieldKey.INFO_CATEGORY: {
                            for (FicheItem ficheItem : (FicheItems) value) {
                                boolean here = testFicheItem(redacteur, ficheItem);
                                if (here) {
                                    return true;
                                }
                            }
                            break;
                        }
                    }
                }
            }
        }
        return false;
    }


    public static boolean testFicheItem(Redacteur redacteur, FicheItem ficheItem) {
        if (!(ficheItem instanceof PersonItem)) {
            return false;
        }
        String redacteurGlobalId = ((PersonItem) ficheItem).getRedacteurGlobalId();
        if (redacteurGlobalId != null) {
            return redacteurGlobalId.equals(redacteur.getGlobalId());
        } else {
            return false;
        }
    }

    public static boolean testGlobalId(String redacteurGlobalId) {
        try {
            getSubsetKey(redacteurGlobalId);
            getId(redacteurGlobalId);
            return true;
        } catch (ParseException pe) {
            return false;
        }
    }

    public static SubsetKey getSubsetKey(String redacteurGlobalId) throws ParseException {
        int idx = redacteurGlobalId.indexOf('/');
        if (idx == -1) {
            throw new ParseException("Missing /", 0);
        }
        return SubsetKey.parse(SubsetKey.CATEGORY_SPHERE, redacteurGlobalId.substring(0, idx));
    }

    public static int getId(String redacteurGlobalId) throws ParseException {
        int idx = redacteurGlobalId.indexOf('/');
        if (idx == -1) {
            throw new ParseException("Missing /", 0);
        }
        try {
            int id = Integer.parseInt(redacteurGlobalId.substring(idx + 1));
            if (idx < 1) {
                throw new ParseException("Not positive integer", idx);
            }
            return id;
        } catch (NumberFormatException nfe) {
            throw new ParseException("Not integer", idx);
        }
    }

    public static boolean ownsToSphere(FicheMeta ficheMeta, Sphere sphere) {
        return ownsToSphere(ficheMeta, sphere.getSubsetName());
    }

    public static boolean ownsToSphere(FicheMeta ficheMeta, String sphereName) {
        List<String> globalIdList = ficheMeta.getRedacteurGlobalIdList();
        if (globalIdList.isEmpty()) {
            return false;
        }
        String prefix = sphereName + "/";
        for (String redacteurGlobalId : globalIdList) {
            if (redacteurGlobalId.startsWith(prefix)) {
                return true;
            }
        }
        return false;
    }

    public static Redacteur parse(Fichotheque fichotheque, String key) throws RedacteurLoginException {
        return parse(fichotheque, key, null);
    }

    public static Redacteur parse(Fichotheque fichotheque, String key, @Nullable SubsetKey defaultSphereKey) throws RedacteurLoginException {
        try {
            LoginKey loginKey = LoginKey.parse(key);
            Sphere sphere = (Sphere) fichotheque.getSubset(loginKey.getSphereKey());
            if (sphere == null) {
                throw new RedacteurLoginException(UNKNOWN_SPHERE, "unknown sphere: " + loginKey.getSphereKey());
            }
            Redacteur redacteur = sphere.getRedacteurByLogin(loginKey.getLogin());
            if (redacteur == null) {
                throw new RedacteurLoginException(UNKNOWN_LOGIN, "unknown redacteur: " + loginKey.getLogin());
            }
            return redacteur;
        } catch (ParseException pe) {

        }
        int idx = key.indexOf('/');
        if (idx != -1) {
            Redacteur redacteur = getRedacteur(fichotheque, key);
            if (redacteur != null) {
                return redacteur;
            } else {
                throw new RedacteurLoginException(MALFORMED_KEY, "Wrong redacteurGlobalId");
            }
        }
        if (defaultSphereKey == null) {
            throw new RedacteurLoginException(MALFORMED_KEY, "Missing separator (_ or [])");
        } else {
            Sphere defaultSphere = (Sphere) fichotheque.getSubset(defaultSphereKey);
            if (defaultSphere == null) {
                throw new RedacteurLoginException(UNKNOWN_SPHERE, "unknown default sphere: " + defaultSphereKey);
            } else {
                Redacteur redacteur = defaultSphere.getRedacteurByLogin(key);
                if (redacteur == null) {
                    throw new RedacteurLoginException(UNKNOWN_LOGIN, "unknown redacteur: " + key);
                }
                return redacteur;
            }
        }
    }


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


    public static class RedacteurLoginException extends Exception {

        private final short type;

        private RedacteurLoginException(short type, String message) {
            super(message);
            this.type = type;
        }

        public short getType() {
            return type;
        }

    }


    private static class RedacteurList extends AbstractList<Redacteur> implements RandomAccess {

        private final Redacteur[] array;

        private RedacteurList(Redacteur[] array) {
            this.array = array;
        }

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

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

    }

}
