package org.richfaces.renderkit.html;
		
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

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

import org.ajax4jsf.context.AjaxContext;
import org.ajax4jsf.event.AjaxEvent;
import org.ajax4jsf.javascript.JSFunction;
import org.ajax4jsf.javascript.JSFunctionDefinition;
import org.ajax4jsf.javascript.JSReference;
import org.ajax4jsf.javascript.ScriptUtils;
import org.ajax4jsf.renderkit.AjaxComponentRendererBase;
import org.ajax4jsf.renderkit.AjaxRendererUtils;
import org.ajax4jsf.resource.InternetResource;
import org.richfaces.component.UIToolTip;
import org.richfaces.skin.Skin;

//import com.gargoylesoftware.htmlunit.protocol.javascript.JavaScriptURLConnection;
//import com.sun.el.lang.FunctionMapperImpl.Function;

public class ToolTipRenderer extends AjaxComponentRendererBase{

	// 
	// Declarations
	//
	private final InternetResource[] styles = {
						getResource("/org/richfaces/renderkit/html/css/tooltip.xcss")
	};

private InternetResource[] stylesAll = null;

protected InternetResource[] getStyles() {
	synchronized (this) {
		if (stylesAll == null) {
			InternetResource[] rsrcs = super.getStyles();
			boolean ignoreSuper = rsrcs == null || rsrcs.length == 0;
			boolean ignoreThis = styles == null || styles.length == 0;
			
			if (ignoreSuper) {
				if (ignoreThis) {
					stylesAll = new InternetResource[0];	
				} else {
					stylesAll = styles;
				}
			} else {
				if (ignoreThis) {
					stylesAll = rsrcs;
				} else {
					java.util.Set rsrcsSet = new java.util.LinkedHashSet();

					for (int i = 0; i < rsrcs.length; i++ ) {
						rsrcsSet.add(rsrcs[i]);
					}

					for (int i = 0; i < styles.length; i++ ) {
						rsrcsSet.add(styles[i]);
					}

					stylesAll = (InternetResource[]) rsrcsSet.toArray(new InternetResource[rsrcsSet.size()]);
				}
			}
		}
	}
	
	return stylesAll;
}
	private final InternetResource[] scripts = {
						new org.ajax4jsf.javascript.PrototypeScript()
						,
				new org.ajax4jsf.javascript.AjaxScript()
						,
				getResource("/org/richfaces/renderkit/html/scripts/utils.js")
						,
				getResource("/org/richfaces/renderkit/html/scripts/tooltip.js")
	};

private InternetResource[] scriptsAll = null;

protected InternetResource[] getScripts() {
	synchronized (this) {
		if (scriptsAll == null) {
			InternetResource[] rsrcs = super.getScripts();
			boolean ignoreSuper = rsrcs == null || rsrcs.length == 0;
			boolean ignoreThis = scripts == null || scripts.length == 0;
			
			if (ignoreSuper) {
				if (ignoreThis) {
					scriptsAll = new InternetResource[0];	
				} else {
					scriptsAll = scripts;
				}
			} else {
				if (ignoreThis) {
					scriptsAll = rsrcs;
				} else {
					java.util.Set rsrcsSet = new java.util.LinkedHashSet();

					for (int i = 0; i < rsrcs.length; i++ ) {
						rsrcsSet.add(rsrcs[i]);
					}

					for (int i = 0; i < scripts.length; i++ ) {
						rsrcsSet.add(scripts[i]);
					}

					scriptsAll = (InternetResource[]) rsrcsSet.toArray(new InternetResource[rsrcsSet.size()]);
				}
			}
		}
	}
	
	return scriptsAll;
}
	
	protected Class getComponentClass() {
		return org.richfaces.component.UIToolTip.class;
	}

	public String getBgColor(FacesContext context, UIComponent component){
		Skin skin = getSkin(context);
		String color = (String) skin.getParameter(context,"headerBackgroundColor");
		return color;
	}

