/* FichothequeLib_API - Copyright (c) 2008-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.format;

import java.text.ParseException;
import net.fichotheque.FichothequeConstants;
import net.fichotheque.SubsetKey;
import net.fichotheque.corpus.metadata.FieldKey;
import net.fichotheque.corpus.metadata.SubfieldKey;
import net.fichotheque.include.IncludeKey;
import net.fichotheque.thesaurus.metadata.ThesaurusFieldKey;
import net.mapeadores.util.attr.AttributeKey;


/**
 * Clé de définition d'une source de données de la fichothèque.
 *
 *
 * @author Vincent Calame
 */
public final class FormatSourceKey {

    public final static short SPECIAL_TYPE = 0;
    public final static short FIELDKEY_TYPE = 1;
    public final static short SUBFIELDKEY_TYPE = 2;
    public final static short INCLUDEKEY_TYPE = 3;
    public final static short THESAURUSFIELDKEY_TYPE = 4;
    public final static short NAMEPREFIXFIELDKEY_TYPE = 5;
    public final static short ATTRIBUTEKEY_TYPE = 6;
    public final static short ID_TYPE = 7;
    public final static short PARENTAGESUBSETKEY_TYPE = 8;
    public final static short SPECIALINCLUDENAME_TYPE = 9;
    public final static short LANG_TYPE = 10;
    public final static FormatSourceKey EXTRACTION = new FormatSourceKey(SPECIAL_TYPE, "extraction");
    public final static FormatSourceKey CONSTANT = new FormatSourceKey(SPECIAL_TYPE, "constant");
    public final static FormatSourceKey ID = new FormatSourceKey(ID_TYPE, "id");
    public final static FormatSourceKey LANG = new FormatSourceKey(LANG_TYPE, "lang");
    public final static FormatSourceKey DATE_CREATION = new FormatSourceKey(SPECIALINCLUDENAME_TYPE, FichothequeConstants.DATECREATION_NAME);
    public final static FormatSourceKey DATE_MODIFICATION = new FormatSourceKey(SPECIALINCLUDENAME_TYPE, FichothequeConstants.DATEMODIFICATION_NAME);
    private final short sourceType;
    private final Object keyObject;
    private final SubsetPathKey subsetPathKey;

    private FormatSourceKey(short sourceType, Object keyObject) {
        this.sourceType = sourceType;
        this.keyObject = keyObject;
        this.subsetPathKey = null;
    }

    private FormatSourceKey(short sourceType, Object keyObject, SubsetPathKey subsetPathKey) {
        this.sourceType = sourceType;
        this.keyObject = keyObject;
        this.subsetPathKey = subsetPathKey;
    }

    public static FormatSourceKey newFieldKeyInstance(FieldKey fieldKey) {
        return newFieldKeyInstance(fieldKey, null);
    }

    public static FormatSourceKey newFieldKeyInstance(FieldKey fieldKey, SubsetPathKey subsetPathKey) {
        if (fieldKey == null) {
            throw new IllegalArgumentException("fieldKey is null");
        }
        FormatSourceKey formatSourceKey = new FormatSourceKey(FIELDKEY_TYPE, fieldKey, subsetPathKey);
        return formatSourceKey;
    }

    public static FormatSourceKey newSubfieldKeyInstance(SubfieldKey subfieldKey) {
        return newSubfieldKeyInstance(subfieldKey, null);
    }

    public static FormatSourceKey newSubfieldKeyInstance(SubfieldKey subfieldKey, SubsetPathKey subsetPathKey) {
        if (subfieldKey == null) {
            throw new IllegalArgumentException("subfieldKey is null");
        }
        return new FormatSourceKey(SUBFIELDKEY_TYPE, subfieldKey, subsetPathKey);
    }

    public static FormatSourceKey newIncludeKeyInstance(IncludeKey includeKey) {
        return newIncludeKeyInstance(includeKey, null);
    }

    public static FormatSourceKey newIncludeKeyInstance(IncludeKey includeKey, SubsetPathKey subsetPathKey) {
        if (includeKey == null) {
            throw new IllegalArgumentException("includeKey is null");
        }
        return new FormatSourceKey(INCLUDEKEY_TYPE, includeKey, subsetPathKey);
    }

