/*
 * Copyright
 *   2008 axYus - www.axyus.com
 *   2008 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 General Public License as published by
 * the Free Software Foundation; either version 2 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 General Public License for more details.
 *
 * You should have received a copy of the GNU 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.web.navigate;

import fr.gouv.finances.dgfip.utils.IoUtils;
import fr.gouv.finances.dgfip.utils.NavigationContext;
import fr.gouv.finances.dgfip.utils.Pair;
import fr.gouv.finances.dgfip.utils.xml.transform.CustomURIResolver;
import fr.gouv.finances.dgfip.xemelios.auth.XemeliosUser;
import fr.gouv.finances.dgfip.xemelios.common.config.DocumentModel;
import fr.gouv.finances.dgfip.xemelios.common.config.ElementModel;
import fr.gouv.finances.dgfip.xemelios.common.config.EtatModel;
import fr.gouv.finances.dgfip.xemelios.data.DataLayerManager;
import fr.gouv.finances.dgfip.xemelios.services.XemeliosHelper;
import fr.gouv.finances.dgfip.xemelios.web.AttributeHelper;
import fr.gouv.finances.dgfip.xemelios.web.utils.StdDispatchAction;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Vector;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import net.sf.saxon.TransformerFactoryImpl;
import org.apache.log4j.Logger;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.w3c.dom.Document;

public class NavigateAction extends StdDispatchAction {
    private static final Logger logger = Logger.getLogger(NavigateAction.class);

    @Override
    public ActionForward unspecified(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
        logger.debug(request.getQueryString());
        HttpSession session = request.getSession();
        XemeliosUser user = AttributeHelper.getUser(session);
        XPathFactory xpf = XPathFactory.newInstance();

        // http://localhost:8080/xemeliosWeb/displayDocument.do?docId=PES_Aller&etatId=PES_RecetteAller&elementId=BordereauRecette&collectivite=26620926100012&budget=00&path=[@added:primary-key=%272008-02-100015%27]&xsl:param=(mandatId,100018)
        String docId = request.getParameter("docId");
        String etatId = request.getParameter("etatId");
        String elementId = request.getParameter("elementId");
        String collectivite = request.getParameter("collectivite");
        String budget = request.getParameter("budget");
        String path = request.getParameter("path");
        String sp1 = request.getParameter("sp1");
        String sp2 = request.getParameter("sp2");
        String[] xslParamsArray = request.getParameterValues("xsl:param");
        
        if(docId==null || docId.length()==0) throw new IllegalArgumentException("parameter docId is not specified");
        DocumentModel documentModel = XemeliosHelper.getDocumentsModel(docId);
        if(documentModel==null) throw new IllegalArgumentException(docId+" is not a valid document ID");
        if(etatId==null || etatId.length()==0) throw new IllegalArgumentException("parameter etatId is not specified");
        EtatModel etatModel = documentModel.getEtatById(etatId);
        if(etatModel==null) throw new IllegalArgumentException(etatId+" is not a valid etat ID within "+documentModel.getTitre());
        if(elementId==null || elementId.length()==0) throw new IllegalArgumentException("parameter elementId is not specified");
        ElementModel elementModel = etatModel.getElementById(elementId);
        if(elementModel==null) throw new IllegalArgumentException(elementId+" is not a valid element ID within "+documentModel.getTitre()+" / "+etatModel.getTitre());
        if(collectivite==null || collectivite.length()==0) throw new IllegalArgumentException("parameter collectivite is not specified");
        // TODO: mettre al hierarchie des collectivites
        Vector<Pair> collectivites = DataLayerManager.getImplementation().getCollectivites(documentModel, null, user);
        Pair coll = null;
        for(Pair p:collectivites) {
            if(p.key.equals(collectivite)) {
                coll=p;
                break;
            }
        }
        if(coll==null) throw new IllegalArgumentException(collectivite+" is not a valid collectivite ID for "+documentModel.getId());
        if(budget==null || budget.length()==0) throw new IllegalArgumentException("parameter budget is no specified");
        Vector<Pair> budgets = DataLayerManager.getImplementation().getBudgets(documentModel, coll, user);
        Pair budg = null;
        for(Pair p:budgets) {
            if(p.key.equals(budget)) {
                budg = p;
                break;
            }
        }
        if(budg==null) throw new IllegalArgumentException(budget+" is not a valid budget ID within collectivite "+collectivite);
        
        String documentId = null;
        String errorMessage = null;
        if(path==null) {
            ArrayList<String> documents = DataLayerManager.getImplementation().getDocumentListFromSpecialKeys(elementModel,coll.key,budg.key,sp1,sp2,null,user);
            if(documents.size()==0) {
                errorMessage = "Le document est introuvable";
            } else if(documents.size()>1 && !etatModel.isMultiPage()) {
                errorMessage = "Plusieurs documents répondant à ces critères ont été trouvés.";
            } else {
                docId = documents.get(0);
            }
        } else {
// config XPath
            String _path = new String(path);
            int pos = _path.indexOf('%');
            while(pos>=0) {
                int closing = _path.indexOf('%', pos+1);
                if(closing<0) throw new RuntimeException("a '%' is not closed! : "+_path);
                String paramName = _path.substring(pos+1, closing);
                if(request.getParameter(paramName)!=null && request.getParameter(paramName).length()>0) {
                    String begin = _path.substring(0,pos);
                    String end = _path.substring(closing+1);
                    String replacedValue = request.getParameter(paramName);
                    _path = begin.concat(replacedValue).concat(end);
                    pos = _path.indexOf('%', pos);
                } else {
                    pos = _path.indexOf('%', closing);
                }
            }
            path = _path;

            if(path!=null && path.startsWith("[")) {
                path = elementModel.getPath().getFormattedPath()+path;
            }
            ArrayList<String> documents = DataLayerManager.getImplementation().getDocumentListFromXPath(elementModel,coll.key,budg.key,sp1,sp2,null,path, user);
            if(documents.size()==0) {
                errorMessage = "Le document est introuvable";
            } else if(documents.size()>1 && !etatModel.isMultiPage()) {
                errorMessage = "Plusieurs documents répondant à ces critères ont été trouvés.";
            } else {
                documentId = documents.get(0);
            }
        }
        if(documentId!=null) {
            Document doc = DataLayerManager.getImplementation().getDocumentAsDom(etatModel,coll,budg,documentId, user);
            File f = null;
            f = new File(new File(new File(System.getProperty("user.home")),"xemelios/documents-def-ovrrd"),elementModel.getXslt());
            if(!f.exists())
                f = new File(etatModel.getParent().getBaseDirectory(),elementModel.getXslt());
            InputStream is = IoUtils.getInputStream(f.getPath());
            Hashtable<String,Object> parameters = new Hashtable<String,Object>();
            if(xslParamsArray!=null) {
                for(String xslParam:xslParamsArray) {
                    int virg = xslParam.indexOf(',');
                    String pName = xslParam.substring(1,virg);
                    String pValue = xslParam.substring(virg+1,xslParam.length()-1);
                    parameters.put(pName,pValue);
                }
            }
            parameters.put("browser-destination", "internal");  // il faut une destination "internal" pour avoir les liens.
            XPath xp = xpf.newXPath();
            xp.setNamespaceContext(documentModel.getNamespaces());
            String idRepository = null;
            if(documentModel.getReferenceNomenclaturePath()!=null && documentModel.getReferenceNomenclaturePath().getCodePath()!=null) {
                idRepository = (String)xp.evaluate(documentModel.getReferenceNomenclaturePath().getCodePath().getPath(),doc.getDocumentElement(),XPathConstants.STRING);
            }
            if(idRepository!=null && idRepository.length()==0) idRepository=null;
            Document repository = DataLayerManager.getImplementation().getRepositoryAsDom(documentModel, coll,idRepository,user);
            if(repository!=null)
                parameters.put("repository", repository);
            // on passe la config
            parameters.put("config",XemeliosHelper.getDocumentsModel().getSmallDOM());
            parameters.put("collectivite",coll);
            parameters.put("budget",budg);
            TransformerFactory tFactory = TransformerFactoryImpl.newInstance();
            tFactory.setURIResolver(new CustomURIResolver(tFactory.getURIResolver(), f.getParentFile().toURI().toURL().toExternalForm()));
            Transformer transformer = tFactory.newTransformer(new StreamSource(f));
            for(String pName:parameters.keySet()) transformer.setParameter(pName, parameters.get(pName));
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            transformer.transform(new DOMSource(doc), new StreamResult(baos));

            File xslFile = new File(getServlet().getServletContext().getRealPath("/js/display/href-converter.xsl"));

            tFactory = TransformerFactoryImpl.newInstance();
            Transformer secondTransformer = tFactory.newTransformer(new StreamSource(xslFile));
//            Hashtable<String,Object> params = new Hashtable<String,Object>();
            secondTransformer.setParameter("server",request.getServerName());
            secondTransformer.setParameter("context",request.getContextPath());
            secondTransformer.setParameter("port",request.getServerPort());
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            AttributeHelper.setPreviousView(session, request.getServletPath()+"?"+request.getQueryString());
            secondTransformer.transform(new StreamSource(bais), new StreamResult(response.getOutputStream()));
        } else {
//            request.setAttribute("error.message",errorMessage);
//            return mapping.findForward("simple.error");
            String url = AttributeHelper.getPreviousView(session).concat("&xsl:param=(error.message,").concat(errorMessage).concat(")");
            return new ActionForward(url);
        }
        return null;
    }

}