	public String getColor(FacesContext context, UIComponent component){
		Skin skin = getSkin(context);
		String color = (String) skin.getParameter(context,"headerTextColor");
		return color;
	}
	
	
	public Map buildEventOptions(FacesContext context, UIComponent component){
        Map eventOptions = AjaxRendererUtils.buildEventOptions(context,	component);
        
        //after element is subsituted in DOM tree, we have to re-create
        //it's JS-reference, cause old one is already invalid
        String refreshTooltipInDOM = ";" + getJsVarName(context, component) + ".toolTip = $('" + component.getClientId(context) + "');" +
        	";" + getJsVarName(context, component) + ".toolTipContent = $('" + component.getClientId(context) + "content');";
        
        String showNewToolTip = ";" + getJsVarName(context, component) + ".displayDiv();";
        
        // before element will be substituted in DOM tree, we need to hide toolTipe to avoid blinking
        String fireBeforeUpdateDOM = ";" + getJsVarName(context, component) + ".toolTip.style.display = 'none';";
        
       
        //enable ajaxSingle mode, i.e. we do not need to submit all form controls to get tooltip content
        eventOptions.put("control", JSReference.THIS);
        
        if(eventOptions.containsKey("oncomplete")){
        	JSFunctionDefinition onComplete = (JSFunctionDefinition)eventOptions.get("oncomplete");
        	onComplete.addToBody(refreshTooltipInDOM + showNewToolTip);
        	eventOptions.put("oncomplete", onComplete);
        } else {
			JSFunctionDefinition onComplete = new JSFunctionDefinition();
			onComplete.addParameter("request");
			onComplete.addParameter("event");
			onComplete.addParameter("data");
			onComplete.addToBody(refreshTooltipInDOM + showNewToolTip);
			eventOptions.put("oncomplete", onComplete);
        }

        if(eventOptions.containsKey(AjaxRendererUtils.ONBEFOREDOMUPDATE_ATTR_NAME)){
        	JSFunctionDefinition beforeUpdate = (JSFunctionDefinition)eventOptions.get(AjaxRendererUtils.ONBEFOREDOMUPDATE_ATTR_NAME);
        	beforeUpdate.addToBody(fireBeforeUpdateDOM);
        	eventOptions.put(AjaxRendererUtils.ONBEFOREDOMUPDATE_ATTR_NAME, beforeUpdate);
        } else {
			JSFunctionDefinition beforeUpdate = new JSFunctionDefinition();
			beforeUpdate.addParameter("request");
			beforeUpdate.addParameter("event");
			beforeUpdate.addParameter("data");
			beforeUpdate.addToBody(fireBeforeUpdateDOM);
			eventOptions.put(AjaxRendererUtils.ONBEFOREDOMUPDATE_ATTR_NAME, beforeUpdate);
        }
        
        
        
        return eventOptions;
	}
	
	public void insertScript(FacesContext context, UIComponent component) throws IOException{
		
		UIToolTip toolTip = (UIToolTip)component;

		StringBuffer ret = new StringBuffer();
		ret.append("<script ");
		ret.append("type=\"text/javascript\" ");
		ret.append("id =\"script" + component.getClientId(context)+"\">\n");
		StringBuffer script = new StringBuffer();
		//
		String jsVar = constructJSVariable(context, component);
		
		script.append(jsVar).append(";\n");
		
		ret.append(script.toString());
		
		ret.append("\n</script>");
		ResponseWriter writer 	= context.getResponseWriter();
		writer.write(ret.toString());
		
	}

	public String constructJSVariable(FacesContext context, UIComponent component) {
		UIToolTip toolTip = (UIToolTip)component;
		String parentClientId = toolTip.getParent().getClientId(context);
		StringBuffer ret = new StringBuffer();
		String comma = ",";
		String quot = "\"";
		String event = toolTip.getEvent();
		if(event.startsWith("on")){
			event = event.substring(2);
		}
		
		Map eventsMap = new HashMap();
		eventsMap.put(new JSReference("event"), event);
		eventsMap.put(new JSReference("onshow"), toolTip.getOnshow());
		eventsMap.put(new JSReference("oncomplete"), toolTip.getOncomplete());
		eventsMap.put(new JSReference("onhide"), toolTip.getOnhide());
		eventsMap.put(new JSReference("delay"), new Integer(toolTip.getDelay()));
		
		JSFunction function = AjaxRendererUtils.buildAjaxFunction(component, context);
		JSReference ref = new JSReference("ajaxOptions");
		function.addParameter(ref);
		String ajaxFunc = function.toScript();
		
		Map ajaxOptions = buildEventOptions(context, toolTip);
		
		ret.append("new ToolTip(" + ScriptUtils.toScript(eventsMap)).append(comma).append(quot).append( toolTip.getClientId(context)).append(quot).append(comma).
		append(quot).append(parentClientId).append(quot).append(comma).
		append(quot).append(toolTip.getMode()).append(quot).append(comma).
		append(toolTip.isDisabled()).append(comma).
		append(quot).append(toolTip.getDirection()).append(quot).append(comma).
		append(toolTip.isFollowMouse()).append(comma).
		append(toolTip.getHorizontalOffset()).append(comma).
		append(toolTip.getVerticalOffset()).append(comma).
		append("\"").append(ajaxFunc).append("\"").append(comma).append(ScriptUtils.toScript(ajaxOptions)).append(");");

		return ret.toString();
	}
	
	
	private String getJsVarName(FacesContext context, UIComponent component){
		return "Richfaces.tooltips[\'" + component.getParent().getClientId(context) + "\']";
	}
	
