/**
 * 
 */
package org.richfaces.renderkit.html;

import java.util.Map;

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

import org.ajax4jsf.javascript.JSFunction;
import org.ajax4jsf.javascript.JSFunctionDefinition;
import org.ajax4jsf.javascript.JSReference;
import org.ajax4jsf.javascript.ScriptUtils;
import org.ajax4jsf.renderkit.AjaxRendererUtils;
import org.richfaces.renderkit.CompositeRenderer;
import org.richfaces.renderkit.RendererContributor;
import org.richfaces.renderkit.ScriptOptions;

/**
 * Renderer contributor for drop support based on {@link org.richfaces.renderkit.DropzoneRendererContributor}.
 * Used only with {@link org.richfaces.component.UIExtendedDataTable} component.
 * All functionality from wrapped {@link org.richfaces.renderkit.DropzoneRendererContributor} are available.
 * Also special methods {@link ExtDropzoneRendererContributor#buildOptions(FacesContext, UIComponent)}
 * and {@link ExtDropzoneRendererContributor#getScriptContribution(FacesContext, UIComponent, String, JSFunctionDefinition)}
 * are added.
 * 
 * @author pawelgo
 *
 */
public class ExtDropzoneRendererContributor implements RendererContributor {
	
	private ExtDropzoneRendererContributor() {
		super();
	}

	private static ExtDropzoneRendererContributor instance;
	
	private static RendererContributor wrappedContributor;

	public static synchronized ExtDropzoneRendererContributor getInstance() {
		if (instance == null) {
			instance = new ExtDropzoneRendererContributor();
			wrappedContributor = org.richfaces.renderkit.DropzoneRendererContributor.getInstance();
		}

		return instance;
	}

	public void decode(FacesContext context, UIComponent component, CompositeRenderer compositeRenderer) {
		wrappedContributor.decode(context, component, compositeRenderer);
	}

	public Class<?> getAcceptableClass() {
		return wrappedContributor.getAcceptableClass();
	}

	public String getScriptContribution(FacesContext context, UIComponent component) {
		return wrappedContributor.getScriptContribution(context, component);
	}
	
	/**
	 * Build JavaScript code for drag'n drop support specialized for change table column order event.
	 * @param context faces context
	 * @param column table column
	 * @param dropTargetScriptId id of HTML script element that contains defined DnD.ExtSimpleDropZone object
	 * @param onAjaxCompleteFunction JavaScript function to be called on AJAX request complete 
	 * @return JavaScript code
	 */
	public String getScriptContribution(FacesContext context, UIComponent column, String dropTargetScriptId, JSFunction preSendAjaxRequestFunction, JSFunctionDefinition onAjaxCompleteFunction) {
		StringBuffer result = new StringBuffer();

		result.append(".drop = ");

		JSFunctionDefinition definition = new JSFunctionDefinition();
		definition.addParameter("event");
		definition.addParameter("drag");

		Map<String, Object> requestOpts = AjaxRendererUtils.buildEventOptions(context, column);
		//replace parameters
		String clientId = column.getClientId(context);
		@SuppressWarnings("unchecked")
		Map<String, Object> parameters = (Map<String, Object>) requestOpts.get("parameters");
		if (parameters != null){
			if (parameters.containsKey(clientId)){
				parameters.remove(clientId);
				parameters.put(dropTargetScriptId, dropTargetScriptId);
			}
		}
		
		if (preSendAjaxRequestFunction != null){
			definition.addToBody(preSendAjaxRequestFunction.toScript()).addToBody(";");
		}
		definition.addToBody("var options = ").addToBody(ScriptUtils.toScript(requestOpts)).addToBody(";");
		definition.addToBody("options.parameters['" + org.richfaces.renderkit.DropzoneRendererContributor.DROP_TARGET_ID + "'] = '" + dropTargetScriptId + "';");
		
		if (onAjaxCompleteFunction != null)		
			definition.addToBody("options['" + AjaxRendererUtils.ONCOMPLETE_ATTR_NAME + "'] = " + onAjaxCompleteFunction.toScript() + ";");
		//TODO remove as legacy
		definition.addToBody("Object.extend(options.parameters,drag.getParameters());");
		definition.addToBody("var dzOptions = this.getDropzoneOptions(); if (dzOptions.ondrop) { if (!dzOptions.ondrop.call(this, event)) return; };");
		
		JSFunction dropFunction = AjaxRendererUtils.buildAjaxFunction(column, context);
		dropFunction.addParameter(new JSReference("options"));
		
		definition.addToBody(dropFunction.toScript()).addToBody(";");
		definition.appendScript(result);
		result.append(";");

		return result.toString();
	}
	
	/**
	 * Build JavaScript code for drag'n drop support specialized for change table column order event.
	 * @param context faces context
	 * @param column table column
	 * @param dropTargetScriptId id of HTML script element that contains defined DnD.ExtSimpleDropZone object
	 * @return JavaScript code
	 */
	public String getScriptContribution(FacesContext context, UIComponent column, String dropTargetScriptId) {
		return getScriptContribution(context, column, dropTargetScriptId, null, null);
	}

	public String[] getScriptDependencies() {
		return wrappedContributor.getScriptDependencies();
	}

	public String[] getStyleDependencies() {
		return wrappedContributor.getStyleDependencies();
	}

	/**
	 * Builds options for DnD.ExtSimpleDropZone JavaScript object.
	 * These options are specialized for drop target used to end changing table columns order event.
	 * @param context faces context
	 * @param column table column
	 * @return all options needed for drop JavaScript object to work
	 */
	public ScriptOptions buildOptions(FacesContext context, UIComponent column) {
		ScriptOptions options = new ScriptOptions(column);
		options.addOption("acceptedTypes", "COLUMN_ORDER_"+column.getParent().getClientId(context));
		return options;
	}

}
