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

import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.convert.Converter;

import org.ajax4jsf.component.UIDataAdaptor;
import org.ajax4jsf.javascript.JSFunctionDefinition;
import org.ajax4jsf.javascript.JSReference;
import org.ajax4jsf.javascript.ScriptString;
import org.ajax4jsf.javascript.ScriptUtils;
import org.ajax4jsf.renderkit.ComponentVariables;
import org.ajax4jsf.renderkit.ComponentsVariableResolver;
import org.ajax4jsf.renderkit.RendererUtils.HTML;
import org.richfaces.component.UIOrderingBaseComponent;
import org.richfaces.component.UIOrderingBaseComponent.ItemState;

/**
 * @author Nick Belaevski
 *
 */
public abstract class OrderingComponentRendererBase extends AbstractRowsRenderer {

	private static final Converter DEFAULT_CONVERTER = new Converter() {

		public Object getAsObject(FacesContext context, UIComponent component,
				String value) {

			return value;
		}

		public String getAsString(FacesContext context, UIComponent component,
				Object value) {
			if (value instanceof String) {
				return (String) value;
			}
			
			if (null == value) {
				return "";
			}
			
			return value.toString();
		}

	};
	
	private static final String ITEM_STATE_VAR_NAME = "itemState";

	protected final static String SHOW_LABELS_ATTRIBUTE_NAME = "showButtonLabels";
	
	protected final static String ATTRIBUTE_CE_ONHEADERCLICK = "onheaderclick";
	
	protected final static String CONTROL_TYPE_LINK = "link";
	
	protected final static String CONTROL_TYPE_BUTTON = "button";
	
	protected final static String CONTROL_TYPE_NONE = "none";
	
	protected final static String ATTRIBUTE_CONTROLS_TYPE = "controlsType";
	
	protected static abstract class ControlsHelper {
		private String name;

		private String bundlePropertyName;

		private String imageURI;

		private String facetName;

		private String styleClassName;

		private String idSuffix;

		String customEvent;

		String styleFromAttribute;

		private String buttonStyleClass;

		boolean enable;

		private String defaultText;

		private String labelAttributeName;

		public abstract boolean isRendered(FacesContext context, UIOrderingBaseComponent list);

		public ControlsHelper(String name, String bundlePropertyName, String defaultText, String imageURI,
				String facetName, String styleClassName, String styleFromAttribute, String buttonStyleClass,
				String idSuffix, String customEvent, boolean isEnable, String labelAttributeName) {
			super();
			this.name = name;
			this.bundlePropertyName = bundlePropertyName;
			this.defaultText = defaultText;
			this.imageURI = imageURI;
			this.facetName = facetName;
			this.styleClassName = styleClassName;
			this.styleFromAttribute = styleFromAttribute;
			this.idSuffix = idSuffix;
			this.customEvent = customEvent; 
			this.buttonStyleClass = buttonStyleClass;
			this.enable = isEnable;
			this.labelAttributeName = labelAttributeName;
		}

		public String getName() {
			return name;
		}

		public String getBundlePropertyName() {
			return bundlePropertyName;
		}

		public String getImageURI() {
			return imageURI;
		}

		public String getFacetName() {
			return facetName;
		}

		public String getStyleClassName() {
			return styleClassName;
		}

		public String getIdSuffix() {
			return idSuffix;
		}

		public String getCustomEvent() {
			return customEvent;
		}

		public String getStyleFromAttribute() {
			return styleFromAttribute;
		}

		public String getButtonStyleClass() {
			return buttonStyleClass;
		}

		public boolean isEnable() {
			return enable;
		}

		public String getDefaultText() {
			return defaultText;
		}

		public String getLabelAttributeName() {
			return labelAttributeName;
		}
	}

	protected static final class SelectionState {
	
		private boolean firstSelected = false;
		private boolean firstSelectedLatch = false;
		
		private boolean selectedLatch = false;

		private boolean itemsExist = false;

		private boolean lastSelected = false;
		
		public SelectionState() {
			super();
		}
		
		public void addState(boolean selected) {
			itemsExist = true;
			
			if (!firstSelectedLatch) {
				firstSelected = selected;
				firstSelectedLatch = true;
			}

			if (selected) {
				selectedLatch = true;
			}
			
			lastSelected = selected;
		}
		
		public boolean isFirstSelected() {
			return firstSelected;
		}
		
		public boolean isSelected() {
			return selectedLatch;
		}

		public boolean isItemExist() {
			return itemsExist;
		}
		
