/*
 * Version 1.1
 * CeCILL Copyright (c) 2006-2007, AtolCD, ADULLACT-projet
 * Initiated by AtolCD S.A. & ADULLACT-projet S.A.
 * Developped by AtolCD
 * 
 * contact@atolcd.com
 * contact@adullact-projet.coop
 * 
 * Ce logiciel est un programme informatique servant à faire circuler des 
 * documents au travers d'un circuit de validation, où chaque acteur vise 
 * le dossier, jusqu'à l'étape finale de signature.
 * 
 * Ce logiciel est régi par la licence CeCILL soumise au droit français et
 * respectant les principes de diffusion des logiciels libres. Vous pouvez
 * utiliser, modifier et/ou redistribuer ce programme sous les conditions
 * de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA 
 * sur le site "http://www.cecill.info".
 * 
 * En contrepartie de l'accessibilité au code source et des droits de copie,
 * de modification et de redistribution accordés par cette licence, il n'est
 * offert aux utilisateurs qu'une garantie limitée.  Pour les mêmes raisons,
 * seule une responsabilité restreinte pèse sur l'auteur du programme,  le
 * titulaire des droits patrimoniaux et les concédants successifs.
 * 
 * A cet égard  l'attention de l'utilisateur est attirée sur les risques
 * associés au chargement,  à l'utilisation,  à la modification et/ou au
 * développement et à la reproduction du logiciel par l'utilisateur étant 
 * donné sa spécificité de logiciel libre, qui peut le rendre complexe à 
 * manipuler et qui le réserve donc à des développeurs et des professionnels
 * avertis possédant  des  connaissances  informatiques approfondies.  Les
 * utilisateurs sont donc invités à charger  et  tester  l'adéquation  du
 * logiciel à leurs besoins dans des conditions permettant d'assurer la
 * sécurité de leurs systèmes et ou de leurs données et, plus généralement, 
 * à l'utiliser et l'exploiter dans les mêmes conditions de sécurité. 
 * 
 * Le fait que vous puissiez accéder à cet en-tête signifie que vous avez 
 * pris connaissance de la licence CeCILL, et que vous en avez accepté les
 * termes.
 *  
 */

package com.atolcd.parapheur.repo;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.content.filestore.FileContentWriter;
import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.TempFileProvider;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.contrib.ssl.EasySSLProtocolSocketFactory;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
import org.apache.commons.ssl.KeyMaterial;
import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;

import com.atolcd.parapheur.model.ParapheurModel;

public class S2lowServiceImpl implements S2lowService, InitializingBean
{
   private static Logger logger = Logger.getLogger(S2lowService.class);
   
   private Properties configuration;
   private NodeService nodeService;
   private ContentService contentService;
   private ParapheurService parapheurService;
   private SearchService searchService;
   private NamespaceService namespaceService;
   
   private boolean enabled;
   
   /**
    * @param configuration The configuration to set.
    */
   public void setConfiguration(Properties configuration)
   {
      this.configuration = configuration;
   }

   /**
    * @param contentService The contentService to set.
    */
   public void setContentService(ContentService contentService)
   {
      this.contentService = contentService;
   }

   /**
    * @param namespaceService The namespaceService to set.
    */
   public void setNamespaceService(NamespaceService namespaceService)
   {
      this.namespaceService = namespaceService;
   }

   /**
    * @param nodeService The nodeService to set.
    */
   public void setNodeService(NodeService nodeService)
   {
      this.nodeService = nodeService;
   }

   /**
    * @param parapheurService The parapheurService to set.
    */
   public void setParapheurService(ParapheurService parapheurService)
   {
      this.parapheurService = parapheurService;
   }

   /**
    * @param searchService The searchService to set.
    */
   public void setSearchService(SearchService searchService)
   {
      this.searchService = searchService;
   }

   /**
    * @return the enabled
    */
   public boolean isEnabled()
   {
      return enabled;
   }

   /**
    * @param enabled the enabled to set
    */
   public void setEnabled(boolean enabled)
   {
      this.enabled = enabled;
   }

   /* (non-Javadoc)
    * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
    */
   public void afterPropertiesSet() throws Exception
   {
      Assert.notNull(nodeService, "There must be a node service");
      Assert.notNull(searchService, "There must be a search service");
      Assert.notNull(contentService, "There must be a content service");
      Assert.notNull(namespaceService, "There must be a namespace service");
      Assert.notNull(parapheurService, "There must be a parapheur service");
      Assert.notNull(configuration, "There must be a configuration element");
   }


