/*
 * Copyright
 *   2007 axYus - www.axyus.com
 *   2009 C.Marchand - christophe.marchand@axyus.com
 *
 * This file is part of XEMELIOS.
 *
 * XEMELIOS is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * XEMELIOS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with XEMELIOS; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package fr.gouv.finances.dgfip.xemelios.batch.imports;

import fr.gouv.finances.dgfip.xemelios.utils.XmlUtils;
import fr.gouv.finances.dgfip.utils.Pair;
import fr.gouv.finances.dgfip.utils.xml.NamespaceContextImpl;
import fr.gouv.finances.dgfip.xemelios.common.config.DocumentModel;
import fr.gouv.finances.dgfip.xemelios.common.config.EtatModel;
import java.io.File;
import java.io.IOException;
import java.util.Stack;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.apache.log4j.Logger;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.ext.DefaultHandler2;

/**
 *
 * @author chm
 */
public class BudgCollParser {

    public static final transient String IGNORABLE_ERROR = "This is not an error, only an expected termination";
    private File xmlFile;
    private DocumentModel documentModel;
    private String collectivite,  collectiviteLib,  budget,  budgetLib,  errorMessage;
    private boolean inError = false;

    public BudgCollParser(File xmlFile, DocumentModel documentModel) {
        super();
        this.xmlFile = xmlFile;
        this.documentModel = documentModel;
    }

    /**
     * Start investigation process
     */
    public void investigate() {
        SAXParserFactory saxFactory = SAXParserFactory.newInstance();
        saxFactory.setNamespaceAware(true);
        ParserHandler ph = new ParserHandler(documentModel);
        try {
            SAXParser parser = saxFactory.newSAXParser();
            parser.parse(xmlFile, ph);
        } catch (ParserConfigurationException pcEx) {
            pcEx.printStackTrace();
        } catch (SAXException saxEx) {
            if (!saxEx.getMessage().equals(IGNORABLE_ERROR)) {
                // file is not valid
                setError(saxEx.getMessage());
            }
        } catch(IOException ioEx) {
            // should never happen
            ioEx.printStackTrace();
        } finally {
            if(ph!=null && !inError) {
                if(ph.getCollectivite()!=null) {
                    if(ph.collectivite.key!=null && ph.collectivite.key.length()>0 && (collectivite==null || collectivite.length()==0)) {
                        collectivite = ph.collectivite.key;
                    }
                    if(ph.collectivite.libelle!=null && ph.collectivite.libelle.length()>0 && (collectiviteLib==null || collectiviteLib.length()==0)) {
                        collectiviteLib = ph.collectivite.libelle;
                    }
                }
                if(ph.budget!=null) {
                    if(ph.budget.key!=null && ph.budget.key.length()>0 && (budget==null || budget.length()==0)) {
                        budget = ph.budget.key;
                    }
                    if(ph.budget.libelle!=null && ph.budget.libelle.length()>0 && (budgetLib==null || budgetLib.length()==0)) {
                        budgetLib = ph.budget.libelle;
                    }
                }
            }
        }
    }

    public String getBudget() {
        return budget;
    }

    public void setBudget(String budget) {
        this.budget = budget;
    }

    public String getBudgetLib() {
        return budgetLib;
    }

    public void setBudgetLib(String budgetLib) {
        this.budgetLib = budgetLib;
    }

    public String getCollectivite() {
        return collectivite;
    }

    public void setCollectivite(String collectivite) {
        this.collectivite = collectivite;
    }

    public String getCollectiviteLib() {
        return collectiviteLib;
    }

    public void setCollectiviteLib(String collectiviteLib) {
        this.collectiviteLib = collectiviteLib;
    }

    public void setError(String errorMessage) {
        inError = true;
        this.errorMessage = errorMessage;
    }

    public String getErrorMessage() {
        return errorMessage;
    }

    public boolean isInError() {
        return inError;
    }