    public static FormatSourceKey newSpecialIncludeNameInstance(String specialIncludeName) {
        return newSpecialIncludeNameInstance(specialIncludeName, null);
    }

    public static FormatSourceKey newSpecialIncludeNameInstance(String specialIncludeName, SubsetPathKey subsetPathKey) {
        if (specialIncludeName == null) {
            throw new IllegalArgumentException("specialIncludeName is null");
        }
        specialIncludeName = FichothequeConstants.checkSpecialIncludeName(specialIncludeName);
        return new FormatSourceKey(SPECIALINCLUDENAME_TYPE, specialIncludeName, subsetPathKey);
    }

    public static FormatSourceKey newThesaurusFieldKeyInstance(ThesaurusFieldKey thesaurusFieldKey) {
        return newThesaurusFieldKeyInstance(thesaurusFieldKey, null);
    }

    public static FormatSourceKey newThesaurusFieldKeyInstance(ThesaurusFieldKey thesaurusFieldKey, SubsetPathKey subsetPathKey) {
        if (thesaurusFieldKey == null) {
            throw new IllegalArgumentException("thesaurusFieldKey is null");
        }
        return new FormatSourceKey(THESAURUSFIELDKEY_TYPE, thesaurusFieldKey, subsetPathKey);
    }

    public static FormatSourceKey newNamePrefixFieldKeyInstance(FieldKey namePrefixFieldKey) {
        return FormatSourceKey.newNamePrefixFieldKeyInstance(namePrefixFieldKey, null);
    }

    public static FormatSourceKey newNamePrefixFieldKeyInstance(FieldKey namePrefixFieldKey, SubsetPathKey subsetPathKey) {
        if (namePrefixFieldKey == null) {
            throw new IllegalArgumentException("namePrefixFieldKey is null");
        }
        if (namePrefixFieldKey.isSpecial()) {
            throw new IllegalArgumentException("namePrefixFieldKey is a special field: " + namePrefixFieldKey.getKeyString());
        }
        FormatSourceKey formatSourceKey = new FormatSourceKey(NAMEPREFIXFIELDKEY_TYPE, namePrefixFieldKey, subsetPathKey);
        return formatSourceKey;
    }

    public static FormatSourceKey newAttributeKeyInstance(AttributeKey attributeKey) {
        return newAttributeKeyInstance(attributeKey, null);
    }

    public static FormatSourceKey newAttributeKeyInstance(AttributeKey attributeKey, SubsetPathKey subsetPathKey) {
        if (attributeKey == null) {
            throw new IllegalArgumentException("attributeKey is null");
        }
        return new FormatSourceKey(ATTRIBUTEKEY_TYPE, attributeKey, subsetPathKey);
    }

    public static FormatSourceKey newIdInstance(SubsetPathKey subsetPathKey) {
        return new FormatSourceKey(ID_TYPE, "id", subsetPathKey);
    }

    public static FormatSourceKey newLangInstance(SubsetPathKey subsetPathKey) {
        return new FormatSourceKey(LANG_TYPE, "lang", subsetPathKey);
    }

    public static FormatSourceKey newParentageSubsetKeyInstance(SubsetKey subsetKey) {
        return newParentageSubsetKeyInstance(subsetKey, null);
    }

    public static FormatSourceKey newParentageSubsetKeyInstance(SubsetKey subsetKey, SubsetPathKey subsetPathKey) {
        if (subsetKey == null) {
            throw new IllegalArgumentException("subsetKey is null");
        }
        if ((!subsetKey.isCorpusSubset()) && (!subsetKey.isThesaurusSubset())) {
            throw new IllegalArgumentException("wrong subsetKey type: " + subsetKey);
        }
        FormatSourceKey formatSourceKey = new FormatSourceKey(PARENTAGESUBSETKEY_TYPE, subsetKey, subsetPathKey);
        return formatSourceKey;
    }

    public short getSourceType() {
        return sourceType;
    }

    public Object getKeyObject() {
        return keyObject;
    }

    public SubsetPathKey getSubsetPathKey() {
        return subsetPathKey;
    }

    public String getKeyString() {
        return toString();
    }