   /**
    * @see com.atolcd.parapheur.repo.ParapheurService#getXadesSignatureProperties()
    */
   public Properties getXadesSignatureProperties()
   {
       Properties props = new Properties();
       props.setProperty("pPolicyIdentifierID", this.configuration.getProperty("s2low.helios.pPolicyIdentifierID"));
       props.setProperty("pPolicyIdentifierDescription", this.configuration.getProperty("s2low.helios.pPolicyIdentifierDescription"));
	// calcul du Hash de la politique de signature?? non, on prendra ça depuis le fichier de conf.
	/* byte[] lhash = getBytesDigest(pPolicyIdentifierDescription.getBytes(), "SHA1");
	StringBuilder builder = new StringBuilder();
	for(byte dataByte: lhash)
	    {
	       builder.append(Integer.toHexString((dataByte & 0xF0)>>4));
	       builder.append(Integer.toHexString(dataByte & 0x0F));
	    }  */
       props.setProperty("pPolicyDigest", this.configuration.getProperty("s2low.helios.pPolicyDigest"));
       props.setProperty("pSPURI", this.configuration.getProperty("s2low.helios.pSPURI"));
       props.setProperty("pCity", this.configuration.getProperty("s2low.helios.pCity"));
       props.setProperty("pPostalCode", this.configuration.getProperty("s2low.helios.pPostalCode"));
       props.setProperty("pCountryName", this.configuration.getProperty("s2low.helios.pCountryName"));
       props.setProperty("pClaimedRole", this.configuration.getProperty("s2low.helios.pClaimedRole"));
       logger.debug("getXadesSignatureProperties"+ props.toString());
       return props;
   }

   
   /**
    * @see com.atolcd.parapheur.repo.ParapheurService#getS2lowClassificationNode()
    */
   public NodeRef getS2lowActesClassificationNodeRef()
   {
      NodeRef res = null;
      
      String xpath = this.configuration.getProperty("spaces.company_home.childname") + "/" +
      this.configuration.getProperty("spaces.dictionary.childname") + "/" + 
      this.configuration.getProperty("spaces.configs2low.childname");
      
      List<NodeRef> results = null;
      try
      {
         results = searchService.selectNodes(
               nodeService.getRootNode(new StoreRef(this.configuration.getProperty("spaces.store"))),
               xpath,
               null,
               namespaceService,
               false);
         if (results != null && results.size() == 1)
         {
            // found what we were looking for
            logger.debug("getS2lowClassificationNodeRef: Found 1 node");
            res = results.get(0);
         }
      }
      catch (AccessDeniedException err)
      {
         // ignore and return null
	 logger.debug("getS2lowClassificationNodeRef: Access denied Exception");
         return null;
      }
      
      return res;
   }
   
   /**
    * @see com.atolcd.parapheur.repo.ParapheurService#getS2lowNatures()
    */
   @SuppressWarnings("unchecked")
   public Map<Integer, String> getS2lowActesNatures()
   {
      Map<Integer, String> mapRes = new HashMap<Integer, String>();
      
      logger.debug("getS2lowNatures: BEGIN");
      NodeRef configRef = getS2lowActesClassificationNodeRef();
      if (configRef == null)
	  logger.debug("getS2lowNatures: configRef is null");
      ContentReader contentreader = contentService.getReader(configRef, ContentModel.PROP_CONTENT);
      SAXReader saxreader = new SAXReader();
      
      try
      {
         Document document = saxreader.read(contentreader.getContentInputStream());
         Element rootElement = document.getRootElement();
         Element natures = rootElement.element("NaturesActes");
         if (natures != null)
         {
            Iterator<Element> elementIterator = (Iterator<Element>) natures.elementIterator("NatureActe");
			for (Iterator<Element> i=elementIterator; i.hasNext(); )
            {
               Element nature = i.next();
               mapRes.put(Integer.parseInt(nature.attribute("CodeNatureActe").getValue()),
                     nature.attribute("Libelle").getValue().toString());
            }
         }
      }
      catch(DocumentException e)
      {
         return null;
      }
      
      return mapRes;
   }
   
   /**
    * @see com.atolcd.parapheur.repo.ParapheurService#getS2lowClassifications()
    */
   @SuppressWarnings("unchecked")
   public Map<String, String> getS2lowActesClassifications()
   {
      Map<String, String> mapRes = new HashMap<String, String>();
      
      NodeRef configRef = getS2lowActesClassificationNodeRef();
      ContentReader contentreader = contentService.getReader(configRef, ContentModel.PROP_CONTENT);
      SAXReader saxreader = new SAXReader();
      
      try
      {
         Document document = saxreader.read(contentreader.getContentInputStream());
         Element rootElement = document.getRootElement();
         Element matieres1 = rootElement.element("Matieres");
         if (matieres1 != null)
         {
            for (Iterator<Element> i1=matieres1.elementIterator("Matiere1"); i1.hasNext(); )
            {
               Element matiere1 = i1.next();
               String id1 = matiere1.attribute("CodeMatiere").getValue().toString();
               mapRes.put(id1,matiere1.attribute("Libelle").getValue().toString());
               
               for (Iterator<Element> i2=matiere1.elementIterator("Matiere2"); i2.hasNext(); )
               {
                  Element matiere2 = i2.next();
                  String id2 = id1 + "-" +matiere2.attribute("CodeMatiere").getValue().toString();
                  mapRes.put(id2,matiere2.attribute("Libelle").getValue().toString());
                  
                  for (Iterator<Element> i3=matiere2.elementIterator("Matiere3"); i3.hasNext(); )
                  {
                     Element matiere3 = i3.next();
                     String id3 = id2 + "-" +matiere3.attribute("CodeMatiere").getValue().toString();
                     mapRes.put(id3,matiere3.attribute("Libelle").getValue().toString());
                     
                     for (Iterator<Element> i4=matiere3.elementIterator("Matiere4"); i4.hasNext(); )
                     {
                        Element matiere4 = i4.next();
                        String id4 = id3 + "-" +matiere4.attribute("CodeMatiere").getValue().toString();
                        mapRes.put(id4,matiere4.attribute("Libelle").getValue().toString());
                        
                        for (Iterator<Element> i5=matiere4.elementIterator("Matiere5"); i5.hasNext(); )
                        {
                           Element matiere5 = i5.next();
                           String id5 = id4 + "-" +matiere5.attribute("CodeMatiere").getValue().toString();
                           mapRes.put(id5,matiere5.attribute("Libelle").getValue().toString());
                        }
                     }
                  }
               }
            }
         }
      }
      catch(DocumentException e)
      {
         return null;
      }
      
      return mapRes;
   }