    private static class ParserHandler extends DefaultHandler2 {
        private static final Logger logger = Logger.getLogger(ParserHandler.class);
        private NamespaceContextImpl nsCtx;
        private Stack<String> tagStack = new Stack<String>();
        private Stack<StringBuilder> charactersStack = new Stack<StringBuilder>();

        private DocumentModel dm;
        private Pair budget,  collectivite;

        public ParserHandler(DocumentModel dm) {
            super();
            this.dm = dm;
            nsCtx = new NamespaceContextImpl();
        }

        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
            String tagName = XmlUtils.getShortTagName(uri, localName, qName);
            switch (tagStack.size()) {
                case 0: {
                    // si ce n'est pas le tag du doc que l'on importe, bye
                    if (!tagName.equals(dm.getBalise())) {
                        throw new SAXException("Le fichier importé n'est pas un " + dm.getTitre());
                    }
                    break;
                }
                case 1: {
                    if (dm.getEntetes().contains(tagName)) {
                    } else if (dm.getReferentiel().equals(tagName)) {
                    } else {
                        EtatModel em = dm.getEtatByTagName(tagName);
                        if(em!=null) throw new SAXException(IGNORABLE_ERROR);
                    }
                    break;
                }
                case 2: {
                    break;
                }
            }
            tagStack.push(tagName);
            charactersStack.push(new StringBuilder());

            String path = XmlUtils.getPath(tagStack);
            for (int i = 0; i < attributes.getLength(); i++) {
                String attrName = attributes.getLocalName(i);
                String value = attributes.getValue(i);
                String fullPath = path + "/@" + attrName;
                checkBudgetCollectivite(fullPath, value);
            }
        }
        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException {
            String tagName = XmlUtils.getShortTagName(uri, localName, qName);
            if(tagStack.size()==1) {
                if (!tagName.equals(dm.getBalise())) {
                    throw new SAXException("Fermeture d'un element root inattendu");
                }
            }
            String path = XmlUtils.getPath(tagStack);
            String innerData = charactersStack.pop().toString();
            checkBudgetCollectivite(path, innerData);
            tagStack.pop();
        }

        @Override
        public void endPrefixMapping(String prefix) throws SAXException {
            super.endPrefixMapping(prefix);
            nsCtx.removeMapping(prefix);
        }

        @Override
        public void startPrefixMapping(String prefix, String uri) throws SAXException {
            super.startPrefixMapping(prefix, uri);
            nsCtx.addMapping(prefix, uri);
        }

        private void checkBudgetCollectivite(String path, String value) throws SAXException {
//            logger.debug("checking coll/budg at"+path);
            if (dm.getBudgetPath() != null) {
                if (dm.getBudgetPath().getCodePath() != null && path.equals(dm.getBudgetPath().getCodePath().getPath())) {
                    if (budget == null) {
                        budget = new Pair();
                    }
                    budget.key = value;
                }
                if (dm.getBudgetPath().getLibellePath() != null && path.equals(dm.getBudgetPath().getLibellePath().getPath())) {
                    if (budget == null) {
                        budget = new Pair();
                    }
                    budget.libelle = value;
                }
            }
            if (dm.getCollectivitePath() != null) {
                if (dm.getCollectivitePath().getCodePath() != null && path.equals(dm.getCollectivitePath().getCodePath().getPath())) {
                    if (collectivite == null) {
                        collectivite = new Pair();
                    }
                    collectivite.key = value;
                }
                if (dm.getCollectivitePath().getLibellePath() != null && path.equals(dm.getCollectivitePath().getLibellePath().getPath())) {
                    if (collectivite == null) {
                        collectivite = new Pair();
                    }
                    collectivite.libelle = value;
                }
            }
            if(collectivite!=null &&collectivite.key!=null && collectivite.key.length()>0 && collectivite.libelle!=null && collectivite.libelle.length()>0
                    && budget!=null && budget.key!=null && budget.key.length()>0 && budget.libelle!=null && budget.libelle.length()>0) {
                throw new SAXException(IGNORABLE_ERROR);
            }
        }

        public Pair getBudget() {
            return budget;
        }

        public Pair getCollectivite() {
            return collectivite;
        }
    }
}
