/**
 * License Agreement.
 *
 *  JBoss RichFaces - Ajax4jsf Component Library
 *
 * Copyright (C) 2007  Exadel, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation.
 *
 * This library 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 library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 */

package org.richfaces.renderkit.html;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.faces.component.UIComponent;
import javax.faces.component.UIParameter;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;

import org.ajax4jsf.javascript.JSFunction;
import org.ajax4jsf.javascript.ScriptUtils;
import org.ajax4jsf.renderkit.AjaxRendererUtils;
import org.ajax4jsf.renderkit.RendererUtils.HTML;
import org.richfaces.component.UIPanelMenu;
import org.richfaces.component.UIPanelMenuGroup;
import org.richfaces.component.UIPanelMenuItem;
import org.richfaces.renderkit.PanelMenuRendererBase;

public class PanelMenuRenderer extends PanelMenuRendererBase {

	/* (non-Javadoc)
	 * @see org.ajax4jsf.framework.renderer.RendererBase#getComponentClass()
	 */
	
	protected Class getComponentClass() {
		return UIComponent.class;
	}
	
    // find and encode UIParameter's components
    public List encodeParams(FacesContext context, UIPanelMenuItem component) throws IOException {
    	
    	UIPanelMenuItem menuItem = component;
    	List params = new ArrayList();
    	StringBuffer buff = new StringBuffer();
    	
    	List children = menuItem.getChildren();
    	for (Iterator iterator = children.iterator(); iterator.hasNext();) {
    		UIComponent child = (UIComponent) iterator.next();
	
    		if(child instanceof UIParameter){
					
    			UIParameter param = (UIParameter)child;
				String name = param.getName();
				
				if (name != null) {
					Object value = param.getValue();
					buff.append("params[");
					buff.append(ScriptUtils.toScript(name));
					buff.append("] = ");
					buff.append(ScriptUtils.toScript(value));
					buff.append(";");
					params.add(buff.toString());
				}

			}
    	}
    	
    	return params;
    }
    