   /**
    * @see com.atolcd.parapheur.repo.ParapheurService#setS2lowArchiveURL(org.alfresco.service.cmr.repository.NodeRef)
    */
   public String setS2lowActesArchiveURL(NodeRef archive) throws IOException
   {
      Assert.isTrue(this.nodeService.hasAspect(archive, ParapheurModel.ASPECT_S2LOW), "Le dossier n'a pas été envoyé à la plate-forme S2LOW");
      String res = null;
      
      String serverAddress = this.configuration.getProperty("s2low.server");
      String port = this.configuration.getProperty("s2low.port");
      String baseURL = this.configuration.getProperty("s2low.baseUrlArchivage");

      String trsId= this.nodeService.getProperty(archive, ParapheurModel.PROP_TRANSACTION_ID).toString();
      
      if (baseURL == null)
      {
	 baseURL = "http://localhost:8080/parapheur/";
	 logger.debug("---- /!\\Impossible de prendre le parametre de config: s2low.baseUrlArchivage");
      }
      else
      {
	  logger.debug("------ /!\\ baseUrlArchivage configuration OK -------");
      }
      logger.debug("setS2lowArchiveURL: trsId="+ trsId);
      
      // Initialisation des objets HttpClient
      // COMMONS-SSL
      try
      {
         EasySSLProtocolSocketFactory easy = new EasySSLProtocolSocketFactory();
         KeyMaterial km = new KeyMaterial(this.configuration.getProperty("s2low.certificat"), this.configuration.getProperty("s2low.password").toCharArray());
         easy.setKeyMaterial(km);
         Protocol easyhttps = new Protocol("https", (ProtocolSocketFactory)easy, Integer.parseInt(port));
         Protocol.registerProtocol("https", easyhttps);
      }
      catch (Exception e)
      {
         logger.warn("Erreur lors de la modification du protocole https");
         logger.warn(e.getStackTrace());
      }
      
      // HTTPCLIENT script /modules/actes/actes_transac_set_archive_url.php
      HttpClient client = new HttpClient();
      PostMethod post = new PostMethod("https://" + serverAddress + ":" + port + "/modules/actes/actes_transac_set_archive_url.php");

      // On ne peut renseigner l'archive que si la transaction existe
      // POST id + url + api=1  , retour OK, KO

      String urlArchive = baseURL + "wcs/parapheur/archives/s2low.html?trsId=" + trsId;
      logger.debug("setS2lowArchiveURL: baseURL=["+ baseURL +"], URL1="+ urlArchive);

      // Remplissage des paramètres POST
      ArrayList<Part> parts = new ArrayList<Part>();
      parts.add(new StringPart("id", trsId));
      parts.add(new StringPart("url", urlArchive));
      parts.add(new StringPart("api", "1"));

      Part[] partsTab = new Part[parts.size()];
      for (int i=0; i<parts.size(); i++)
         partsTab[i] = parts.get(i);
      
//      partsTab[0] = new StringPart("id", trsId );
//      partsTab[1] = new StringPart("url", urlArchive);
//      partsTab[2] = new StringPart("api", "1");
     
      //ExternalAccessServlet.resolveNamePath(sc, path)
      //urlArchive = ExternalAccessServlet.generateExternalURL(ExternalAccessServlet.OUTCOME_BROWSE, archive.getStoreRef()+"/stv/"+archive.getId());
      //logger.debug("setS2lowArchiveURL: URL2="+ urlArchive);
      
      post.setRequestEntity(new MultipartRequestEntity(partsTab, post.getParams()));
      // Exécution de la méthode POST
      try
      {
	 logger.debug("=== Before executeMethod");
	 int status = client.executeMethod(post);
	 logger.debug("=== After  executeMethod");
         if (HttpStatus.SC_OK == status)
         {
            String reponse = post.getResponseBodyAsString();
            String[] tab = reponse.split("\n");
            if (tab.length == 1 || "KO".equals(tab[0]))
            {
               String error = "Erreur retournée par la plate-forme s2low : "; 
               for (int i=1; i<tab.length; i++)
                  error += tab[i];
               throw new RuntimeException(error);
            }
            // La transaction s'est bien passée, on sort gentiment
            logger.debug("C'est OK!");
            res = urlArchive;
         }
         else
         {
            throw new IOException("Echec de la récupération de la connexion à la plate-forme : statut = " + status);
         }
      }
      finally
      {
         post.releaseConnection();
      }

      // ça semble assez débile et hasardeux de se reposer sur le client Web
      // TODO  --> étudier le webscript, plus simple; mais attention à l'authentification (wcs?)
      return res;
   }

   /**
    * @see com.atolcd.parapheur.repo.ParapheurService#getInfosS2low(org.alfresco.service.cmr.repository.NodeRef)
    */
   public int getInfosS2low(NodeRef dossier) throws IOException
   {
      Assert.isTrue(this.parapheurService.isDossier(dossier), "Node Ref doit être de type ph:dossier");
      Assert.isTrue(this.parapheurService.isTermine(dossier), "Le dossier n'est pas terminé");
      Assert.isTrue(this.nodeService.hasAspect(dossier, ParapheurModel.ASPECT_S2LOW), "Le dossier n'a pas été envoyé à la plate-forme S2LOW");
       
      if ("HELIOS".equals(this.nodeService.getProperty(dossier, ParapheurModel.PROP_TDT_PROTOCOLE)))
	 return getInfosS2lowHelios(dossier);
      else
	 return getInfosS2lowActes(dossier);
   }
   
