/*
Copyright (C) 2000-2010  Ministere de la culture et de la communication (France), AJLSM
See LICENCE file
*/
package fr.gouv.culture.sdx.thesaurus;

import fr.gouv.culture.sdx.document.IndexableFieldProperty;
import fr.gouv.culture.sdx.document.XMLDocument;
import fr.gouv.culture.sdx.exception.SDXException;
import fr.gouv.culture.sdx.utils.Utilities;
import fr.gouv.culture.sdx.utils.constants.Node;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;

import java.util.Enumeration;

abstract public class AbstractConcept extends XMLDocument implements Concept {


    protected String valueKey = TERM;
    protected String xmlLangKey = XMLLANG;
    protected String value = null;
    protected String xmlLang = null;


    public AbstractConcept() {
        super.DOC_ROOT_ELEMENT_NAME = Node.Name.CONCEPT;
        super.DOC_NAMESPACE = Thesaurus.NAMESPACE_URI;
    }

    public AbstractConcept(String id) throws SDXException {
        super(id);
        super.DOC_ROOT_ELEMENT_NAME = Node.Name.CONCEPT;
        super.DOC_NAMESPACE = Thesaurus.NAMESPACE_URI;
    }


    public String getDocType() {
        return DOCTYPE_CONCEPT;
    }

    /*
    <?xml version="1.0" encoding="UTF-8"?>
    <thesaurus
        xmlns="http://sdx.culture.fr/thesaurus/1.0/"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://sdx.culture.fr/thesaurus/1.0/ th-20021113.xsd"
        id="exemple"
        xml:lang="fr">

        <!-- La hierarchie "Administration (1)" -->
        <concept id="c01">
            <term>Administration</term>
            <concept id="c02">
                <term>Administration generale</term>
                <concept id="c03">
                    <term>Collectivite locale</term>
                    <relations>
                        <relation type="UF">Collectivite territoriale</relation>
                    </relations>
                    <concept id="c04">
                        <term>Commune</term>
                        <relations>
                            <relation type="RT" ref="c05"/>
                        </relations>
                    </concept>
                </concept>
            </concept>
        </concept>
    </thesaurus>

    This will build 4 concepts/documents:

    1) the ancestor with fields:
    sdxdocid:c01
    term:Administration
    nt:c02

    2)the 1st child with fields:
    sdxdocid:cO2
    term:Administration generale
    bt:c01
    nt:c03

    3)the 2nd child with fields
    sdxdocid:cO3
    term:Collectivite locale
    bt:c02
    bts:c01
    nt:c04
    uf:Collectivite territoriale


    4)the 3rd child with fields:
    sdxdocid:cO4
    term:Commune
    bts:c02
    bts:c01
    bt:c03
    rt:c05
    */


    public void startElement(String uri, String loc, String raw, Attributes a)
            throws SAXException {
        if (DOC_NAMESPACE.equals(uri)) {
            /*
            if (loc.equals(ConfigurationNode.Name.THESAURUS)){
                String id = a.getValue(ConfigurationNode.Name.ID);
                if (Utilities.checkString(id))
                    this.thesaurusId = id;
                String xmlLang = a.getValue(ConfigurationNode.Name.XML_LANG);
                if (Utilities.checkString(xmlLang))
                    this.thesaurusXmlLang = xmlLang;
            }
             */
            if (DOC_ROOT_ELEMENT_NAME.equals(loc)) {
                //we let abstractxmldocument have an opportunity at each element
                super.startElement(uri, loc, raw, a);
                String xmlLang = a.getValue(Node.Name.XML_LANG);
                if (Utilities.checkString(xmlLang))
                    this.setXmlLang(xmlLang);
            }

            //we identify that we want to keep the value of this element as a field of a lucene concept
            else if (TERM.equals(loc))//adding the term field
                sendSdxFieldStartElement(loc);

            //we identify that we want to keep the value of this element as a field of the a lucene concept
            else if (Node.Name.RELATION.equals(loc)) {
                makeRelationTypeAttributeValueAsFieldNameForConcept(Node.Name.TYPE, a);
                String refValue = a.getValue(Node.Name.REF);
                if (Utilities.checkString(refValue))//we have a relation ref id to keep
                    addRelationNameAsFieldValueOfConcept(refValue);
            } else if (Node.Name.P.equals(loc))//adding the scope note
                sendSdxFieldStartElement(ABBREVIATION_SCOPE_NOTE);
            else
                super.startElement(uri, loc, raw, a);
        } else
            super.startElement(uri, loc, raw, a);
    }