    @Override
    protected void preEncodeBegin(FacesContext context, UIComponent component) throws IOException {
        super.preEncodeBegin(context, component);

        // In case of encoding the UIPanelMenu in "expandSingle=true" mode
        // the value of "firstExpandedEncoded" attribute should be reset to
        // initial "false" state 
	if (component instanceof UIPanelMenu) {
	    UIPanelMenu panelMenu = (UIPanelMenu) component;
	    if (panelMenu.isExpandSingle()) {
		panelMenu.getAttributes().put("firstExpandedEncoded", false);
	    }
	}
    }

	
	public void insertScript(FacesContext context, UIComponent component)
			throws IOException {
		
		StringBuffer buffer		= new StringBuffer();
		StringBuffer panelMenu	= new StringBuffer();
		List flatList		 	= new LinkedList();
		Map levels			 	= new HashMap();
		
		Set itemNames = new HashSet();
		
		UIPanelMenu parentMenu = (UIPanelMenu)component;
		
		boolean expandSingle = parentMenu.isExpandSingle();
		
		String selectedChild = parentMenu.getSelectedName();
		
		flatten(component.getChildren(), flatList, levels, 0);
		
		panelMenu.append("var ids = new PanelMenu('")
					.append(component.getClientId(context).toString())
					.append("',")
					.append(new Boolean(expandSingle).toString())
					.append(",").append("'").append(selectedChild).append("'")
					.append(").getIds();\n");
		
		for (Iterator iter = flatList.iterator(); iter.hasNext();) {
			UIComponent child = (UIComponent) iter.next();
			if ((child instanceof UIPanelMenuItem)||(child instanceof UIPanelMenuGroup)) {
				boolean childDisabled;
				if (!((UIPanelMenu)component).isDisabled())
					childDisabled = child instanceof UIPanelMenuGroup ? ((UIPanelMenuGroup)child).isDisabled() : ((UIPanelMenuItem)child).isDisabled();
				else
					childDisabled = true;
				boolean childRendered = child instanceof UIPanelMenuGroup ? ((UIPanelMenuGroup)child).isRendered() : ((UIPanelMenuItem)child).isRendered();
				boolean parentRendered = true;
				if (! (child.getParent() instanceof UIPanelMenu))
					parentRendered = child.getParent() instanceof UIPanelMenuGroup ? ((UIPanelMenuGroup)child.getParent()).isRendered() : ((UIPanelMenuItem)child.getParent()).isRendered();
				if (!parentRendered){
					child.getAttributes().put("rendered",Boolean.FALSE);
				}
				
				//UIPanelMenu parentMenu = findMenu(child);
				
				String event = parentMenu.getEvent();
				if ("".equals(event))
					event = "click";
				else if (event.startsWith("on"))
					event = event.substring(2);
				
				String onopen = (child instanceof UIPanelMenuGroup) &&  !((UIPanelMenuGroup)child).isDisabled() && !isParentDisabled(child) ? 
						parentMenu.getOngroupexpand() + ";" + ((UIPanelMenuGroup)child).getOnexpand() : "";
				String onclose = (child instanceof UIPanelMenuGroup) && !((UIPanelMenuGroup)child).isDisabled() && !isParentDisabled(child) ? 
						parentMenu.getOngroupcollapse() + ";" + ((UIPanelMenuGroup)child).getOncollapse() : "";
				String hoveredStyle = (child instanceof UIPanelMenuGroup ? 
						parentMenu.getHoveredGroupStyle() : parentMenu.getHoveredItemStyle())
						+ ";" + (child instanceof UIPanelMenuGroup ? 
								((UIPanelMenuGroup)child).getHoverStyle() : ((UIPanelMenuItem)child).getHoverStyle());
				String hoveredClass = (child instanceof UIPanelMenuGroup ? 
						parentMenu.getHoveredGroupClass() : parentMenu.getHoveredItemClass())
						+ " " + (child instanceof UIPanelMenuGroup ? 
								((UIPanelMenuGroup)child).getHoverClass() : ((UIPanelMenuItem)child).getHoverClass());
				String [] hoveredStyles = hoveredStyle.split(";");
				String [] hoveredClasses = hoveredClass.split(" ");
				
				String mode = getItemMode(child);
				Object target = child.getAttributes().get("target");
				String targetString;
				if (null == target)
					targetString = "";
				else 
					targetString = target.toString();
				
				
				
				
				if (childRendered && parentRendered){
					if ( !isParentDisabled(child) ){
						String childName;
						if(child instanceof UIPanelMenuGroup){
							childName = ((UIPanelMenuGroup)child).getName();
						} else {
							childName = ((UIPanelMenuItem)child).getName();
						}
						
						if(itemNames.contains(childName)){
							throw new RuntimeException("Attibute \"name\" with value \"" + childName + "\" is already used in PanelMenu. It must be unique for every group/item.");
						} else {
							itemNames.add(childName);
						}
						
						buffer.append("var params = new Object();");

						if(child instanceof UIPanelMenuItem){
							List paramsList = encodeParams(context, (UIPanelMenuItem)child);
							if(!paramsList.isEmpty()){
								for (Iterator iterator = paramsList.iterator(); iterator.hasNext();) {
									buffer.append((String)iterator.next());
								}
							}
						}
						
						buffer
								.append("new PanelMenuItem(ids, params,{myId:'")
								.append((String) child.getClientId(context))
								.append("',parentId:'")
								.append((String) child.getParent().getClientId(context))
								.append("'},{type:" + (child instanceof UIPanelMenuItem ? "\"item\"":"\"node\""))
								.append(",onopen:"+("".equals(onopen) ? "\"\"" : "\"" + onopen + "\"")+",onclose:"+("".equals(onclose) ? "\"\"" : "\"" + onclose + "\""))
								.append(",event:\"" + event + "\"")
								.append(",mode:\"" + mode + "\"")
								.append(",target:\"" + targetString + "\"")
								.append(",disabled:" + new Boolean(childDisabled).toString())
								.append(",target:\"" + targetString + "\"")		
								.append(",name:\"" + childName + "\"")
								.append("},{");
							
						for (int i = 0; i < hoveredStyles.length; i++)
							if (!"".equals(hoveredStyles[i])) {
								String [] temp = hoveredStyles[i].split(":");
								String cssName = temp[0].trim();
								String cssValue = temp[1].trim();
								buffer.append("\"" + cssName + "\": \"" + cssValue + "\"");
								if (i != hoveredStyles.length - 1)
									buffer.append(",");
							}
						
						buffer.append("},");
						if (hoveredClasses.length > 0) {
							buffer.append("new Array(");
							for (int i = 0; i < hoveredClasses.length; i++)
								if (!"".equals(hoveredClasses[i])) {;
									buffer.append("\"" + hoveredClasses[i] + "\"");
									if (i != hoveredClasses.length - 1)
										buffer.append(",");
								}
							buffer.append("),");
						} else
							buffer.append("new Array(),");
	
						buffer.append(levels.get(child.getClientId(context)));
						switchOnImagesIfNeeded(context,child,buffer);
						
						addActionIfNeeded(context,child,buffer);
						
						setExpandedIfNeeded(context,child,buffer);
						
						addAjaxFunction(context,child,buffer);
						
						addOnItemHover(parentMenu.getOnitemhover(), child, buffer);
						
						String iconPos = "left";
						boolean isTopLevel = isTopLevel(child);
						if(child instanceof UIPanelMenuGroup){
							iconPos = isTopLevel ? parentMenu.getIconGroupTopPosition() : parentMenu.getIconGroupPosition();
						} else {
							iconPos = isTopLevel ? parentMenu.getIconItemTopPosition() : parentMenu.getIconItemPosition();
						}
						
						buffer.append(","+'"'+iconPos+'"');
						
						addImages(buffer,context,child,component.getClientId(context).toString());
						
						buffer.append(");\n");
					}
				} else {
					continue;
				}
			}
		}
		
		ResponseWriter writer = context.getResponseWriter();
		writer.startElement(HTML.SCRIPT_ELEM, component);
		writer.writeAttribute(HTML.id_ATTRIBUTE, "script" + component.getClientId(context), null);
		writer.writeAttribute(HTML.TYPE_ATTR, "text/javascript", null);
		writer.writeText(panelMenu, null);
		writer.writeText(buffer, null);
		writer.endElement(HTML.SCRIPT_ELEM);
	}
	