	protected void doDecode(FacesContext context, UIComponent component) {

		UIToolTip tooltip = (UIToolTip)component;
		
		String clientId = tooltip.getClientId(context);
		
		if(context.getExternalContext().getRequestParameterMap().containsKey(clientId)){
			if("ajax".equals(tooltip.getMode())){
				new AjaxEvent(component).queue();
			}
		}
	}

	public void encodeTooltipText(FacesContext context, UIToolTip component) throws IOException {
		 ResponseWriter responseWriter = context.getResponseWriter();
		 responseWriter.startElement(component.getUsedElementType(), component);
		 responseWriter.writeAttribute("id", component.getClientId(context) + "content", null);
		 if("ajax".equals(component.getMode())){
			 // we want to avoid rendering toolTip content during initialy page displaying
			 AjaxContext ajaxContext = AjaxContext.getCurrentInstance();
			 if(ajaxContext != null && ajaxContext.getAjaxAreasToRender().contains(component.getClientId(context) + "content")){
		 		 responseWriter.write(component.getValue() != null ? component.getValue() : "");
				 super.renderChildren(context, component);
			 }
		 } else {
			 // client mode
	 		 responseWriter.write(component.getValue() != null ? component.getValue() : "");
			 super.renderChildren(context, component);
		 }
		 responseWriter.endElement(component.getUsedElementType());
	}

	
	protected void doEncodeBegin(ResponseWriter writer, FacesContext context,
			UIComponent component) throws IOException {

		UIToolTip toolTip = (UIToolTip)component;
		Class rendererClass = null;
		try{
			if("block".equals(toolTip.getLayout())){
				rendererClass = Class.forName("org.richfaces.renderkit.html.HtmlToolTipRendererBlock");
			} else if("inline".equals(toolTip.getLayout())){
				rendererClass = Class.forName("org.richfaces.renderkit.html.HtmlToolTipRenderer");
			} else {
				throw new FacesException("Only \"block\" or \"inline\" values can be established to attribute \"layout\".");
			}
			ToolTipRenderer renderer = (ToolTipRenderer)rendererClass.newInstance(); 
			renderer.doEncodeBegin(writer, context, component);
			
		} catch(Exception e){
			throw new FacesException(e.getMessage(), e);
		}
	}

	
	protected void doEncodeChildren(ResponseWriter writer,
			FacesContext context, UIComponent component) throws IOException {

		UIToolTip toolTip = (UIToolTip)component;
		Class rendererClass = null;
		try{
			if("block".equals(toolTip.getLayout())){
				rendererClass = Class.forName("org.richfaces.renderkit.html.HtmlToolTipRendererBlock");
			} else {
				rendererClass = Class.forName("org.richfaces.renderkit.html.HtmlToolTipRenderer");
			}
			
			ToolTipRenderer renderer = null;
			
			renderer = (ToolTipRenderer)rendererClass.newInstance();
			
			renderer.doEncodeChildren(writer, context, component);
		} catch(Exception e){
			throw new FacesException(e.getMessage(), e);
		}
		
		
	}

	
	protected void doEncodeEnd(ResponseWriter writer, FacesContext context,
			UIComponent component) throws IOException {

		UIToolTip toolTip = (UIToolTip)component;
		Class rendererClass = null;
		try{
			if("block".equals(toolTip.getLayout())){
				rendererClass = Class.forName("org.richfaces.renderkit.html.HtmlToolTipRendererBlock");
			} else {
				rendererClass = Class.forName("org.richfaces.renderkit.html.HtmlToolTipRenderer");
			}
			ToolTipRenderer renderer = (ToolTipRenderer)rendererClass.newInstance(); 
			renderer.doEncodeEnd(writer, context, component);
		} catch (Exception e) {
			throw new FacesException(e.getMessage(), e);
		}
	}
	
	
	/* (non-Javadoc)
	 * @see javax.faces.render.Renderer#getRendersChildren()
	 */
	public boolean getRendersChildren() {
		return true;
	}

	
}