    public void endElement(String uri, String loc, String raw)
            throws SAXException {
        if (DOC_NAMESPACE.equals(uri)) {
            //we collect the concepts at the end of an element
            if (DOC_ROOT_ELEMENT_NAME.equals(loc)) {
                //we let abstractxmldocument have an opportunity at each element
                super.endElement(uri, loc, raw);
                //we need to build relations here, when we have finished building all sub relations
                if (openSdxDocElems == 0) buildRelations();
            } else if (TERM.equals(loc) || Node.Name.RELATION.equals(loc) || Node.Name.P.equals(loc) || RELATION_USED_FOR.equals(loc)) {
                sendSdxFieldEndElement();
            } else
                super.endElement(uri, loc, raw);
        } else
            super.endElement(uri, loc, raw);

    }

    private void buildRelations() throws SAXException {
        Enumeration children = this.getSubDocuments();
        if (children != null) {
            while (children.hasMoreElements()) {
                Concept child = (Concept) children.nextElement();
                addRelationReferenceToConcept(child, this, RELATION_NARROWER_TERM);
                addRelationReferenceToConcept(this, child, RELATION_BROADER_TERM);
                addRelationReferenceToConcept(this, child, RELATION_BROADER_TERMS);
                buildBroaderTermsRelations(child);
            }
        }

    }

    private void buildBroaderTermsRelations(Concept child) throws SAXException {
        if (child != null) {
            Enumeration grandChildren = child.getSubDocuments();
            if (grandChildren != null) {
                while (grandChildren.hasMoreElements()) {
                    Concept grandChild = (Concept) grandChildren.nextElement();
                    //all children below should get this field
                    if (grandChild != null)
                        addRelationReferenceToConcept(this, grandChild, RELATION_BROADER_TERMS);
                    //recursive call
                    buildBroaderTermsRelations(grandChild);
                }
            }
        }
    }


    private void makeRelationTypeAttributeValueAsFieldNameForConcept(String attName, Attributes atts) throws SAXException {
        if (Utilities.checkString(attName)) {
            if (atts != null) {
                String value = atts.getValue(attName);
                if (Utilities.checkString(value))
                //the user-defined relation type like (type=UF) will become the field name "uf"
                    sendSdxFieldStartElement(value.toLowerCase());
            }
        }
    }

    /**Takes an the 'id' field value of the first concept
     * and adds it as a relation to the second second concept
     *
     * @param relationFrom Concept from which the relation is taken
     * @param relationTo   Concept to which the relation is added
     *@param  relationType The type of relation to be added as a field
     */
    private void addRelationReferenceToConcept(Concept relationFrom, Concept relationTo, String relationType) {
        String relationFromId = relationFrom.getId();
        relationTo.addRelation(relationType, relationFromId);
        /*
        sendSdxFieldStartElement(relationType);
        sendSdxFieldCharacters(relationFromId);
        sendSdxFieldEndElement();
        */
    }

    private void addRelationNameAsFieldValueOfConcept(String fieldValue) throws SAXException {
        sendSdxFieldCharacters(fieldValue);
    }


    private void sendSdxFieldStartElement(String attValue) throws SAXException {
        AttributesImpl atts = new AttributesImpl();
        atts.addAttribute("", Node.Name.NAME, Node.Name.NAME, Node.Type.CDATA, attValue);
        super.startElement(DOC_NAMESPACE, DOC_FIELD_ELEMENT_NAME, DOC_FIELD_ELEMENT_NAME, atts);
    }

    private void sendSdxFieldCharacters(String fieldValue) throws SAXException {
        if (Utilities.checkString(fieldValue)) {
            char[] c = fieldValue.toCharArray();
            super.characters(c, 0, c.length);
        }
    }

    private void sendSdxFieldEndElement() throws SAXException {
        super.endElement(DOC_NAMESPACE, DOC_FIELD_ELEMENT_NAME, DOC_FIELD_ELEMENT_NAME);
    }

    /*Methods for building a concept before indexation*/
    public void addRelation(String relationType, String relationValue) {
        super.properties.add(new IndexableFieldProperty(relationType, relationValue));
    }

    public void setValue(String value) {
        if (value != null) {
            if (super.properties != null)
                super.properties.add(new IndexableFieldProperty(valueKey, value));
            this.value = value;
        }
    }

    public void setXmlLang(String xmlLang) {
        if (xmlLang != null) {
            if (super.properties != null)
                super.properties.add(new IndexableFieldProperty(xmlLangKey, xmlLang));
            this.xmlLang = xmlLang;
        }
    }


}