		public boolean isLastSelected() {
			return lastSelected;
		}
	}

	private final String bundleName;
	
	public OrderingComponentRendererBase(String bundleName) {
		super();
		
		this.bundleName = bundleName;
	}
	
	public void encodeCaption(FacesContext context, UIComponent component) throws IOException {
		encodeCaption(context, component, OrderingComponentControlsHelper.FACET_CAPTION, 
			"rich-ordering-list-caption");
	}
	
	protected void encodeCaption(FacesContext context, UIComponent component, 
			String facetCaption, String captionStyle, String attributeName) throws IOException{
		ResponseWriter writer = context.getResponseWriter();
		UIComponent facetEl = component.getFacet(facetCaption);
		boolean renderFacet = ((facetEl != null) && facetEl.isRendered());
		String captionAttr = (String) component.getAttributes().get(attributeName);

		if (renderFacet || (captionAttr != null)) {
			writer.startElement(HTML.DIV_ELEM, component);
			writer.writeAttribute(HTML.class_ATTRIBUTE, captionStyle, null);
			if (renderFacet) {
				renderChild(context, facetEl);
			} else {
				writer.write(captionAttr);
			}
			writer.endElement(HTML.DIV_ELEM);
		}
	}
	
	protected void encodeCaption(FacesContext context, UIComponent component, String facetCaption, String captionStyle) 
			throws IOException {
		encodeCaption(context, component, facetCaption, captionStyle, OrderingComponentControlsHelper.ATTRIBUTE_CAPTION_LABEL);
	}

	public void encodeHeader(FacesContext context, UIOrderingBaseComponent component) 
	throws IOException {
		encodeHeader(context, component, "rich-ordering-list-table-header", "rich-ordering-list-table-header-cell", "headerClass");
	}
	
	protected void encodeHeader(FacesContext context, UIOrderingBaseComponent component, String rowClass, 
								String cellClass, String headerClassAttr) throws IOException {
		ResponseWriter writer = context.getResponseWriter();
		//UIComponent header = orderingList.getHeader();
		Iterator headers = component.columns();

		if (headers.hasNext()) {
			writer.startElement("thead", component);
			String headerClass = (String) component.getAttributes().get(headerClassAttr);

			writer.startElement("tr", component);
			encodeStyleClass(writer, null, rowClass, null, headerClass);
			encodeHeaderFacets(context, writer, headers, cellClass, headerClass, "header", "th", component);

			writer.endElement("tr");
			
			writer.endElement("thead");
		}
	}