	public void flatten(List children, List flatList, Map levels,int initialLevel) {
		FacesContext context = FacesContext.getCurrentInstance();
		if (children != null) {
			for (Iterator iter = children.iterator(); iter.hasNext();) {
				UIComponent child = (UIComponent) iter.next();
				if (child instanceof UIPanelMenu){
					continue;
				}
				flatList.add(child);
				levels.put(child.getClientId(context), new Integer(initialLevel));
				flatten(child.getChildren(), flatList, levels, initialLevel + 1);
			}
		}
	}
	
	private void switchOnImagesIfNeeded(FacesContext context, UIComponent child, StringBuffer buffer)throws IOException {
		boolean isToplevel			= isTopLevel(child);
		String customIconOpened		= "";
		String customIconClosed		= "";

		UIPanelMenu panelMenu = findMenu(child);
		if(panelMenu == null){
			return;
		}
		String iconOpened			= isToplevel ? panelMenu.getIconExpandedTopGroup() : panelMenu.getIconExpandedGroup();
		String iconClosed			= isToplevel ? panelMenu.getIconCollapsedTopGroup() : panelMenu.getIconCollapsedGroup();

		try {
			customIconOpened = (String)child.getAttributes().get("iconOpened");
			customIconClosed = (String)child.getAttributes().get("iconClosed");
		} catch (Exception e) {}
		
		if (child instanceof UIPanelMenuItem){
			buffer.append(",false");
		} else {
			if (iconClosed.equals("custom")&&iconOpened.equals("custom")){
				if (customIconClosed.equals("")&&customIconOpened.equals("")){
					buffer.append(",false");
				} else {
					buffer.append(",true");
				}
			} else {
				buffer.append(",true");
			}
		}
	}
	
	private void addActionIfNeeded(FacesContext context,UIComponent child,StringBuffer buffer){
		//TODO by nick - dima - use CommandScriptBuilder
		if (child instanceof UIPanelMenuItem){
			if (((UIPanelMenuItem)child).getAction() == null){
				buffer.append(",false");	
			} else {
				buffer.append(",true");
			}
		} else {
			if (((UIPanelMenuGroup)child).getAction()!=null&&!((UIPanelMenuGroup)child).getAction().equals("")){
				buffer.append(",'panelMenuNodeAction'");
			} else {
				buffer.append(",false");
			}
		}
	}
	
	private void setExpandedIfNeeded(FacesContext context,UIComponent child,StringBuffer buffer){
		if(child instanceof UIPanelMenuItem){
			buffer.append(",false");
		} else {
			UIPanelMenuGroup group = (UIPanelMenuGroup)child;
			if(group.getValue() != null){
				buffer.append(",").append(group.getValue().toString());
			} else {
				PanelMenuGroupRenderer r =  (PanelMenuGroupRenderer)context.getRenderKit().getRenderer(group.getFamily(), group.getRendererType());
				boolean isNodeOpened;
				try {
					isNodeOpened = r.isOpened(context, child);
				} catch (IOException e) {
					isNodeOpened = false;
				}
				buffer.append(",").append(String.valueOf(isNodeOpened));
			}
		}
	}
	