    @Override
    public String toString() {
        StringBuilder buf = new StringBuilder();
        if (subsetPathKey != null) {
            buf.append(subsetPathKey.toString());
            buf.append('/');
        }
        if (sourceType == PARENTAGESUBSETKEY_TYPE) {
            buf.append(SubsetKey.toParentageString((SubsetKey) keyObject));
        } else {
            buf.append(keyObject.toString());
            if (sourceType == NAMEPREFIXFIELDKEY_TYPE) {
                buf.append('*');
            }
        }
        return buf.toString();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj == this) {
            return true;
        }
        if (obj.getClass() != this.getClass()) {
            return false;
        }
        FormatSourceKey other = (FormatSourceKey) obj;
        if (other.sourceType != this.sourceType) {
            return false;
        }
        if (subsetPathKey != null) {
            if (other.subsetPathKey != null) {
                if (!subsetPathKey.equals(other.subsetPathKey)) {
                    return false;
                }
            } else {
                return false;
            }
        } else if (other.subsetPathKey != null) {
            return false;
        }
        return other.keyObject.equals(this.keyObject);
    }

    @Override
    public int hashCode() {
        int hashCode = 0;
        if (subsetPathKey != null) {
            hashCode = subsetPathKey.hashCode();
        }
        return hashCode + keyObject.hashCode();
    }

    public static FormatSourceKey parse(String formatSource) throws ParseException {
        switch (formatSource) {
            case "extraction":
                return FormatSourceKey.EXTRACTION;
            case "constant":
            case "null":
                return FormatSourceKey.CONSTANT;
            case "id":
                return FormatSourceKey.ID;
            case "lang":
                return FormatSourceKey.LANG;
        }
        SubsetPathKey subsetPathKey = null;
        int idx = formatSource.lastIndexOf('/');
        if (idx != -1) {
            String subsetPathString = formatSource.substring(0, idx);
            subsetPathKey = SubsetPathKey.parse(subsetPathString);
            formatSource = formatSource.substring(idx + 1);
            switch (formatSource) {
                case "id":
                    return FormatSourceKey.newIdInstance(subsetPathKey);
                case "lang":
                    return FormatSourceKey.newLangInstance(subsetPathKey);
            }
        }
        if (formatSource.endsWith("*")) {
            FieldKey namePrefixFieldKey = FieldKey.parse(formatSource.substring(0, formatSource.length() - 1));
            try {
                return FormatSourceKey.newNamePrefixFieldKeyInstance(namePrefixFieldKey, subsetPathKey);
            } catch (IllegalArgumentException iae) {
                throw new ParseException("Special field key", 0);
            }
        }
        try {
            String specialFormatSource = FichothequeConstants.checkSpecialIncludeName(formatSource);
            return FormatSourceKey.newSpecialIncludeNameInstance(specialFormatSource, subsetPathKey);
        } catch (IllegalArgumentException iae) {

        }
        try {
            FieldKey fk = FieldKey.parse(formatSource);
            return FormatSourceKey.newFieldKeyInstance(fk, subsetPathKey);
        } catch (ParseException pe1) {
            try {
                SubfieldKey subfieldKey = SubfieldKey.parse(formatSource);
                return FormatSourceKey.newSubfieldKeyInstance(subfieldKey, subsetPathKey);
            } catch (ParseException pe2) {
                try {
                    IncludeKey includeKey = IncludeKey.parse(formatSource);
                    return FormatSourceKey.newIncludeKeyInstance(includeKey, subsetPathKey);
                } catch (ParseException pe3) {
                    try {
                        ThesaurusFieldKey thesaurusFieldKey = ThesaurusFieldKey.parse(formatSource);
                        return FormatSourceKey.newThesaurusFieldKeyInstance(thesaurusFieldKey, subsetPathKey);
                    } catch (ParseException pe4) {
                        try {
                            AttributeKey attributeKey = AttributeKey.parse(formatSource);
                            return FormatSourceKey.newAttributeKeyInstance(attributeKey, subsetPathKey);
                        } catch (ParseException pe5) {
                            try {
                                SubsetKey parentageSubsetKey = SubsetKey.parseParentageSubsetKey(formatSource);
                                return FormatSourceKey.newParentageSubsetKeyInstance(parentageSubsetKey, subsetPathKey);
                            } catch (ParseException pe6) {
                                throw new ParseException("Unable to parse " + formatSource, 0);
                            }
                        }
                    }
                }
            }
        }
    }

}