   private int getInfosS2lowActes(NodeRef dossier) throws IOException
   {
      String res = null;
      String serverAddress = this.configuration.getProperty("s2low.server");
      String port = this.configuration.getProperty("s2low.port");
      int codeRetour = -1;
      
      // Initialisation des objets HttpClient
      // COMMONS-SSL
      try
      {
         EasySSLProtocolSocketFactory easy = new EasySSLProtocolSocketFactory();
         KeyMaterial km = new KeyMaterial(this.configuration.getProperty("s2low.certificat"), this.configuration.getProperty("s2low.password").toCharArray());
         easy.setKeyMaterial(km);
         Protocol easyhttps = new Protocol("https", (ProtocolSocketFactory)easy, Integer.parseInt(port));
         Protocol.registerProtocol("https", easyhttps);
      }
      catch (Exception e)
      {
         logger.warn("Erreur lors de la modification du protocole https");
         logger.warn(e.getStackTrace());
      }
      
      // HTTPCLIENT
      HttpClient client = new HttpClient();
      GetMethod get = new GetMethod("https://" + serverAddress + ":" + port + 
            "/modules/actes/actes_transac_get_status.php?transaction=" + 
            this.nodeService.getProperty(dossier, ParapheurModel.PROP_TRANSACTION_ID));
      
      // Exécution de la méthode GET
      try
      {
         int status = client.executeMethod(get);
         if (HttpStatus.SC_OK == status)
         {
            String reponse = get.getResponseBodyAsString();
            String[] tab = reponse.split("\n");
            if (tab.length == 1 || "KO".equals(tab[0]))
            {
               String error = "Erreur retournée par la plate-forme s2low : "; 
               for (int i=1; i<tab.length; i++)
                  error += tab[i];
               throw new RuntimeException(error);
            }
            
            // La méthode a changé en 1.0.5, logger ce qu'on a...
            for (int i=0; i<tab.length; i++)
        	logger.debug("Réponse Statut S2LOW: ["+ tab[i] +"]");
            
            // La  transaction s'est bien passée, on renvoie le statut (ligne 2)
            codeRetour = Integer.parseInt(tab[1]);
            res = statutS2lowToString(codeRetour);
            // On enregistre le statut renvoyé
            this.nodeService.setProperty(dossier, ParapheurModel.PROP_STATUS, res);
            
            // Avec S2LOW 1.0.5 ACTES: Dans les cas 3,4 et 6, le status est accompagné du message de retour envoyé par la préfecture
            // Seuls les cas 4-Ack et 6-Nack sont pertinents ici
            if (tab.length > 2 && (codeRetour==4 || codeRetour==6))
            {
               String ARActeXml = "";
               for (int i=2; i<tab.length; i++)
                  ARActeXml += tab[i] + "\n";
               // On enregistre dans PROP_ARACTE_XML
               ContentWriter arWriter = this.contentService.getWriter(dossier, ParapheurModel.PROP_ARACTE_XML, true);
               arWriter.setMimetype(MimetypeMap.MIMETYPE_XML);
               arWriter.setEncoding("UTF-8");  // FIXME: c'est peut-etre ISO-8859-1
               arWriter.putContent(ARActeXml);
            }
         }
         else
         {
            throw new RuntimeException("Echec de la récupération de la connexion à la plate-forme : statut = " + status);
         }
      }
      finally
      {
         get.releaseConnection();
      }
      
      return codeRetour;
   }
   
   private int getInfosS2lowHelios(NodeRef dossier) throws IOException
   {
      String serverAddress = this.configuration.getProperty("s2low.helios.server");
      String port = this.configuration.getProperty("s2low.helios.port");
      int codeRetour = -1;
      
      // Initialisation des objets HttpClient
      // COMMONS-SSL
      try
      {
         EasySSLProtocolSocketFactory easy = new EasySSLProtocolSocketFactory();
         KeyMaterial km = new KeyMaterial(this.configuration.getProperty("s2low.helios.certificat"), this.configuration.getProperty("s2low.helios.password").toCharArray());
         easy.setKeyMaterial(km);
         Protocol easyhttps = new Protocol("https", (ProtocolSocketFactory)easy, Integer.parseInt(port));
         Protocol.registerProtocol("https", easyhttps);
      }
      catch (Exception e)
      {
         logger.warn("Erreur lors de la modification du protocole https");
         logger.warn(e.getStackTrace());
      }
      
      // HTTPCLIENT
      HttpClient client = new HttpClient();
      GetMethod get = new GetMethod("https://" + serverAddress + ":" + port + 
            "/modules/helios/api/helios_transac_get_status.php?transaction=" + 
            this.nodeService.getProperty(dossier, ParapheurModel.PROP_TRANSACTION_ID));
      
      // Exécution de la méthode GET
      try
      {
         int status = client.executeMethod(get);
         if (HttpStatus.SC_OK == status)
         {
            InputStream reponse = get.getResponseBodyAsStream();
            if (logger.isDebugEnabled())
        	logger.debug("Reponse s2low=["+reponse.toString()+"]");
            //            <transaction>
            //              <id> numéro de la transaction  </id>
            //              <resultat> OK ou KO </resultat>
            //              <status> status </status>
            //              <message> message complémentaire </message>
            //            </transaction>
            SAXReader saxreader = new SAXReader();
            try
            {
               Document document = saxreader.read(reponse);
               Element rootElement = document.getRootElement();
               Element xid = rootElement.element("id");//.getTextTrim();
               Element xresultat = rootElement.element("resultat");
               Element xstatus = rootElement.element("status");
               Element xmessage = rootElement.element("message");
               if (xid == null || xresultat == null || xstatus == null || "KO".equals(xresultat.getTextTrim()))
               {
                   String error = "Erreur retournée par la plate-forme s2low : ";
                   if (xmessage!=null)
                       error += xmessage.getTextTrim();
                   throw new RuntimeException(error);
               }
               // La  transaction s'est bien passée, on enregistre le statut
               codeRetour = Integer.parseInt(xstatus.getTextTrim());
               this.nodeService.setProperty(dossier, ParapheurModel.PROP_STATUS, statutS2lowToString(codeRetour));

               // TODO : que faire du message complémentaire? AUDIT!
            }
            catch(DocumentException e)
            {
               throw new RuntimeException("Echec de la récupération de la réponse du TdT");
            }
         }
         else
         {
            throw new RuntimeException("Echec de la récupération de la connexion à la plate-forme : statut = " + status);
         }
      }
      finally
      {
         get.releaseConnection();
      }
      
      return codeRetour;
   }

