/* ScrutariDataExport - Java implementation of ScrutariDataExport API
 * http://www.scrutari.net/dokuwiki/scrutaridata:exportapi
 *
 * Copyright (c) 2010-2020 Vincent Calame - Exemole
 * Licensed under MIT
 * (http://en.wikipedia.org/wiki/MIT_License)
 */


package net.scrutari.dataexport.xml;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import net.scrutari.dataexport.api.BaseMetadataExport;
import net.scrutari.dataexport.api.CorpusMetadataExport;
import net.scrutari.dataexport.api.ExportStateException;
import net.scrutari.dataexport.api.FicheExport;
import net.scrutari.dataexport.api.MotcleExport;
import net.scrutari.dataexport.api.RelationExport;
import net.scrutari.dataexport.api.ScrutariDataExport;
import net.scrutari.dataexport.api.ThesaurusMetadataExport;


/**
 *
 * @author Vincent Calame
 */
public class XmlScrutariDataExport implements ScrutariDataExport {

    private final static SimpleDateFormat ISO_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
    private final static String[] RELATIONS_NAMES = {"default-relation-type", "default-member-type", "default-thesaurus", "default-corpus", "default-role"};
    private final XmlWriter xmlWriter;
    private final XmlFicheExport ficheExport = new XmlFicheExport();
    private final XmlMotcleExport motcleExport = new XmlMotcleExport();
    private final XmlRelationExport relationExport = new XmlRelationExport();
    private final Map<String, Map<String, Integer>> indexationMap = new LinkedHashMap<String, Map<String, Integer>>();
    private final Date date;
    private XmlBuilder xmlBuilder;
    private int currentState = START;

    public XmlScrutariDataExport(XmlWriter xmlWriter) {
        this.xmlWriter = xmlWriter;
        this.date = null;
    }

    public XmlScrutariDataExport(XmlWriter xmlWriter, Date date) {
        this.xmlWriter = xmlWriter;
        this.date = date;
    }

    @Override
    public BaseMetadataExport startExport() {
        if (currentState != START) {
            throw new ExportStateException(currentState, "startExport");
        }
        if (date != null) {
            xmlWriter.openTagWithAttribute("base", "date", ISO_FORMAT.format(date));
        } else {
            xmlWriter.openTag("base");
        }
        currentState = BASEMETADATA;
        XmlBaseMetadataExport baseMetadataExport = new XmlBaseMetadataExport();
        this.xmlBuilder = baseMetadataExport;
        return baseMetadataExport;
    }

    @Override
    public CorpusMetadataExport newCorpus(String corpusName) {
        commonTest("newCorpus");
        flushXmlBuilder();
        flushFirstLevel();
        xmlWriter.openTagWithAttribute("corpus", "corpus-name", corpusName);
        currentState = CORPUSMETADATA;
        XmlCorpusMetadataExport corpusMetadataExport = new XmlCorpusMetadataExport();
        this.xmlBuilder = corpusMetadataExport;
        return corpusMetadataExport;
    }

    @Override
    public FicheExport newFiche(String ficheId) {
        switch (currentState) {
            case CORPUSMETADATA:
            case FICHE:
                break;
            default:
                throw new ExportStateException(currentState, "newFiche");
        }
        flushXmlBuilder();
        currentState = FICHE;
        ficheExport.reinit(ficheId);
        this.xmlBuilder = ficheExport;
        return ficheExport;
    }

    @Override
    public ThesaurusMetadataExport newThesaurus(String thesaurusName) {
        commonTest("newThesaurus");
        flushXmlBuilder();
        flushFirstLevel();
        xmlWriter.openTagWithAttribute("thesaurus", "thesaurus-name", thesaurusName);
        currentState = THESAURUSMETADATA;
        XmlThesaurusMetadataExport thesaurusMetadataExport = new XmlThesaurusMetadataExport();
        this.xmlBuilder = thesaurusMetadataExport;
        return thesaurusMetadataExport;
    }

    @Override
    public MotcleExport newMotcle(String motcleId) {
        switch (currentState) {
            case THESAURUSMETADATA:
            case MOTCLE:
                break;
            default:
                throw new ExportStateException(currentState, "newFiche");
        }
        flushXmlBuilder();
        currentState = MOTCLE;
        motcleExport.reinit(motcleId);
        this.xmlBuilder = motcleExport;
        return motcleExport;
    }