	private void addImages(StringBuffer buffer,FacesContext context,UIComponent component,String id){
		UIPanelMenu panelMenu = findMenu(component);
		if(panelMenu == null){
			return;
		}
		boolean isTopLevel = isTopLevel(component);
		
		final String PANEL_MENU_SPACER_ICON = getIconByType(PANEL_MENU_SPACER_ICON_NAME, isTopLevel,context, component);

		if(component instanceof UIPanelMenuItem){
			UIPanelMenuItem item = (UIPanelMenuItem)component;
			
			String defaultItemIcon = null;
			String defaultItemIconSrc = null;
			String customItemIcon = null;
			String customIconSource = null;
			
			if(isTopLevel){
				if(item.isDisabled()){
					defaultItemIcon = panelMenu.getIconTopDisabledItem();
				} else {
					defaultItemIcon = panelMenu.getIconTopItem();
				}
				if(defaultItemIcon == null || defaultItemIcon.equals("")){
					if(item.isDisabled()){
						defaultItemIcon = panelMenu.getIconDisabledItem();
					} else {
						defaultItemIcon = panelMenu.getIconItem();
					}
				}
			} else {
				//isTopLevel == false
				if(defaultItemIcon == null || defaultItemIcon.equals("")){
					if(item.isDisabled()){
						defaultItemIcon = panelMenu.getIconDisabledItem();
					} else {
						defaultItemIcon = panelMenu.getIconItem();
					}
				}
			}
			
			if(defaultItemIcon != null && defaultItemIcon.equals("none")){
				defaultItemIconSrc = PANEL_MENU_SPACER_ICON;
			} else {
				defaultItemIconSrc = getIconByType(defaultItemIcon, isTopLevel,context, component);
			}
			
			customItemIcon = item.isDisabled() ? item.getIconDisabled() : item.getIcon();
			if(customItemIcon != null && customItemIcon.equals("none")){
				customIconSource = PANEL_MENU_SPACER_ICON;
			} else {
				customIconSource = getIconByType(customItemIcon, isTopLevel,context, component);
			}
			
			if(customItemIcon != null && !customItemIcon.equals("")){
				buffer.append(","+'"'+customIconSource+'"').append(","+'"'+customIconSource+'"'+" ");
			} else if (defaultItemIcon != null && !defaultItemIcon.equals("")){
				buffer.append(","+'"'+defaultItemIconSrc+'"').append(","+'"'+defaultItemIconSrc+'"'+" ");
			} else {
				buffer.append(","+'"'+PANEL_MENU_SPACER_ICON+'"').append(","+'"'+PANEL_MENU_SPACER_ICON+'"'+" ");
			}
			buffer.append(",\"\" ");
			
		} else if(component instanceof UIPanelMenuGroup){
			UIPanelMenuGroup group = (UIPanelMenuGroup)component;

			String defaultIconNodeClosed = isTopLevel ? (group.isDisabled() ? panelMenu.getIconTopDisableGroup() : panelMenu.getIconCollapsedTopGroup()) : (group.isDisabled() ? panelMenu.getIconDisabledGroup() : panelMenu.getIconCollapsedGroup());
			
			if(isTopLevel){
				if(group.isDisabled()){
					defaultIconNodeClosed = panelMenu.getIconTopDisableGroup();
					if(defaultIconNodeClosed == null || defaultIconNodeClosed.equals("")){
						defaultIconNodeClosed = panelMenu.getIconDisabledGroup();
					}
				} else {
					defaultIconNodeClosed = panelMenu.getIconCollapsedTopGroup();
					if(defaultIconNodeClosed == null || defaultIconNodeClosed.equals("")){
						defaultIconNodeClosed = panelMenu.getIconCollapsedGroup();
					}
				}
			} else {
				if(group.isDisabled()){
					defaultIconNodeClosed = panelMenu.getIconDisabledGroup();
				} else {
					defaultIconNodeClosed = panelMenu.getIconCollapsedGroup();
				}
			}
			
			String defaultIconNodeOpened = isTopLevel ? panelMenu.getIconExpandedTopGroup() : panelMenu.getIconExpandedGroup();
			
			if(isTopLevel){
				defaultIconNodeOpened = panelMenu.getIconExpandedTopGroup();
				if(defaultIconNodeOpened == null || defaultIconNodeOpened.equals("")){
					defaultIconNodeOpened = panelMenu.getIconExpandedGroup();
				}
			} else {
				defaultIconNodeOpened = panelMenu.getIconExpandedGroup();
			}

			String defaultIconNodeClosedSrc	= getIconByType(defaultIconNodeClosed, isTopLevel,context, component);
			String defaultIconNodeOpenedSrc	= getIconByType(defaultIconNodeOpened, isTopLevel,context, component);

			String iconExpanded = "";
			String iconCollapsed = "";
			String iconExpandedSource = "";
			String iconCollapsedSource = "";

			iconExpanded = group.isDisabled() ? group.getIconDisabled() : group.getIconExpanded();
			iconCollapsed = group.isDisabled() ? group.getIconDisabled() : group.getIconCollapsed();
			iconExpandedSource = getIconByType(iconExpanded,isTopLevel,context,component);
			iconCollapsedSource = getIconByType(iconCollapsed,isTopLevel,context,component);
			
			if(iconExpanded != null && !iconExpanded.equals("")){
				buffer.append(","+'"'+ iconExpandedSource + '"');
			} else if(defaultIconNodeOpened != null && !defaultIconNodeOpened.equals("")){
				buffer.append(","+'"'+defaultIconNodeOpenedSrc + '"');
			} else {
				buffer.append(","+'"'+PANEL_MENU_SPACER_ICON + '"');
			}
			
			if(iconCollapsed != null && !iconCollapsed.equals("")){
				buffer.append(","+'"'+iconCollapsedSource+'"');	
			} else if(defaultIconNodeClosed != null && !defaultIconNodeClosed.equals("")){
				buffer.append(","+'"'+defaultIconNodeClosedSrc+'"');
			} else {
				buffer.append(","+'"'+PANEL_MENU_SPACER_ICON + '"');
			}
		}
		buffer.append(",\"" + PANEL_MENU_SPACER_ICON + "\"");
	}
			