	protected void renderDefaultControl(FacesContext context,
			UIOrderingBaseComponent orderingList, ResponseWriter writer,
			boolean useFacet, OrderingComponentRendererBase.ControlsHelper helper, String clientId, ResourceBundle bundle, 
			boolean enabled, String baseStyle, String baseControlStyle)
			throws IOException {
		UIComponent facet = orderingList.getFacet(helper.getFacetName());
		String customEvent = null;
		Map attributes = orderingList.getAttributes();
		if (helper.customEvent != null) {
			customEvent = (String) attributes.get(helper.customEvent);
		}

		String styleFromAttribute = (String) attributes
				.get(helper.styleFromAttribute);

		String baseStyleLight = baseStyle.concat("-light");
		String baseStylePress = baseStyle.concat("-press");

		String currentStyle = baseControlStyle + helper.getStyleClassName();
		if (styleFromAttribute != null) {
			currentStyle = styleFromAttribute.concat(currentStyle);
		}

		writer.startElement(HTML.DIV_ELEM, orderingList);
		String controlId = clientId + helper.getIdSuffix();
		writer.writeAttribute(HTML.id_ATTRIBUTE, controlId, null); //FIXME:
		writer.writeAttribute(HTML.class_ATTRIBUTE, currentStyle, null);
		String style = null;
		if (enabled) {
			style = "display:block;";
		} else {
			style = "display:none;";
		}
		writer.writeAttribute(HTML.style_ATTRIBUTE, style, null);
		if (!useFacet) {
			
			writer.startElement(HTML.DIV_ELEM, orderingList);
			writer.writeAttribute(HTML.class_ATTRIBUTE, baseStyle + helper.getButtonStyleClass(), null);
			if (helper.enable) {
				writer.writeAttribute(HTML.onmouseover_ATTRIBUTE, "this.className='" + baseStyleLight + "'", null);
				writer.writeAttribute(HTML.onmousedown_ATTRIBUTE, "this.className='" + baseStylePress + "'", null);
				writer.writeAttribute(HTML.onmouseup_ATTRIBUTE, "this.className='" + baseStyle + "'", null);
				writer.writeAttribute(HTML.onmouseout_ATTRIBUTE, "this.className='" + baseStyle + "'", null);
			}
			
			writer.startElement(HTML.a_ELEMENT, orderingList);
			writer.writeAttribute(HTML.id_ATTRIBUTE, controlId + "link", null); //FIXME:
			writer.writeAttribute(HTML.HREF_ATTR, "#", null);
			writer.writeAttribute(HTML.onclick_ATTRIBUTE, "return false;", null);
			if (!helper.enable) {
				writer.writeAttribute(HTML.DISABLED_ATTR, "disabled", null);
				writer.writeAttribute(HTML.class_ATTRIBUTE, baseStyle + "-a-disabled", null);
				writer.startElement(HTML.a_ELEMENT, orderingList);
			} else {
				writer.writeAttribute(HTML.class_ATTRIBUTE, baseStyle + "-selection", null);
				writer.writeAttribute(HTML.onblur_ATTRIBUTE, "Control.onblur(this);", null);
				writer.writeAttribute(HTML.onfocus_ATTRIBUTE, "Control.onfocus(this);", null);
			}
			
			writer.startElement(HTML.DIV_ELEM, orderingList);
			writer.writeAttribute(HTML.class_ATTRIBUTE, baseStyle + "-content", null);

		}

		if (customEvent != null) {
			writer.writeAttribute(HTML.onclick_ATTRIBUTE, customEvent,null);
		}
		
		if (useFacet) {
			renderChild(context, facet);
		} else {
			writer.startElement(HTML.IMG_ELEMENT, orderingList);
			writer.writeAttribute(HTML.width_ATTRIBUTE, "15", null);
			writer.writeAttribute(HTML.height_ATTRIBUTE, "15", null);
			writer.writeAttribute(HTML.border_ATTRIBUTE, "0", null);
			writer.writeAttribute(HTML.alt_ATTRIBUTE, helper.getFacetName(),
					null);
			writer.writeAttribute(HTML.src_ATTRIBUTE, getResource(
					helper.getImageURI()).getUri(context, null), null);

			writer.endElement(HTML.IMG_ELEMENT);

			if (getUtils().isBooleanAttribute(orderingList, SHOW_LABELS_ATTRIBUTE_NAME)) {
				String label = (String) attributes.get(helper.getLabelAttributeName());
				
				if (label == null && bundle != null) {
					try {
						label = bundle.getString(helper.getBundlePropertyName());
					} catch (MissingResourceException e) {
					
					}
				}

				if (label == null) {
					label = helper.getDefaultText();
				}
				
				writer.writeText(label, null);
			}
		}
		// writer.writeAttribute(HTML.id_ATTRIBUTE, clientId +
		// helper.getIdSuffix(), null);

		// writer.writeAttribute(HTML.class_ATTRIBUTE, currentStyle, null);
		
		if (!useFacet) {
			writer.endElement(HTML.DIV_ELEM);
			if (!helper.enable) {
				writer.endElement(HTML.a_ELEMENT);
			}
			writer.endElement(HTML.a_ELEMENT);
			writer.endElement(HTML.DIV_ELEM);
		}
		writer.endElement(HTML.DIV_ELEM);
	}
	
	protected boolean isHeaderExists(FacesContext context, UIOrderingBaseComponent component, String facetName) {
		Iterator headers = component.columns();
		while (headers.hasNext()) {
			UIComponent column = (UIComponent) headers.next();
			UIComponent facet = column.getFacet(facetName);
			if (facet != null) {
				return true;
			}
		}
		return false;
	}
	
	protected void encodeHeaderFacets(FacesContext context, ResponseWriter writer, 
									  Iterator headers, String skinCellClass,
									  String headerClass, String facetName, String element, UIOrderingBaseComponent orderingList
									  )
			throws IOException {
		while (headers.hasNext()) {
			UIComponent column = (UIComponent) headers.next();
			String classAttribute = facetName + "Class";
			String columnHeaderClass = (String) column.getAttributes().get(classAttribute);
			
			writer.startElement(element, column);
			if (!headers.hasNext()) {
				skinCellClass = skinCellClass + "-last";
			}
			encodeStyleClass(writer, null, skinCellClass, headerClass, columnHeaderClass);
			getUtils().encodeAttribute(context, column, "colspan");
			writer.startElement("div", column);
			writer.writeAttribute("style", "overflow:hidden;white-space: nowrap;", null);
			String onHeaderClickEvent = (String) orderingList.getAttributes().get(ATTRIBUTE_CE_ONHEADERCLICK);
			if (onHeaderClickEvent != null) {
				writer.writeAttribute(HTML.onclick_ATTRIBUTE, onHeaderClickEvent, null);
			}
			
			UIComponent facet = column.getFacet(facetName);
			if (facet != null && facet.isRendered()) {
				renderChild(context, facet);
			} else {
				writer.write("&nbsp;");
			}
			
			writer.endElement("div");
			writer.endElement(element);
		}
	}