    @Override
    public void addIndexation(String corpusName, String ficheId, String thesaurusName, String motcleId, int weight) {
        commonTest("addIndexation");
        if (weight < 1) {
            weight = 1;
        }
        Map<String, Integer> map = getIndexationGroupMap(corpusName, thesaurusName);
        String key = ficheId + '\t' + motcleId;
        map.put(key, weight);
    }

    @Override
    public void newRelations(String defaultRelationType, String defaultMemberType, String defaultThesaurus, String defaultCorpus, String defaultRole) {
        String[] values = new String[5];
        values[0] = defaultRelationType;
        values[1] = defaultMemberType;
        values[2] = defaultThesaurus;
        values[3] = defaultCorpus;
        values[4] = defaultRole;
        commonTest("newRelations");
        flushXmlBuilder();
        flushFirstLevel();
        xmlWriter.openTagWithAttributes("relations", RELATIONS_NAMES, values);
        currentState = RELATION;
    }

    @Override
    public RelationExport newRelation(String type) {
        switch (currentState) {
            case RELATION:
                break;
            default:
                throw new ExportStateException(currentState, "newRelation");
        }
        flushXmlBuilder();
        relationExport.reinit(type);
        this.xmlBuilder = relationExport;
        return relationExport;
    }

    @Override
    public void endExport() {
        commonTest("endExport");
        flushXmlBuilder();
        flushFirstLevel();
        writeIndexation();
        xmlWriter.closeTag("base");
        currentState = END;
    }

    @Override
    public int getState() {
        return currentState;
    }

    private void writeIndexation() {
        String[] indexationGroupNameArray = {"corpus-path", "thesaurus-path"};
        String[] indexationNameArray = {"fiche-id", "motcle-id", "poids"};
        for (Map.Entry<String, Map<String, Integer>> mapEntry : indexationMap.entrySet()) {
            String key = mapEntry.getKey();
            int idx = key.indexOf('\t');
            String[] indexationGroupValueArray = new String[2];
            indexationGroupValueArray[0] = key.substring(0, idx);
            indexationGroupValueArray[1] = key.substring(idx + 1);
            xmlWriter.openTagWithAttributes("indexation-group", indexationGroupNameArray, indexationGroupValueArray);
            Map<String, Integer> map = mapEntry.getValue();
            for (Map.Entry<String, Integer> entry2 : map.entrySet()) {
                String key2 = entry2.getKey();
                int idx2 = key2.indexOf('\t');
                String[] indexationValueArray = new String[3];
                indexationValueArray[0] = key2.substring(0, idx2);
                indexationValueArray[1] = key2.substring(idx2 + 1);
                int weight = entry2.getValue();
                String weightString = null;
                if (weight > 1) {
                    weightString = String.valueOf(weight);
                }
                indexationValueArray[2] = weightString;
                xmlWriter.addEmptyElement("indexation", indexationNameArray, indexationValueArray);
            }
            xmlWriter.closeTag("indexation-group");
        }
    }

    private Map<String, Integer> getIndexationGroupMap(String corpusName, String thesaurusName) {
        String key = corpusName + '\t' + thesaurusName;
        Map<String, Integer> map = indexationMap.get(key);
        if (map == null) {
            map = new LinkedHashMap<String, Integer>();
            indexationMap.put(key, map);
        }
        return map;
    }

    private void commonTest(String methodName) {
        switch (currentState) {
            case START:
            case END:
                throw new ExportStateException(currentState, methodName);
        }
    }

    private void flushXmlBuilder() {
        if (xmlBuilder != null) {
            xmlBuilder.writeXML(xmlWriter);
            xmlBuilder = null;
        }
    }

    private void flushFirstLevel() {
        switch (currentState) {
            case CORPUSMETADATA:
            case FICHE:
                xmlWriter.closeTag("corpus");
                break;
            case THESAURUSMETADATA:
            case MOTCLE:
                xmlWriter.closeTag("thesaurus");
                break;
            case RELATION:
                xmlWriter.closeTag("relations");
                break;
        }
    }

}