	protected void addAjaxFunction(FacesContext context, UIComponent child, StringBuffer buffer) {
		JSFunction function = AjaxRendererUtils.buildAjaxFunction(child,
                context);
        Map eventOptions = AjaxRendererUtils.buildEventOptions(context,
        		child);
        function.addParameter(eventOptions);
        
        buffer.append(",\"");
        function.appendScript(buffer);
        buffer.append("\"");
	}
	
	protected void addOnItemHover(String menuOnItemHover, UIComponent child, StringBuffer buffer) {
        buffer.append(",\"");
		if(child instanceof UIPanelMenuItem){
			if(menuOnItemHover != null && !menuOnItemHover.equals("")) buffer.append(menuOnItemHover);
		}
		buffer.append("\"");
	}
	
	public void renderChildren(FacesContext facesContext, UIComponent component)throws IOException {

		if(component instanceof UIPanelMenu){
			UIPanelMenu panelMenu = (UIPanelMenu)component;
			if(panelMenu.getChildCount() > 0){
				for (Iterator it = component.getChildren().iterator(); it.hasNext();) {
					UIComponent child = (UIComponent) it.next();
					if(child instanceof UIPanelMenuGroup) {
						UIPanelMenuGroup group = (UIPanelMenuGroup)child;
		
						if(panelMenu.isExpandSingle()) {	
							if(!(Boolean)panelMenu.getAttributes().get("firstExpandedEncoded")) {
								if(group.isExpanded()) {
									panelMenu.getAttributes().put("firstExpandedEncoded", true);
								}
							}else {
								group.setExpanded(false);
								if((Boolean)group.getValue()){
									group.setValue(null);
								}
							}
						}						
					}
					renderChild(facesContext, child);
				}
			}
		}
	}
	
	public void doDecode(FacesContext context, UIComponent component) {
		Map requestMap = context.getExternalContext().getRequestParameterMap();
		String menuClientId = component.getClientId(context);
		UIPanelMenu menu = ((UIPanelMenu)component);
		Object selectedItemName = requestMap.get(menuClientId + "selectedItemName");
		if(selectedItemName != null){
			menu.setSubmittedValue(selectedItemName);
		}
	}
	
}