   /**
    * @see com.atolcd.parapheur.repo.ParapheurService#statutS2lowToString(int)
    */
   public String statutS2lowToString(int code)
   {
       String res = null;
       switch (code)
       {
       case -1 : res = "Erreur"; break;
       case 0 : res = "Annulé"; break;
       case 1 : res = "Posté"; break;
       case 2 : res = "En attente de transmission"; break;
       case 3 : res = "Transmis"; break;
       case 4 : res = "Acquittement reçu"; break;
       case 5 : res = "Validé"; break;
       case 6 : res = "Refusé"; break;
       case 7 : res = "En traitement"; break;
       }
       return res;
   }
   
   /**
    * @see com.atolcd.parapheur.repo.ParapheurService#envoiS2low(org.alfresco.service.cmr.repository.NodeRef, java.lang.String)
    */
   public void envoiS2lowActes(NodeRef dossier, String nature, String classification, String numero, String objet, String date) throws IOException
   {
      Assert.isTrue(this.parapheurService.isDossier(dossier),"Node Ref doit être de type ph:dossier");
      Assert.isTrue(this.parapheurService.isTermine(dossier), "Le dossier n'est pas terminé");
      Assert.isTrue(!this.nodeService.hasAspect(dossier, ParapheurModel.ASPECT_S2LOW), "Le dossier a déjà été envoyé à la plate-forme S2LOW");
      
      String serverAddress = this.configuration.getProperty("s2low.server");
      String port = this.configuration.getProperty("s2low.port");
      
      // Initialisation des objets HttpClient
      // COMMONS-SSL
      try
      {
         EasySSLProtocolSocketFactory easy = new EasySSLProtocolSocketFactory();
         KeyMaterial km = new KeyMaterial(this.configuration.getProperty("s2low.certificat"), this.configuration.getProperty("s2low.password").toCharArray());
         easy.setKeyMaterial(km);
         Protocol easyhttps = new Protocol("https", (ProtocolSocketFactory)easy, Integer.parseInt(port));
         Protocol.registerProtocol("https", easyhttps);
      }
      catch (Exception e)
      {
         logger.warn("Erreur lors de la modification du protocole https");
         logger.warn(e.getStackTrace());
      }
      
      // HTTPCLIENT
      HttpClient client = new HttpClient();
      PostMethod post = new PostMethod("https://" + serverAddress + ":" + port + "/modules/actes/actes_transac_create.php");
      
      // Récupération des fichiers
      List<NodeRef> documents = this.parapheurService.getDocuments(dossier);
      ArrayList<FilePart> fileParts = new ArrayList<FilePart>();
      // Premier fichier
      ContentReader mainDocReader = this.contentService.getReader(documents.get(0), ContentModel.PROP_CONTENT);
      logger.debug("envoiS2low ("+ documents.size() +" docs): ajout mainDoc type " + mainDocReader.getMimetype());
      File ficPdf = ensureMimeType(mainDocReader, MimetypeMap.MIMETYPE_PDF);
      fileParts.add(new FilePart("acte_pdf_file", ficPdf));
      if (MimetypeMap.MIMETYPE_PDF.equals(mainDocReader.getMimetype()))
      {
         byte[] signature = this.parapheurService.getSignature(dossier);
         if (signature != null && signature.length > 0)
         {
            logger.debug("envoiS2low: signature PDF presente");
            File ficSign = TempFileProvider.createTempFile("s2low", "p7s");
            FileOutputStream os = new FileOutputStream(ficSign);
            os.write(signature);
            os.close();
            fileParts.add(new FilePart("acte_pdf_file_sign", ficSign));
         }
      }
      
      // Suivants
      // TODO Pour l'instant les PJ sont envoyées sans signature
      for (int i=1; i<documents.size(); i++)
      {
         NodeRef doc = documents.get(i);
         // Correction bug #1869 pièces attachées ne sont pas converties: autorisé PDF JPG PNG
         ContentReader attReader = this.contentService.getReader(doc, ContentModel.PROP_CONTENT);
         if (MimetypeMap.MIMETYPE_IMAGE_JPEG.equalsIgnoreCase(attReader.getMimetype())
        	 || MimetypeMap.MIMETYPE_IMAGE_PNG.equalsIgnoreCase(attReader.getMimetype()))
         {
             // authorized formats JPG/PNG: no need for PDF conversion
             logger.debug("envoiS2low (doc num "+ i +"): ajout piece jointe img: " + attReader.getMimetype());
             File ficPdfx = TempFileProvider.createTempFile("s2low-Att"+i, null);
             attReader.getContent(ficPdfx);
             fileParts.add(new FilePart("acte_attachments[]", ficPdfx));
         }
         else
         {
             // convert to PDF if needed
             logger.debug("envoiS2low (doc num "+ i +"): ajout piece jointe type " + attReader.getMimetype());
             File ficPdfx = ensureMimeType(attReader, MimetypeMap.MIMETYPE_PDF);
             fileParts.add(new FilePart("acte_attachments[]", ficPdfx));
         }
//         File ficPdfx = TempFileProvider.createTempFile("s2low-Att"+i, attReader.getMimetype());
//         attReader.getContent(ficPdfx);
//         fileParts.add(new FilePart("acte_attachments[]", ficPdfx));
      }
      
      // Lecture de la classification
      ArrayList<StringPart> classifParts = new ArrayList<StringPart>();      
      String[] classifications = classification.split("-");
      for (int i=1; i<=classifications.length; i++)
      {
         classifParts.add(new StringPart("classif"+i, classifications[i-1]));
      }
      
      // Remplissage des paramètres POST
      ArrayList<Part> parts = new ArrayList<Part>();
      parts.add(new StringPart("api", "1"));
      parts.add(new StringPart("nature_code", nature));
      for (StringPart classif : classifParts)
         parts.add(classif);
      parts.add(new StringPart("number", new String (numero.getBytes(), "ISO-8859-1" )));
      parts.add(new StringPart("decision_date", new String (date.getBytes(), "ISO-8859-1" )));

      // FIXME l'encodage de l'objet est pourri!!
      logger.debug("envoiS2low: number [" + numero + "], [" + new String (numero.getBytes(), "ISO-8859-1" ) + "]");
      logger.debug("envoiS2low: decision_date [" + date + "], [" + new String (date.getBytes(), "ISO-8859-1" ) + "]");
      logger.debug("envoiS2low: objet [" + objet + "], [" + new String (objet.getBytes(), "ISO-8859-1" ) + "]");
      if (Charset.isSupported("ISO-8859-1"))
	  logger.debug("  Default charset: " + Charset.defaultCharset()+ ", ISO-8859-1 is supported");

      parts.add(new StringPart("subject", new String (objet.getBytes("UTF-8") )));
      for (FilePart ficPart : fileParts)
         parts.add(ficPart);
   
      Part[] partsTab = new Part[parts.size()];
      for (int i=0; i<parts.size(); i++)
         partsTab[i] = parts.get(i);
      
      logger.debug("   Request Charset: " + post.getRequestCharSet());
      
      
      post.setRequestEntity(new MultipartRequestEntity(partsTab, post.getParams()));
      // Exécution de la méthode POST
      try
      {
         logger.debug("QueryString=[[" + post.toString() + "]]");
         int status = client.executeMethod(post);
         if (HttpStatus.SC_OK == status)
         {
            String reponse = post.getResponseBodyAsString();
            String[] tab = reponse.split("\n");
            if (tab.length == 1 || "KO".equals(tab[0]))
            {
               String error = "Erreur retournée par la plate-forme s2low : "; 
               for (int i=1; i<tab.length; i++)
                  error += tab[i];
               throw new RuntimeException(error);
            }
            
            // La  transaction s'est bien passée, on renvoie son identifiant
            int res = Integer.parseInt(tab[1]);
            // On enregistre le numéro de transaction attribué par la plate-forme
            Map<QName, Serializable> pptes = new HashMap<QName, Serializable>();
            pptes.put(ParapheurModel.PROP_TRANSACTION_ID, res);
            this.nodeService.addAspect(dossier, ParapheurModel.ASPECT_S2LOW, pptes);
         }
         else
         {
            throw new RuntimeException("Echec de la récupération de la connexion à la plate-forme : statut = " + status);
         }
      }
      finally
      {
         post.releaseConnection();
      }
   }

