/*
 * 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.web.bean;

import java.io.File;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.transaction.UserTransaction;

import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.context.IContextListener;
import org.alfresco.web.app.context.UIContextService;
import org.alfresco.web.bean.BrowseBean;
import org.alfresco.web.bean.FileUploadBean;
import org.alfresco.web.bean.repository.MapNode;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.NodePropertyResolver;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.ui.common.Utils;
import org.alfresco.web.ui.common.component.UIActionLink;
import org.alfresco.web.ui.common.component.data.UIRichList;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.atolcd.parapheur.model.ParapheurModel;
import com.atolcd.parapheur.repo.EtapeCircuit;
import com.atolcd.parapheur.repo.ParapheurService;
import com.atolcd.parapheur.repo.S2lowService;

public class AdminParapheurBean implements IContextListener
{
   private static Log logger = LogFactory.getLog(AdminParapheurBean.class);

   protected NodeService nodeService;
   protected ParapheurService parapheurService;
   protected S2lowService s2lowService;
   protected ContentService contentService;
   protected AuthenticationService authenticationService;
   protected BrowseBean browseBean;
   protected ParapheurBean parapheurBean;
   
   protected UIRichList parapheurRichList;
   protected UIRichList dossierRichList;
   
   private List<Node> parapheurs;
   private List<Node> dossiers;
   private NodeRef dossierCourant = null;
   private NodeRef parapheurCourant = null;
   
   private File file;
   private String fileName;
   
   public AdminParapheurBean()
   {
      UIContextService.getInstance(FacesContext.getCurrentInstance()).registerBean(this);
   }
   
   /**
    * @return Returns the nodeService.
    */
   public NodeService getNodeService()
   {
      return nodeService;
   }
   
   /**
    * @param nodeService The nodeService to set.
    */
   public void setNodeService(NodeService nodeService)
   {
      this.nodeService = nodeService;
   }
   
   /**
    * @return Returns the parapheurService.
    */
   public ParapheurService getParapheurService()
   {
      return parapheurService;
   }

   /**
    * @param parapheurService The parapheurService to set.
    */
   public void setParapheurService(ParapheurService parapheurService)
   {
      this.parapheurService = parapheurService;
   }
   
   /**
    * @param service the s2lowService to set
    */
   public void setS2lowService(S2lowService service)
   {
      s2lowService = service;
   }

   /**
    * @return Returns the contentService.
    */
   public ContentService getContentService()
   {
      return contentService;
   }

   /**
    * @param contentService The contentService to set.
    */
   public void setContentService(ContentService contentService)
   {
      this.contentService = contentService;
   }

   /**
    * @param browseBean The browseBean to set.
    */
   public void setBrowseBean(BrowseBean browseBean)
   {
      this.browseBean = browseBean;
   }

   /**
    * @param parapheurBean The parapheurBean to set.
    */
   public void setParapheurBean(ParapheurBean parapheurBean)
   {
      this.parapheurBean = parapheurBean;
   }

   /**
    * @param authenticationService The authenticationService to set.
    */
   public void setAuthenticationService(AuthenticationService authenticationService)
   {
      this.authenticationService = authenticationService;
   }

   /**
    * @return Returns the parapheurRichList.
    */
   public UIRichList getParapheurRichList()
   {
      return parapheurRichList;
   }
   /**
    * @param parapheurRichList The parapheurRichList to set.
    */
   public void setParapheurRichList(UIRichList racineRichList)
   {
      this.parapheurRichList = racineRichList;
   }
   
   /**
    * @return Returns the dossierRichList.
    */
   public UIRichList getDossierRichList()
   {
      return dossierRichList;
   }

   /**
    * @param dossierRichList The dossierRichList to set.
    */
   public void setDossierRichList(UIRichList dossierRichList)
   {
      this.dossierRichList = dossierRichList;
   }

   public String getParapheurCourant()
   {
      String res = "Inapplicable";
      if (this.parapheurCourant != null)
      {
         res = (String) this.nodeService.getProperty(this.parapheurCourant, ContentModel.PROP_NAME);
      }
      
      return res;
   }
   public String getParapheurPrecedent()
   {
      String res = "";
      if (this.parapheurCourant != null)
      {
         NodeRef prec = this.parapheurService.getParapheurResponsable(this.parapheurCourant);
         if (prec != null)
            res = (String)this.nodeService.getProperty(prec, ContentModel.PROP_NAME);
         else
            res = "Espace de stockage des parapheurs";
      }
      
      return res;
   }
   public Node getParapheurNode()
   {
      if (this.parapheurCourant != null)
         return new Node(this.parapheurCourant);
      else
      {
         if (this.getParapheurs().size() > 0)
            return this.getParapheurs().get(0);
         else
            return null;
      }
   }
   public Node getDossierNode()
   {
      if (this.dossierCourant != null)
         return new Node(this.dossierCourant);
      else
         return null;
   }

   /**
    * @return Returns the parapheurs.
    */
   public List<Node> getParapheurs()
   {
      if (this.parapheurs == null)
      {
         // Récupération des parapheurs
         List<NodeRef> parapheurs = this.parapheurService.getParapheurs();
         this.parapheurs = new ArrayList<Node>(parapheurs.size());
         if (this.parapheurCourant == null)
         {
            for (NodeRef tmpParapheur : parapheurs)
            {
               // Les parapheurs sans supérieur hiérarchique sont ajoutés à la liste de parapheurs
               if (this.parapheurService.getParapheurResponsable(tmpParapheur) == null)
               {
                  MapNode racine = new MapNode(tmpParapheur);
                  this.parapheurs.add(racine);
               }
            }
         }
         else
         {
            for (NodeRef tmpParapheur : parapheurs)
            {
               // Les parapheurs ayant pour supérieur hiérarchique le parapheur courant sont ajoutés à la liste de parapheurs
               if (this.parapheurCourant.equals(this.parapheurService.getParapheurResponsable(tmpParapheur)))
               {
                  MapNode parapheur = new MapNode(tmpParapheur);
                  this.parapheurs.add(parapheur);
               }
            }
         }
      }
      return this.parapheurs;
   }
   /**
    * @param parapheurs The parapheurs to set.
    */
   public void setParapheurs(List<Node> racines)
   {
      this.parapheurs = racines;
   }
      
   /**
    * @return Returns the dossiers.
    */
   public List<Node> getDossiers()
   {
      if (this.dossiers == null)
      {
         this.dossiers = new ArrayList<Node>();
         
         if (this.parapheurService.getParapheurs() != null)
         {            
            for (NodeRef parapheur : this.parapheurService.getParapheurs())
            {
               if (this.parapheurService.getCorbeilles(parapheur) != null)
               {
                  for (NodeRef corbeille : this.parapheurService.getCorbeilles(parapheur))
                  {
                     if (this.parapheurService.getDossiers(corbeille) != null)
                     {
                        for (NodeRef dossier : this.parapheurService.getDossiers(corbeille))
                        {
                           Node dossierNode = new MapNode(dossier);
                           dossierNode.addPropertyResolver("current", this.parapheurBean.resolverCurrent);
                           dossierNode.addPropertyResolver("etat", this.resolverState);
                           this.dossiers.add(dossierNode);
                        }
                     }
                  }
               }
            }
         }
      }
      return dossiers;
   }

   /**
    * @param dossiers The dossiers to set.
    */
   public void setDossiers(List<Node> dossiers)
   {
      this.dossiers = dossiers;
   }
   
   public void clickDossier(ActionEvent event)
   {
      UIActionLink link = (UIActionLink)event.getComponent();
      Map<String, String> params = link.getParameterMap();
      String id = params.get("id");
      if (id != null && id.length() != 0)
      {
         System.out.println("id="+id);
         this.dossierCourant = new NodeRef(Repository.getStoreRef(), id);
      }
   }

   public void clickParapheur(ActionEvent event)
   {
      UIActionLink link = (UIActionLink)event.getComponent();
      Map<String, String> params = link.getParameterMap();
      String id = params.get("id");
      NodeRef ref = null;
      if (id != null && id.length() != 0)
      {
         ref = new NodeRef(Repository.getStoreRef(), id);
      }
      else
      {
         if (this.parapheurCourant != null)
            ref = this.parapheurService.getParapheurResponsable(this.parapheurCourant);
      }
      
      try
      {
         this.parapheurCourant = ref;
         this.parapheurs = null;
         UIContextService.getInstance(FacesContext.getCurrentInstance()).notifyBeans();
      }
      catch (InvalidNodeRefException refErr)
      {
         Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
            FacesContext.getCurrentInstance(), Repository.ERROR_NODEREF), new Object[] {id}) );
      }
   }
   
   public String deleteDossierOK()
   {
      String outcome = "cancel";
      if (dossierCourant != null)
      {
         this.nodeService.deleteNode(dossierCourant);
         dossierCourant = null;
         
         UIContextService.getInstance(FacesContext.getCurrentInstance()).notifyBeans();
         outcome = "dossierDeleted";
      }
      return outcome;
   }
   
   /**
    * Called when the user confirms they wish to delete a "dossier" space
    * 
    * @return The outcome
    */
   public String deleteParapheurOK()
   {
      String outcome = "cancel";
      
      if (parapheurCourant != null)
      {
         // On vérifie le contenu du parapheur avant de lancer la suppression
         boolean suppression = true;
         List<NodeRef> corbeilles = this.parapheurService.getCorbeilles(this.parapheurCourant);
         for (NodeRef corbeille : corbeilles)
         {
            List<NodeRef> dossiers = this.parapheurService.getDossiers(corbeille);
            if (dossiers != null && !dossiers.isEmpty())
            {
               suppression = false;
               break;
            }
         }
         
         if (suppression)
         {
            // FIXME : doit-on changer les associations de hiérarchie des subordonnés ?
            /*
            List<AssociationRef> lstSubordonnes = this.nodeService.getSourceAssocs(parapheurRef, ParapheurModel.ASSOC_HIERARCHIE);
            for (AssociationRef assoc : lstSubordonnes)
            {
               this.nodeService.removeAssociation(assoc.getSourceRef(), assoc.getTargetRef(), ParapheurModel.ASSOC_HIERARCHIE);
               this.nodeService.createAssociation(assoc.getSourceRef(),this.parapheurService.getParapheurResponsable(parapheurRef),ParapheurModel.ASSOC_HIERARCHIE);
            }
            */
            
            // Suppression effective du parapheur
            NodeRef parentRef = this.nodeService.getPrimaryParent(this.parapheurCourant).getParentRef();
            String userName = this.parapheurService.getParapheurOwner(this.parapheurCourant);
            this.nodeService.deleteNode(this.parapheurCourant);
            this.parapheurCourant = null;
            this.parapheurs = null;
            
            UIContextService.getInstance(FacesContext.getCurrentInstance()).notifyBeans();
            
            // Suppression ok
            outcome = "parapheurDeleted";
            
            // Si l'admin vient de supprimer son propre parapheur, on met l'UI à jour
            if (this.authenticationService.getCurrentUserName().equals(userName))
               this.browseBean.updateUILocation(parentRef);
         }
         else
         {
            Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
                    FacesContext.getCurrentInstance(), "error_generic"), "Le parapheur contient des dossiers"));
            logger.error("Impossible de supprimer le parapheur, car il contient des dossiers : id = " + this.parapheurCourant);
            outcome = "";
         }
      }
      return outcome;
   }
   
   public String getFileName()
   {
      // try and retrieve the file and filename from the file upload bean
      // representing the file we previously uploaded.
      FacesContext ctx = FacesContext.getCurrentInstance();
      FileUploadBean fileBean = (FileUploadBean)ctx.getExternalContext().getSessionMap().
         get(FileUploadBean.FILE_UPLOAD_BEAN_NAME);
      if (fileBean != null)
      {
         this.file = fileBean.getFile();
         this.fileName = fileBean.getFileName();
      }
      
      return this.fileName;
   }
   
   private void clearUpload()
   {
      // delete the temporary file we uploaded earlier
      if (this.file != null)
      {
         this.file.delete();
      }
      
      this.file = null;
      this.fileName = null;
      
      // remove the file upload bean from the session
      FacesContext ctx = FacesContext.getCurrentInstance();
      ctx.getExternalContext().getSessionMap().remove(FileUploadBean.FILE_UPLOAD_BEAN_NAME);
   }
   
   public String getFileUploadSuccessMsg()
   {
      String msg = Application.getMessage(FacesContext.getCurrentInstance(), "file_upload_success");
      return MessageFormat.format(msg, new Object[] {getFileName()});
   }
   
   public String cancel()
   {
      // reset the state
      clearUpload();
      
      return "adminConsole";
   }
   
   public String updateFileOK()
   {
      String outcome = null;
      
      UserTransaction tx = null;
      
      Node node = new Node(this.s2lowService.getS2lowActesClassificationNodeRef());
      
      if (node != null && this.getFileName() != null)
      {
         try
         {
            if (logger.isDebugEnabled())
               logger.debug("Trying to update content node Id: " + node.getId());
            
            tx = Repository.getUserTransaction(FacesContext.getCurrentInstance());
            tx.begin();

            ContentWriter writer = this.contentService.getWriter(node.getNodeRef(), ContentModel.PROP_CONTENT, true);
            writer.setMimetype(MimetypeMap.MIMETYPE_XML);
            writer.putContent(this.file);
            
            // commit the transaction
            tx.commit();
            
            // clear action context
            clearUpload();
            
            outcome = "adminConsole";
         }
         catch (Throwable err)
         {
            // rollback the transaction
            try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
            Utils.addErrorMessage(Application.getMessage(
                  FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC) + err.getMessage(), err);
         }
      }
      else
      {
         logger.warn("WARNING: updateFileOK called without a current Document!");
      }
      
      return outcome;
   }
   
   /**
    * @see org.alfresco.web.app.context.IContextListener#contextUpdated()
    */
   public void contextUpdated()
   {
      if (logger.isDebugEnabled())
         logger.debug("Invalidating \"AdminParapheur\" components...");
      
      if (this.parapheurRichList != null)
      {
         this.parapheurRichList.setValue(null);
      }
      this.parapheurs = null;
      
      if (this.dossierRichList != null)
      {
         this.dossierRichList.setValue(null);
      }
      this.dossiers = null;
   }
   
   // Property Resolvers
   
   public NodePropertyResolver resolverState = new NodePropertyResolver() {
      public Object get(Node node) {
         String res = "";
         NodeRef dossierRef = node.getNodeRef();
         if(parapheurService.isDossier(dossierRef))
         {
            if ((Boolean) nodeService.getProperty(dossierRef, ParapheurModel.PROP_TERMINE))
            {
               // Dossier terminé
               NodeRef corbeilleRef = parapheurService.getParentCorbeille(dossierRef); 
               if (ParapheurModel.NAME_RETOURNES.equals(nodeService.getPrimaryParent(corbeilleRef).getQName()))
                  res = "Refusé";
               else
                  res = "Approuvé";
            }
            else
            {
               if ((Boolean) parapheurService.isEmis(dossierRef) != true)
                  res = "En préparation";
               else
               {
                  // Dossier en cours
                  String currentUser = parapheurService.getActeurCourant(dossierRef);
                  if (currentUser != null & currentUser.length() > 0)
                  {
                     List<EtapeCircuit> circuit = parapheurService.getCircuit(dossierRef);
                     EtapeCircuit etapeCourante = null, etapeSuivante = null;
                     for (EtapeCircuit etape : circuit)
                     {
                        if (!etape.isApproved())
                        {
                           if (etapeCourante == null)
                           {
                              etapeCourante = etape;
                           }
                           else if (etapeSuivante == null)
                           {
                              etapeSuivante = etape;
                              break;
                           }
                        }
                     }
                     
                     if (etapeSuivante == null)
                        res = "A la signature";
                     else
                     {
                        res = "Acteur suivant : ";
                        res += parapheurService.getNomProprietaire(etapeSuivante.getParapheur());
                     }
                  }
                  else
                  {
                     res += "Aucun acteur courant";
                  }
               }
            }
            
         }
         return res;
      }
   };

	public void areaChanged()
	{
		// TODO Auto-generated method stub
		
	}

	public void spaceChanged()
	{
		// TODO Auto-generated method stub
		
	}
}
