/**
 * Copyright (C) 2009 eXo Platform SAS.
 * 
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 * 
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.exoplatform.webui.core;

import org.exoplatform.commons.serialization.api.annotations.Serialized;
import org.exoplatform.util.ReflectionUtil;
import org.exoplatform.webui.application.WebuiRequestContext;
import org.exoplatform.webui.config.annotation.ComponentConfig;
import org.exoplatform.webui.config.annotation.EventConfig;
import org.exoplatform.webui.event.Event;
import org.exoplatform.webui.event.EventListener;
import org.exoplatform.webui.form.UIForm;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;

/**
 * Author : Nhu Dinh Thuan
 *          nhudinhthuan@exoplatform.com
 * Jul 7, 2006
 * 
 * A component that represents a tree.
 * Can contain a UIRightClickPopupMenu
 * @see UIRightClickPopupMenu
 */
@ComponentConfig(template = "system:/groovy/webui/core/UITree.gtmpl", events = @EventConfig(listeners = UITree.ChangeNodeActionListener.class))
@Serialized
public class UITree extends UIComponent
{
   /**
    * The css class name to show the expand icon
    */
   private String expandIcon = "ExpandIcon";

   /**
    * The css class name to show the collapse icon
    */
   private String colapseIcon = "CollapseIcon";
   
   /**
    * The css class name to show the null icon (item has no child)
    */
   private String nullItemIcon = "NullItemIcon";

   /**
    * The css class name to show the selected icon
    */
   private String selectedIcon = "";

   private String icon = "";

   /**
    * The bean field that holds the id of the bean
    */
   private String beanIdField_;

   /**
    * The bean field that holds the count number of the children
    * This help to express the node have childs or not
    */
   private String beanChildCountField_;

   /**
    * The bean field that holds the label of the bean
    */
   private String beanLabelField_;

   /**
    * The bean field that holds the icon of the bean
    */
   private String beanIconField_ = "";

   /**
    * The field that holds max character's title of node
    */
   private Integer maxTitleCharacter_ = 0;

   /**
    * A list of sibling nodes
    */
   private Collection<?> sibbling;

   /**
    * A list of children nodes
    */
   private Collection<?> children;

   /**
    * The selected node
    */
   private Object selected;

   /**
    * The parent node of the selected node
    */
   private Object parentSelected;

   /**
    * A right click popup menu
    */
   private UIRightClickPopupMenu uiPopupMenu_;

   public Object getFieldValue(Object bean, String field) throws Exception
   {
      Method method = ReflectionUtil.getGetBindingMethod(bean, field);
      return method.invoke(bean, ReflectionUtil.EMPTY_ARGS);
   }

   public void setBeanIdField(String beanIdField_)
   {
      this.beanIdField_ = beanIdField_;
   }

   public void setBeanIconField(String beanIconField_)
   {
      this.beanIconField_ = beanIconField_;
   }

   public void setBeanLabelField(String beanLabelField_)
   {
      this.beanLabelField_ = beanLabelField_;
   }

   public void setBeanChildCountField(String beanChildCountField)
   {
      this.beanChildCountField_ = beanChildCountField;
   }

   public Object getId(Object object) throws Exception
   {
      return getFieldValue(object, beanIdField_);
   }

   public String getActionLink() throws Exception
   {
      if (selected == null)
         return "#";
      if (parentSelected == null)
         return event("ChangeNode");
      return event("ChangeNode", (String)getId(parentSelected));
   }

   //TODO review equals object with id
   public boolean isSelected(Object obj) throws Exception
   {
      if (selected == null)
         return false;
      return getId(obj).equals(getId(selected));
   }

   public String getColapseIcon()
   {
      return colapseIcon;
   }

   public void setCollapseIcon(String colapseIcon)
   {
      this.colapseIcon = colapseIcon;
   }

   public String getExpandIcon()
   {
      return expandIcon;
   }

   public void setExpandIcon(String expandIcon)
   {
      this.expandIcon = expandIcon;
   }

   public String getIcon()
   {
      return icon;
   }

   public void setIcon(String icon)
   {
      this.icon = icon;
   }

   public String getSelectedIcon()
   {
      return selectedIcon;
   }

   public void setSelectedIcon(String selectedIcon)
   {
      this.selectedIcon = selectedIcon;
   }

   public Collection<?> getChildren()
   {
      return children;
   }

   public void setChildren(Collection<?> children)
   {
      this.children = children;
   }

   @SuppressWarnings("unchecked")
   public <T> T getSelected()
   {
      return (T)selected;
   }

   public void setSelected(Object selectedObject)
   {
      this.selected = selectedObject;
   }

   @SuppressWarnings("unchecked")
   public <T> T getParentSelected()
   {
      return (T)parentSelected;
   }

   public void setParentSelected(Object parentSelected)
   {
      this.parentSelected = parentSelected;
   }

   public Collection<?> getSibbling()
   {
      return sibbling;
   }

   public void setSibbling(Collection<?> sibbling)
   {
      this.sibbling = sibbling;
   }

   public UIRightClickPopupMenu getUIRightClickPopupMenu()
   {
      return uiPopupMenu_;
   }