   /**
    * @see com.atolcd.parapheur.repo.S2lowService#envoiS2lowHelios(org.alfresco.service.cmr.repository.NodeRef)
    */
   public void envoiS2lowHelios(NodeRef dossier) throws IOException
   {
       Assert.isTrue(this.parapheurService.isDossier(dossier),"Node Ref doit être de type ph:dossier");
       Assert.isTrue(this.parapheurService.isTermine(dossier), "Le dossier n'est pas terminé");
       Assert.isTrue(!this.nodeService.hasAspect(dossier, ParapheurModel.ASPECT_S2LOW), "Le dossier a déjà été envoyé à la plate-forme S2LOW");
       
       String serverAddress = this.configuration.getProperty("s2low.helios.server");
       String port = this.configuration.getProperty("s2low.helios.port");
       
       // Initialisation des objets HttpClient
       // COMMONS-SSL
       try
       {
          EasySSLProtocolSocketFactory easy = new EasySSLProtocolSocketFactory();
          KeyMaterial km = new KeyMaterial(this.configuration.getProperty("s2low.helios.certificat"), this.configuration.getProperty("s2low.helios.password").toCharArray());
          easy.setKeyMaterial(km);
          Protocol easyhttps = new Protocol("https", (ProtocolSocketFactory)easy, Integer.parseInt(port));
          Protocol.registerProtocol("https", easyhttps);
       }
       catch (Exception e)
       {
          logger.warn("Erreur lors de la modification du protocole https");
          logger.warn(e.getStackTrace());
       }
       
       // HTTPCLIENT
       HttpClient client = new HttpClient();
       PostMethod post = new PostMethod("https://" + serverAddress + ":" + port + "/modules/helios/api/helios_import_fichier.php");
       
       // Récupération des fichiers
       List<NodeRef> documents = this.parapheurService.getDocuments(dossier);
       ArrayList<FilePart> fileParts = new ArrayList<FilePart>();
       ArrayList<Part> parts = new ArrayList<Part>();

       // Premier fichier; Cette version d'API ne permet l'envoi que d'un fichier XML (le premier doc du dossier)
       ContentReader mainDocReader = this.contentService.getReader(documents.get(0), ContentModel.PROP_CONTENT);
       if (logger.isDebugEnabled())
	   logger.debug("envoiS2lowHelios ("+ documents.size() +" docs): ajout mainDoc type " + mainDocReader.getMimetype());
       File ficXml = ensureMimeType(mainDocReader, MimetypeMap.MIMETYPE_XML);
       fileParts.add(new FilePart("enveloppe", ficXml));

       for (FilePart ficPart : fileParts)
          parts.add(ficPart);
       Part[] partsTab = new Part[parts.size()];
       for (int i=0; i<parts.size(); i++)
          partsTab[i] = parts.get(i);

       if (logger.isDebugEnabled())
	   logger.debug("   Request Charset: " + post.getRequestCharSet());
       post.setRequestEntity(new MultipartRequestEntity(partsTab, post.getParams()));
       // Exécution de la méthode POST
       try
       {
          // logger.debug("QueryString=[[" + post.toString() + "]]");
          int status = client.executeMethod(post);
          if (HttpStatus.SC_OK == status)
          {
             InputStream reponse = post.getResponseBodyAsStream();
             if (logger.isDebugEnabled())
        	 logger.debug("Reponse s2low=["+reponse.toString()+"]");
             // Le retour est un message formaté XML
             //           <import>
             //             <id> numéro de la transaction créée </id>
             //             <resultat> OK ou KO </resultat>
             //             <message> message complémentaire </message>
             //           </import>
             SAXReader saxreader = new SAXReader();
             try
             {
                Document document = saxreader.read(reponse);
                Element rootElement = document.getRootElement();
                Element id = rootElement.element("id");//.getTextTrim();
                Element resultat = rootElement.element("resultat");
                Element message = rootElement.element("message");
                if (id == null || resultat == null || "KO".equals(resultat.getTextTrim()))
                {
                    String error = "Erreur retournée par la plate-forme s2low : ";
                    if (message!=null)
                	error += message.getTextTrim();
                    throw new RuntimeException(error);
                }
                // La  transaction s'est bien passée, on enregistre le numéro de transaction
                int res = Integer.parseInt(id.getTextTrim());
                Map<QName, Serializable> pptes = new HashMap<QName, Serializable>();
                pptes.put(ParapheurModel.PROP_TRANSACTION_ID, res);
                this.nodeService.addAspect(dossier, ParapheurModel.ASPECT_S2LOW, pptes);
             }
             catch(DocumentException e)
             {
                throw new RuntimeException("Echec de la récupération de la réponse du TdT");
             }
          }
          else
          {
             throw new RuntimeException("Echec de la récupération de la connexion à la plate-forme : statut = " + status);
          }
       }
       finally
       {
          post.releaseConnection();
       }
   }

