/*
 * 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.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.event.ActionEvent;

import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.PersonService;
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.NavigationBean;
import org.alfresco.web.bean.repository.MapNode;
import org.alfresco.web.bean.repository.Node;
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.UIColumn;
import org.alfresco.web.ui.common.component.data.UIRichList;
import org.alfresco.web.ui.common.renderer.data.IRichListRenderer;

import com.atolcd.parapheur.model.ParapheurModel;
import com.atolcd.parapheur.repo.EtapeCircuit;
import com.atolcd.parapheur.repo.ParapheurService;

public class DisplayParapheurDetails implements IContextListener
{
    /** NodeService bean reference */
    protected NodeService nodeService;
    
    /** The NavigationBean bean reference */
    protected NavigationBean navigator;
    
    /** The NavigationBean bean reference */
    protected BrowseBean browseBean;
    
    /** The PersonService bean reference */
    protected PersonService personService;
    
    /** The DictionaryService bean reference */
    protected DictionaryService dictionaryService;
    
    /** The ParapheurService bean reference*/
    protected ParapheurService parapheurService;    
    
    /** The SearchService bean reference. */
    protected SearchService searchService;
    
    /** Component reference for Users RichList control */
    protected UIRichList visaRichList;
    
    /** Component reference for Users RichList control */
    protected UIRichList visaAnnotationRichList;

    /** Component reference for Users RichList control */
    protected UIRichList notificationRichList;
    
    protected List<Node> visa;
    protected List<Node> visaAnnotation;
    protected List<Node> notification;
    protected NodeRef detailsNodeRef;
    
    
    public DisplayParapheurDetails(){
        UIContextService.getInstance(FacesContext.getCurrentInstance()).registerBean(this);
    }
    
    
   /**
    * @param nodeService The NodeService to set.
    */
   public void setNodeService(NodeService nodeService)
   {
      this.nodeService = nodeService;
   }
   
   /**
    * @param navigator The NavigationBean to set.
    */
   public void setNavigator(NavigationBean navigator)
   {
      this.navigator = navigator;
   }
   
   /**
    * @param browseBean The BrowseBean to set.
    */
   public void setBrowseBean(BrowseBean browseBean)
   {
      this.browseBean = browseBean;
   }

    /**
     * 
     * @param personService The PersonService to set.
     */
   public void setPersonService(PersonService personService)
    {
        this.personService = personService;
    }
   
   /**
    * @param dictionaryService The DictionaryService to set.
    */
   public void setDictionaryService(DictionaryService dictionaryService)
   {
      this.dictionaryService = dictionaryService;
   }
 
   /**
    * @return Returns the visaRichList.
    */
   public UIRichList getVisaRichList()
   {
      return this.visaRichList;
   }

   /**
    * @param _visaRichList  The visaRichList to set.
    */
   public void setVisaRichList(UIRichList _visaRichList)
   {
      this.visaRichList = _visaRichList;
   }
   
   /**
    * @return Returns the visaRichList.
    */
   public UIRichList getVisaAnnotationRichList()
   {
      return this.visaAnnotationRichList;
   }

   /**
    * @param _visaRichList  The visaRichList to set.
    */
   public void setVisaAnnotationRichList(UIRichList _visaAnnotationRichList)
   {
      this.visaAnnotationRichList = _visaAnnotationRichList;
   }
   
   /**
    * @return Returns the notificationRichList.
    */
   public UIRichList getNotificationRichList()
   {
      return this.notificationRichList;
   }

   /**
    * @param _stepsRichList  The notificationRichList to set.
    */
   public void setNotificationRichList(UIRichList _notificationRichList)
   {
      this.notificationRichList = _notificationRichList;
   }

   /**
    * 
    * @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;
   }
   
   public String getAnnotationPrivee()
   {
   	
   	NodeRef currentNodeRef = this.navigator.getCurrentNode().getNodeRef();
   	if (!this.parapheurService.isDossier(currentNodeRef))
   	{
   		if (this.detailsNodeRef != null)
   			currentNodeRef = this.detailsNodeRef;
   	}
   	
   	try
   	{
   	   String annotationPrivee = this.parapheurService.getAnnotationPriveePrecedente(currentNodeRef);
       
       if (annotationPrivee != null)
          return annotationPrivee;
       else
          return "";
   	}
   	catch(IllegalArgumentException e)
   	{
   		return "";
   	}
   }
   
   /**
    * @return the list of user Nodes to display
    */
   public List<Node> getVisa()
   {
      this.visa = new ArrayList<Node>();
      
      List<EtapeCircuit> circuit = this.parapheurService.getCircuit(navigator.getCurrentNode().getNodeRef());
      for (EtapeCircuit etape : circuit)
      {
         NodeRef paraphRef = etape.getParapheur();
         String paraphName = this.parapheurService.getNomParapheur(paraphRef), 
         fullName = this.parapheurService.getNomProprietaire(paraphRef);
         
         MapNode nodeEtape = new MapNode(paraphRef);
         nodeEtape.put("fullName", paraphName + " ("+fullName+")");
         this.visa.add(nodeEtape);
      }
      return this.visa;
   }
   
   /**
    * @return the list of user Nodes to display
    */
   public List<Node> getVisaAnnotation()
   {
      this.visaAnnotation = new ArrayList<Node>();
      
      NodeRef currentNodeRef = this.navigator.getCurrentNode().getNodeRef();
      if (!this.parapheurService.isDossier(currentNodeRef))
      {
         if (this.detailsNodeRef != null)
            if (this.parapheurService.isDossier(this.detailsNodeRef))
               currentNodeRef = this.detailsNodeRef;
            else
               return this.visaAnnotation;
         else
            return this.visaAnnotation;
      }
      
      List<EtapeCircuit> circuit = this.parapheurService.getCircuit(currentNodeRef);
      for (EtapeCircuit etape : circuit)
      {
         if (etape.isApproved())
         {
            NodeRef paraphRef = etape.getParapheur();
            String paraphName = this.parapheurService.getNomParapheur(paraphRef),
            fullName = this.parapheurService.getNomProprietaire(paraphRef),
            annotation = etape.getAnnotation();
            MapNode nodeEtape = new MapNode(paraphRef);
            nodeEtape.put("fullName", paraphName + " ("+ fullName + ")");
            if(annotation != null && annotation.length() != 0)
               nodeEtape.put("annotationPublique", annotation);
            else
               nodeEtape.put("annotationPublique", "Pas d'annotation");
            this.visaAnnotation.add(nodeEtape);
         }
         else
            break;
      }
      return this.visaAnnotation;
   }
   
   public String getAnnotationPubliqueCourante()
   {
      String res = "";
      NodeRef currentNodeRef = this.navigator.getCurrentNode().getNodeRef();
      if (!this.parapheurService.isDossier(currentNodeRef))
      {
         if (this.detailsNodeRef != null)
            if (this.parapheurService.isDossier(this.detailsNodeRef))
               currentNodeRef = this.detailsNodeRef;
            else
               return res;
         else
            return res;
      }
      
      try
      {
         res = this.parapheurService.getAnnotationPublique(currentNodeRef); 
      }
      catch (Exception e)
      {
         res = "";
      }
      return res;
   }
   
   public String getAnnotationPriveeCourante()
   {
      String res = "";
      NodeRef currentNodeRef = this.navigator.getCurrentNode().getNodeRef();
      if (!this.parapheurService.isDossier(currentNodeRef))
      {
         if (this.detailsNodeRef != null)
            if (this.parapheurService.isDossier(this.detailsNodeRef))
               currentNodeRef = this.detailsNodeRef;
            else
               return res;
         else
            return res;
      }
      
      try
      {
         res = this.parapheurService.getAnnotationPrivee(currentNodeRef); 
      }
      catch (Exception e)
      {
         res = "";
      }
      return res;
   }
   
   /**
    * @return the list of user Nodes to display
    */
   public List<Node> getNotification()
   {      
       this.notification = new ArrayList<Node>();
       
       // get the current space from NavigationBean
       String parentNodeId = this.navigator.getCurrentNodeId();
       
       NodeRef parentRef;
       if (parentNodeId == null)
       {
          // no specific parent node specified - use the root node
          parentRef = this.nodeService.getRootNode(Repository.getStoreRef());
       }
       else
       {
          // build a NodeRef for the specified Id and our store
          parentRef = new NodeRef(Repository.getStoreRef(), parentNodeId);
       }
       
       if (this.parapheurService.isDossier(parentRef))
       {
           Set<NodeRef> list = parapheurService.getDiffusion(parentRef);
           if (list != null)
           {
               String paraphName, fullName;
               MapNode node;
               Map<String, Object> props;
               
               for(NodeRef nodeParapheur : list)
               {
                   paraphName = (String)this.nodeService.getProperty(nodeParapheur,ContentModel.PROP_NAME);
                   fullName = this.parapheurService.getNomProprietaire(nodeParapheur);

                   node = new MapNode(nodeParapheur);
                   props = node.getProperties(); 
                   props.put("fullName", paraphName+"  ("+fullName+")");
                     
                   this.notification.add(node);
               }
           }

       }   
       
       return this.notification;
   }
   
   /**
    * 
    * @see org.alfresco.web.app.context.IContextListener#contextUpdated()
    */
    public void contextUpdated()
    {
        if (this.visaRichList != null)
        {
           this.visaRichList.setValue(null);
           this.visa = null;
        }
        
        if (this.visaAnnotationRichList != null)
        {
           this.visaAnnotationRichList.setValue(null);
           this.visaAnnotation = null;
        }
        
        if (this.notificationRichList != null)
        {
           this.notificationRichList.setValue(null);
           this.notification = null;
        }
    }
    
    /**
     * Teste si l'utilisateur en cours est l'émetteur
     * @return
     */
    public boolean getEmetteur()
    {
       NodeRef dossierRef = this.navigator.getCurrentNode().getNodeRef(),
               parapheurRef = this.parapheurService.getEmetteur(dossierRef);
       
       boolean isEmetteur = false;
       
       if (!this.parapheurService.isEmis(dossierRef))
       {
          String userName = this.parapheurService.getParapheurOwner(parapheurRef);
          if (userName != null && userName.equalsIgnoreCase(this.navigator.getCurrentUser().getUserName()))
             isEmetteur = true;
       }
       
       return isEmetteur;
    }
    
    public boolean getActeurCourantOuSecretaire()
    {
        NodeRef currentNodeRef = this.navigator.getCurrentNode().getNodeRef();
        if (!this.parapheurService.isDossier(currentNodeRef))
        {
            if (this.detailsNodeRef != null)
                currentNodeRef = this.detailsNodeRef;
        }
        
        boolean isActeurCourant;
        
        String username = Application.getCurrentUser(FacesContext.getCurrentInstance()).getUserName();
        
        if (!parapheurService.isTermine(currentNodeRef))
        {
           if (!nodeService.hasAspect(currentNodeRef, ParapheurModel.ASPECT_SECRETARIAT))
           {
               isActeurCourant = parapheurService.isActeurCourant(currentNodeRef, username);
           }
           else
           {
               isActeurCourant = parapheurService.isParapheurSecretaire(parapheurService.getOwnedParapheur(parapheurService.getActeurCourant(currentNodeRef)),username);
           }
        }
        else
        {
           isActeurCourant = parapheurService.getEmetteur(currentNodeRef).equals(parapheurService.getOwnedParapheur(username));
        }
        
        
        return isActeurCourant;
    }
    
    public boolean getActeurCourant()
    {
        NodeRef currentNodeRef = this.navigator.getCurrentNode().getNodeRef();
        if (!this.parapheurService.isDossier(currentNodeRef))
        {
            if (this.detailsNodeRef != null)
                currentNodeRef = this.detailsNodeRef;
        }
        
        boolean isActeurCourant;
        
        String username = Application.getCurrentUser(FacesContext.getCurrentInstance()).getUserName();
        
        if (!parapheurService.isTermine(currentNodeRef))
        {
           isActeurCourant = parapheurService.isActeurCourant(currentNodeRef, username);
        }
        else
        {
           isActeurCourant = parapheurService.getEmetteur(currentNodeRef).equals(parapheurService.getOwnedParapheur(username));
        }
        
        
        return isActeurCourant;
    }
    
    public void setupSpace(ActionEvent event)
    {
   	 UIActionLink link = (UIActionLink)event.getComponent();
       Map<String, String> params = link.getParameterMap();
       String id = params.get("id");
       detailsNodeRef = new NodeRef(Repository.getStoreRef(),id);
       this.browseBean.setupSpaceAction(event);
    }

    public void desinscription(ActionEvent event)
    {
       NodeRef dossier = this.browseBean.getActionSpace().getNodeRef();
       Set<NodeRef> setDiffusion = this.parapheurService.getDiffusion(dossier);
       NodeRef parapheurRef = this.parapheurService.getOwnedParapheur(this.navigator.getCurrentUser().getUserName());
       if (setDiffusion.contains(parapheurRef))
       {
          setDiffusion.remove(parapheurRef);
          this.parapheurService.setDiffusion(dossier, setDiffusion);
          this.browseBean.updateUILocation(dossier);
       }
    }
    
    public boolean isNotifie()
    {
       NodeRef dossier;
       if (this.browseBean.getActionSpace() != null)
          dossier = this.browseBean.getActionSpace().getNodeRef();
       else
          dossier = this.navigator.getCurrentNode().getNodeRef();
       
       Set<NodeRef> setDiffusion = this.parapheurService.getDiffusion(dossier);
       NodeRef parapheurRef = this.parapheurService.getOwnedParapheur(this.navigator.getCurrentUser().getUserName());
       if (setDiffusion.contains(parapheurRef))
          return true;
       else
          return false;
    }
    
    public static class AnnotationBubbleViewRenderer implements IRichListRenderer
    {
       public static final String VIEWMODEID = "annotation_bubble";
       
       /**
        * @see org.alfresco.web.ui.common.renderer.data.IRichListRenderer#getViewModeID()
        */
       public String getViewModeID()
       {
          return VIEWMODEID;
       }
       
       /**
        * @see org.alfresco.web.ui.common.renderer.data.IRichListRenderer#renderListBefore(javax.faces.context.FacesContext, org.alfresco.web.ui.common.component.data.UIColumn[])
        */
       public void renderListBefore(FacesContext context, UIRichList richList, UIColumn[] columns)
             throws IOException
       {
          // nothing to do
       }
       
       /**
        * @see org.alfresco.web.ui.common.renderer.data.IRichListRenderer#renderListRow(javax.faces.context.FacesContext, org.alfresco.web.ui.common.component.data.UIColumn[], java.lang.Object)
        */
       public void renderListRow(FacesContext context, UIRichList richList, UIColumn[] columns, Object row)
             throws IOException
       {
          ResponseWriter out = context.getResponseWriter();
          
          // find primary column (which must exist) and the actions column (which doesn't 
          // have to exist)
          UIColumn primaryColumn = null;
          UIColumn actionsColumn = null;
          for (int i = 0; i < columns.length; i++)
          {
             if (columns[i].isRendered())
             {
                if (columns[i].getPrimary())
                {
                   primaryColumn = columns[i];
                }
                else if (columns[i].getActions())
                {
                   actionsColumn = columns[i];
                }
             }
          }
          
          MapNode node = (MapNode)row;
          
          out.write("<tr>");
          
          String contextPath = context.getExternalContext().getRequestContextPath();
          String colour = "orange";
          
          out.write("<td><table border='0' cellpadding='0' cellspacing='0' width='100%'><tr>");
          out.write("<td valign='top'><img src='");
          out.write(contextPath);
          out.write("/images/icons/user_large.gif'/><br/>");
          out.write((String)node.get("fullName"));
          out.write("</td><td width='100%'>");
          
          renderBubbleTop(out, contextPath, colour, "#FCC75E");
          renderHeaderContents(context, out, primaryColumn, actionsColumn, columns); 
          renderBubbleMiddle(out, contextPath, colour);
          renderBodyContents(context, primaryColumn);
          renderBubbleBottom(out, contextPath, colour);
          
          out.write("</td><td><div style='width:32px;' /></td></table></td>");
          
          
          out.write("</tr>");
       
          // add a little padding
          out.write("<tr><td><div style='padding:3px'></div></td></tr>");
       }
       
       /**
        * @see org.alfresco.web.ui.common.renderer.data.IRichListRenderer#renderListAfter(javax.faces.context.FacesContext, org.alfresco.web.ui.common.component.data.UIColumn[])
        */
       @SuppressWarnings("unchecked")
       public void renderListAfter(FacesContext context, UIRichList richList, UIColumn[] columns)
             throws IOException
       {
          ResponseWriter out = context.getResponseWriter();
          
          out.write("<tr><td colspan='99' align='right'>");
          for (Iterator<UIComponent> i = richList.getChildren().iterator(); i.hasNext(); /**/)
          {
             // output all remaining child components that are not UIColumn
             UIComponent child = (UIComponent)i.next();
             if (child instanceof UIColumn == false)
             {
                Utils.encodeRecursive(context, child);
             }
          }
          out.write("</td></tr>");
       }
       
       /**
        * Renders the top part of the bubble i.e. before the header
        * 
        * @param out The writer to output to
        * @param contextPath Context path of the application
        * @param colour The colour of the bubble
        * @param titleBgColour Background colour of the header area
        */
       public static void renderBubbleTop(Writer out, String contextPath, 
             String colour, String titleBgColour) throws IOException
       {
          out.write("<table border='0' cellpadding='0' cellspacing='0' width='100%'><tr>");
          out.write("<td style='background: url(");
          out.write(contextPath);
          out.write("/images/parts/");
          out.write(colour);
          out.write("header_1.gif) no-repeat #FFFFFF;' width='24' height='24'></td>");
          out.write("<td style='background: url(");
          out.write(contextPath);
          out.write("/images/parts/");
          out.write(colour);
          out.write("header_2.gif) repeat-x ");
          out.write(titleBgColour);
          out.write("'>");
       }
       
       /**
        * Renders the middle part of the bubble i.e. after the header and before the body
        * 
        * @param out The writer to output to
        * @param contextPath Context path of the application
        * @param colour The colour of the bubble
        */
       public static void renderBubbleMiddle(Writer out, String contextPath, String colour) 
             throws IOException
       {
          out.write("</td>");
          out.write("<td style='background: url(");
          out.write(contextPath);
          out.write("/images/parts/");
          out.write(colour);
          out.write("header_3.gif) no-repeat #FFFFFF;' width='24' height='24'></td>");
          out.write("</tr><tr>");
          out.write("<td width='24' height='13'><img width='24' height='13' alt='' src='");
          out.write(contextPath);
          out.write("/images/parts/");
          out.write(colour);
          out.write("body_1.gif' /></td>");
          out.write("<td background='");
          out.write(contextPath);
          out.write("/images/parts/");
          out.write(colour);
          out.write("body_2.gif'><img width='21' height='13' alt='' src='");
          out.write(contextPath);
          out.write("/images/parts/");
          out.write(colour);
          out.write("body_2.gif' /></td>");
          out.write("<td width='24' height='13'><img width='24' height='13' alt='' src='");
          out.write(contextPath);
          out.write("/images/parts/");
          out.write(colour);
          out.write("body_3.gif' /></td>");
          out.write("</tr><tr>");
          out.write("<td width='24' height='13' background='");
          out.write(contextPath);
          out.write("/images/parts/");
          out.write(colour);
          out.write("body_4.gif'><img width='24' height='4' alt='' src='");
          out.write(contextPath);
          out.write("/images/parts/");
          out.write(colour);
          out.write("body_4.gif' /></td>");         
          out.write("<td>");
       }
       
       /**
        * Renders the bottom part of the bubble i.e. after the body
        * 
        * @param out The writer to output to
        * @param contextPath Context path of the application
        * @param colour The colour of the bubble
        */
       public static void renderBubbleBottom(Writer out, String contextPath, String colour)
             throws IOException
       {
          out.write("</td>");
          out.write("<td width='24' height='13' background='");
          out.write(contextPath);
          out.write("/images/parts/");
          out.write(colour);
          out.write("body_6.gif'><img width='24' height='3' alt='' src='");
          out.write(contextPath);
          out.write("/images/parts/");
          out.write(colour);
          out.write("body_6.gif' /></td>");
          out.write("</tr><tr>");
          out.write("<td width='24' height='13'><img width='24' height='13' alt='' src='");
          out.write(contextPath);
          out.write("/images/parts/");
          out.write(colour);
          out.write("body_7.gif' /></td>");
          out.write("<td background='");
          out.write(contextPath);
          out.write("/images/parts/");
          out.write(colour);
          out.write("body_8.gif'><img width='20' height='13' alt='' src='");
          out.write(contextPath);
          out.write("/images/parts/");
          out.write(colour);
          out.write("body_8.gif' /></td>");
          out.write("<td width='24' height='13'><img width='24' height='13' alt='' src='");
          out.write(contextPath);
          out.write("/images/parts/");
          out.write(colour);
          out.write("body_9.gif' /></td>");
          out.write("</tr></table>");
       }

       private void renderHeaderContents(FacesContext context, ResponseWriter out,  
             UIColumn primaryColumn, UIColumn actionsColumn, UIColumn[] columns) throws IOException
       {
          // render the header area with the configured columns
          out.write("<table width='100%' cellpadding='2' cellspacing='2' border='0'><tr>");
          
          for (int i = 0; i < columns.length; i++)
          {
             UIColumn column = columns[i];
             
             if (column.isRendered() == true &&
                 column.getPrimary() == false && 
                 column.getActions() == false)
             {
                // render the column header as the label
                UIComponent header = column.getFacet("header");
                if (header != null)
                {
                   out.write("<td><nobr><b>");
                   Utils.encodeRecursive(context, header);
                   out.write("</nobr></b></td>");
                }
                
                // render the contents of the column
                if (column.getChildCount() != 0)
                {
                   out.write("<td><nobr>");
                   Utils.encodeRecursive(context, column);
                   out.write("</nobr></td>");
                }
             }
          }
          
          // render the actions column
          out.write("<td align='right' width='100%'><nobr>");
          if (actionsColumn != null && actionsColumn.getChildCount() != 0)
          {
             Utils.encodeRecursive(context, actionsColumn);
          }
          out.write("</nobr></td></tr></table>");
       }
       
       /**
        * Renders the body contents for the bubble using the given primary coumn 
        * 
        * @param context Faces context
        * @param primaryColumn The primary column holding the message text
        */
       private void renderBodyContents(FacesContext context, UIColumn primaryColumn)
             throws IOException
       {
          // render the primary column
          if (primaryColumn != null && primaryColumn.getChildCount() != 0)
          {
             Utils.encodeRecursive(context, primaryColumn);
          }
       }
    }

	public void areaChanged()
	{
		// TODO Auto-generated method stub
		
	}


	public void spaceChanged()
	{
		// TODO Auto-generated method stub
		
	}
}