	protected void encodeControlFacet(FacesContext context,
			UIOrderingBaseComponent orderingList, OrderingComponentRendererBase.ControlsHelper helper,
			String clientId, ResponseWriter writer, boolean enabled, String baseStyle, 
			String baseControlStyle) throws IOException {
		Locale locale = null;
		
		UIViewRoot viewRoot = context.getViewRoot();
		if (viewRoot != null) {
			locale = viewRoot.getLocale();
		}
		
		ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
		ResourceBundle bundle = null;
		
		if (locale != null) {
			try {
				bundle = ResourceBundle.getBundle(bundleName, locale, contextClassLoader);
			} catch (MissingResourceException e) {

			}
		}
		
		Map attributes = orderingList.getAttributes();
		UIComponent facet = orderingList.getFacet(helper.getFacetName());
		boolean useFacet = (facet != null && facet.isRendered());
		String controlType = (String) attributes.get(ATTRIBUTE_CONTROLS_TYPE);

		if (CONTROL_TYPE_NONE.equals(controlType)) {
			if (useFacet) {
				renderChild(context, facet);
			}
		} else {
			renderDefaultControl(context, orderingList, writer, useFacet,
								 helper, clientId, bundle ,enabled, baseStyle, baseControlStyle);
		}
	}
	
	public void encodeBegin(FacesContext context, UIComponent component)
			throws IOException {
		UIOrderingBaseComponent orderingComponent = (UIOrderingBaseComponent) component;
		ComponentVariables variables = ComponentsVariableResolver.getVariables(this, component);
		variables.setVariable(ITEM_STATE_VAR_NAME, orderingComponent.getItemState());
		
		super.encodeBegin(context, component);
	}
	
	protected ItemState getItemState(FacesContext context, UIComponent component, ComponentVariables variables)
			throws IOException {
		return (ItemState) variables.getVariable(ITEM_STATE_VAR_NAME);
	}

	protected Converter getConverter(FacesContext context, UIOrderingBaseComponent component) {
		Converter converter = component.getConverter();

		if (converter == null) {
			converter = component.getConverterForValue(context);
		}
		
		if (converter == null) {
			converter = DEFAULT_CONVERTER;
		}
		
		return converter;
	}
	
	public String getCaptionDisplay(FacesContext context, UIComponent component) {
		Object caption = component.getAttributes().get(OrderingComponentControlsHelper.ATTRIBUTE_CAPTION_LABEL);
		UIComponent facet = component.getFacet(OrderingComponentControlsHelper.FACET_CAPTION);
		
		if ((null != caption && !"".equals(caption)) ||
				(null != facet && facet.isRendered())) {
			return "";
		}
		return "display: none;";
	}
	
	public String getAsEventHandler(FacesContext context, UIComponent component, String attributeName) {
		String event = (String) component.getAttributes().get(attributeName);
		ScriptString result = JSReference.NULL;
		
		if (event != null) {
			event = event.trim();
		
			if (event.length() != 0) {
				JSFunctionDefinition function = new JSFunctionDefinition();
				function.addParameter("event");
				function.addToBody(event);

				result = function;
			}
		}

		return ScriptUtils.toScript(result);
	}
	
	public String getColumnClassesAsJSArray(FacesContext context, UIComponent component) {
		return ScriptUtils.toScript(getClassesAsList(context, component, "columnClasses"));
	}
	
	public String getRowClassesAsJSArray(FacesContext context, UIComponent component) {
		return ScriptUtils.toScript(getClassesAsList(context, component, "rowClasses"));
	}
	
	protected List getClassesAsList(FacesContext context, UIComponent component, String attr) {
		
		String value = (String) ((UIDataAdaptor) component).getAttributes().get(attr);
		if (value != null && (value.length() != 0)) {
			return Arrays.asList(value.split(","));
		}
		return null;
	}
}