   /**
    * @see com.atolcd.parapheur.repo.S2lowService#getS2lowHeliosListePES_Retour()
    */
   @SuppressWarnings("unchecked")
   public void getS2lowHeliosListePES_Retour() throws IOException
   {
       // get liste des ids de pes_retour selon numéro de collectivité
       
       if (this.isEnabled()) {
       
       String serverAddress = this.configuration.getProperty("s2low.helios.server");
       String port = this.configuration.getProperty("s2low.helios.port");
       String idCollectivite = this.configuration.getProperty("s2low.helios.collectivite");
       String nomParapheur = this.configuration.getProperty("s2low.helios.parapheur-pesretour");
       logger.debug("getS2lowHeliosListePES_Retour Debut");
       
       // Initialisation des objets HttpClient
       // COMMONS-SSL
       try
       {
          EasySSLProtocolSocketFactory easy = new EasySSLProtocolSocketFactory();
          KeyMaterial km = new KeyMaterial(this.configuration.getProperty("s2low.helios.certificat"), this.configuration.getProperty("s2low.helios.password").toCharArray());
          easy.setKeyMaterial(km);
          Protocol easyhttps = new Protocol("https", (ProtocolSocketFactory)easy, Integer.parseInt(port));
          Protocol.registerProtocol("https", easyhttps);
       }
       catch (Exception e)
       {
          logger.warn("Erreur lors de la modification du protocole https");
          logger.warn(e.getStackTrace());
       }
       
       // HTTPCLIENT
       HttpClient client = new HttpClient();
       GetMethod get = new GetMethod("https://" + serverAddress + ":" + port + 
             "/modules/helios/api/helios_get_list.php?collectivite=" + idCollectivite);
       
       // Exécution de la méthode GET
       try
       {
	  logger.debug("Debut: try");
          int status = client.executeMethod(get);
          logger.debug("After helios_get_list.php?collectivite=" + idCollectivite);
          if (HttpStatus.SC_OK == status)
          {
             String sreponse = get.getResponseBodyAsString();
             if (logger.isDebugEnabled())
           	logger.debug("Reponse s2low=["+ sreponse +"]");
             InputStream reponse = get.getResponseBodyAsStream();
             //     <liste>
             //             <idColl> identifiant de la collectivité </idColl>
             //             <dateDemande> date de demande de la liste </dateDemande>
             //             <resultat> OK ou KO </resultat>
             //             <message> message complémentaire </message>
             //             <pes_retour>
             //                 <id> identifiant du pes_retour  </id>
             //                 <nom> nom </nom>
             //                 <date> date de réception du pes_retour par le tdt </date>
             //             </pes_retour>
             //             <pes_retour>
             //                 <id> identifiant du pes_retour  </id>
             //                 <nom> nom </nom>
             //                 <date> date de réception du pes_retour par le tdt </date>
             //             </pes_retour>
             //             ...
             //     </liste>

             SAXReader saxreader = new SAXReader();
             try
             {
                Document document = saxreader.read(reponse);
                Element rootElement = document.getRootElement();
                Element xresultat = rootElement.element("resultat");
                Element xmessage = rootElement.element("message");
                if (xresultat == null || "KO".equals(xresultat.getTextTrim()))
                {
                    String error = "Erreur retournée par la plate-forme s2low : ";
                    if (xmessage!=null)
                        error += xmessage.getTextTrim();
                    logger.error(error);
                    throw new RuntimeException(error);
                }
                // La  transaction s'est bien passée, on traite la liste
                Iterator<Element> elementIterator = (Iterator<Element>) rootElement.elementIterator("pes_retour");
		for (Iterator<Element> i=elementIterator; i.hasNext(); )
		{
		    Element pes_retour = i.next();
		    String idPes = pes_retour.element("id").toString();
		    if (logger.isDebugEnabled())
			logger.debug("Trouvé: id=" + idPes +
				", nom=" + pes_retour.element("nom") +
				", date="+ pes_retour.element("date"));
		    // TODO 
		    // 1- récup. PES_Retour
		    InputStream xmlPesRetour = getS2lowHeliosPES_Retour(idPes);
		    // 2- créer dossier PES.    WARN: dans quel parapheur? info à choper dans fichier conf.: nomParapheur
		    NodeRef dossierCree = this.parapheurService.createDossierPesRetour(idPes, nomParapheur, xmlPesRetour);
		    // 3- si ok: changer status à "lu" sur s2low: helios_change_status.php
		    if (null != dossierCree)
		    {
			setS2lowHeliosChangeStatus(idPes);
		    }
		    
		}

             }
             catch(DocumentException e)
             {
                throw new RuntimeException("Echec de la récupération de la réponse du TdT");
             }
          }
          else
          {
             throw new RuntimeException("Echec de la récupération de la connexion à la plate-forme : statut = " + status);
          }
       }
       finally
       {
          get.releaseConnection();
       }
       } // if (this.isEnabled())    
   }
   