   public void setUIRightClickPopupMenu(UIRightClickPopupMenu uiPopupMenu)
   {
      uiPopupMenu_ = uiPopupMenu;
      if (uiPopupMenu_ != null)
         uiPopupMenu_.setParent(this);
   }

   public String event(String name, String beanId) throws Exception
   {
      UIForm uiForm = getAncestorOfType(UIForm.class);
      if (uiForm != null)
         return uiForm.event(name, getId(), beanId);
      return super.event(name, beanId);
   }

   public String renderNode(Object obj) throws Exception
   {
      String nodeIcon = expandIcon;
      String iconGroup = icon;

      String note = "";
      if (isSelected(obj))
      {
         nodeIcon = colapseIcon;
         iconGroup = selectedIcon;
         note = " NodeSelected";
      }

      if(getBeanChildCountField() != null) {
         Object childCount = getFieldValue(obj, getBeanChildCountField());         
         if (childCount != null && childCount.getClass().isAssignableFrom(Integer.class) &&
            (Integer)childCount == 0)
         {
            nodeIcon = nullItemIcon;  
         }
      }

      if (beanIconField_ != null && beanIconField_.length() > 0)
      {
         if (getFieldValue(obj, beanIconField_) != null)
            iconGroup = (String)getFieldValue(obj, beanIconField_);
      }
      String objId = String.valueOf(getId(obj));
      String actionLink = event("ChangeNode", objId);
      String fieldValue = (String)getFieldValue(obj, beanLabelField_);
      StringBuilder builder = new StringBuilder();

      // if field's length > max field's length then cut field value
      if ((fieldValue.length() > getMaxTitleCharacter()) && (getMaxTitleCharacter() != 0))
      {
         fieldValue = fieldValue.substring(0, getMaxTitleCharacter() - 3) + "...";
      }
      if (nodeIcon.equals(expandIcon))
      {
         builder.append(" <div class=\"").append(nodeIcon).append("\" onclick=\"").append(actionLink).append("\">");
      }
      else if (nodeIcon.equals(colapseIcon))
      {
         builder.append(" <div class=\"").append(nodeIcon).append(
            "\" onclick=\"eXo.portal.UIPortalControl.collapseTree(this)").append("\">");
      }
      else {//Null item
         builder.append(" <div class=\"").append(nodeIcon).append("\" onclick=\"").append(actionLink).append("\">");
      }
      if (uiPopupMenu_ == null)
      {
         builder.append(" <a href=\"javascript:void(0);\" class=\"NodeIcon ").append(iconGroup).append(note).append(
            "\"").append(" title=\"").append(getFieldValue(obj, beanLabelField_)).append("\"").append(">").append(
            fieldValue).append("</a>");
      }
      else
      {
         builder.append("<a href=\"javascript:void(0);\" class=\"NodeIcon ").append(iconGroup).append(note).append(
            "\" ").append(uiPopupMenu_.getJSOnclickShowPopup(objId, null)).append(" title=\"").append(
            getFieldValue(obj, beanLabelField_)).append("\"").append(">").append(fieldValue).append("</a>");
      }
      builder.append(" </div>");
      return builder.toString();
   }

   public void renderUIComponent(UIComponent uicomponent) throws Exception
   {
      uicomponent.processRender((WebuiRequestContext)WebuiRequestContext.getCurrentInstance());
   }

   @SuppressWarnings("unchecked")
   public <T extends UIComponent> T findComponentById(String id)
   {
      if (getId().equals(id))
         return (T)this;
      if (uiPopupMenu_ == null)
         return null;
      return (T)uiPopupMenu_.findComponentById(id);
   }

   public <T extends UIComponent> T findFirstComponentOfType(Class<T> type)
   {
      if (type.isInstance(this))
         return type.cast(this);
      if (uiPopupMenu_ == null)
         return null;
      return uiPopupMenu_.findFirstComponentOfType(type);
   }

   public <T> void findComponentOfType(List<T> list, Class<T> type)
   {
      if (type.isInstance(this))
         list.add(type.cast(this));
      if (uiPopupMenu_ == null)
         return;
      uiPopupMenu_.findComponentOfType(list, type);
   }

   static public class ChangeNodeActionListener extends EventListener<UITree>
   {
      public void execute(Event<UITree> event) throws Exception
      {
         event.getSource().<UIComponent> getParent().broadcast(event, event.getExecutionPhase());
      }
   }

   public String getBeanIdField()
   {
      return beanIdField_;
   }

   public String getBeanChildCountField()
   {
      return beanChildCountField_;
   }

   public String getBeanLabelField()
   {
      return beanLabelField_;
   }

   public String getBeanIconField()
   {
      return beanIconField_;
   }

   public void setMaxTitleCharacter(Integer maxTitleCharacter_)
   {
      this.maxTitleCharacter_ = maxTitleCharacter_;
   }

   public Integer getMaxTitleCharacter()
   {
      return maxTitleCharacter_;
   }

   public UIRightClickPopupMenu getUiPopupMenu()
   {
      return uiPopupMenu_;
   }

   public void setUiPopupMenu(UIRightClickPopupMenu uiPopupMenu)
   {
      this.uiPopupMenu_ = uiPopupMenu;
   }

   public void setColapseIcon(String colapseIcon)
   {
      this.colapseIcon = colapseIcon;
   }
}