   private InputStream getS2lowHeliosPES_Retour(String idRetour) throws IOException
   {
      // helios_get_retour
      //   Assert.isInstanceOf(Integer.TYPE, Integer.parseInt(idRetour), "l'identifiant doit etre un entier");
      String serverAddress = this.configuration.getProperty("s2low.helios.server");
      String port = this.configuration.getProperty("s2low.helios.port");
       
      // Initialisation des objets HttpClient
      // COMMONS-SSL
      try
      {
	 EasySSLProtocolSocketFactory easy = new EasySSLProtocolSocketFactory();
	 KeyMaterial km = new KeyMaterial(this.configuration.getProperty("s2low.helios.certificat"), this.configuration.getProperty("s2low.helios.password").toCharArray());
	 easy.setKeyMaterial(km);
	 Protocol easyhttps = new Protocol("https", (ProtocolSocketFactory)easy, Integer.parseInt(port));
	 Protocol.registerProtocol("https", easyhttps);
      }
      catch (Exception e)
      {
	  logger.warn("Erreur lors de la modification du protocole https");
	  logger.warn(e.getStackTrace());
      }

      // HTTPCLIENT
      HttpClient client = new HttpClient();
      GetMethod get = new GetMethod("https://" + serverAddress + ":" + port + 
	      "/modules/helios/api/helios_get_retour.php?idRetour=" + idRetour);
      
      // Exécution de la méthode GET
      InputStream res = null;
      try
      {
         int status = client.executeMethod(get);
         if (HttpStatus.SC_OK == status)
         {
            res = get.getResponseBodyAsStream();
         }
         else
         {
            throw new RuntimeException("Echec de la récupération de la connexion à la plate-forme : statut = " + status);
         }
      }
      finally
      {
         get.releaseConnection();
      }
      return res;
   }
   
   private void setS2lowHeliosChangeStatus(String idRetour) throws IOException
   {
      //  helios_change_status.php
      String serverAddress = this.configuration.getProperty("s2low.helios.server");
      String port = this.configuration.getProperty("s2low.helios.port");

      // Initialisation des objets HttpClient
      // COMMONS-SSL
      try
      {
	 EasySSLProtocolSocketFactory easy = new EasySSLProtocolSocketFactory();
	 KeyMaterial km = new KeyMaterial(this.configuration.getProperty("s2low.helios.certificat"), this.configuration.getProperty("s2low.helios.password").toCharArray());
	 easy.setKeyMaterial(km);
	 Protocol easyhttps = new Protocol("https", (ProtocolSocketFactory)easy, Integer.parseInt(port));
	 Protocol.registerProtocol("https", easyhttps);
      }
      catch (Exception e)
      {
	  logger.warn("Erreur lors de la modification du protocole https");
	  logger.warn(e.getStackTrace());
      }

      // HTTPCLIENT
      HttpClient client = new HttpClient();
      GetMethod get = new GetMethod("https://" + serverAddress + ":" + port + 
	      "/modules/helios/api/helios_change_status.php?idRetour=" + idRetour);

      // Exécution de la méthode GET
      InputStream reponse = null;
      try
      {
         int status = client.executeMethod(get);
         if (HttpStatus.SC_OK == status)
         {
            reponse = get.getResponseBodyAsStream();
            if (logger.isDebugEnabled())
         	logger.debug("Reponse s2low=["+reponse.toString()+"]");
            //            <change>
            //              <id> numéro de la transaction  </id>
            //              <resultat> OK ou KO </resultat>
            //              <message> message complémentaire </message>
            //            </change>
            SAXReader saxreader = new SAXReader();
            try
            {
               Document document = saxreader.read(reponse);
               Element rootElement = document.getRootElement();
               Element xresultat = rootElement.element("resultat");
               Element xmessage = rootElement.element("message");
               if (xresultat == null || "KO".equals(xresultat.getTextTrim()))
               {
                   String error = "Erreur retournée par la plate-forme s2low : ";
                   if (xmessage!=null)
                       error += xmessage.getTextTrim();
                   throw new RuntimeException(error);
               }
            }
            catch(DocumentException e)
            {
               throw new RuntimeException("Echec de la récupération de la réponse du TdT");
            }  
         }
         else
         {
            throw new RuntimeException("Echec de la récupération de la connexion à la plate-forme : statut = " + status);
         }
      }
      finally
      {
         get.releaseConnection();
      }
   }

   private File ensureMimeType(ContentReader reader, String mimeType)
   {
      File fic = TempFileProvider.createTempFile("s2low", null);
      // Si le type MIME correspond, on l'envoie tel quel
      if(mimeType.equals(reader.getMimetype()))
      {
         reader.getContent(fic);
      }
      // Sinon, on commence par le transformer
      else
      {
         FileContentWriter tmpWriter = new FileContentWriter(fic);
         tmpWriter.setMimetype(mimeType);
         tmpWriter.setEncoding(reader.getEncoding());
         this.contentService.transform(reader, tmpWriter);
      }
      
      return fic;
   }

}
