package org.seasar.jsf.render.html;

import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.Renderer;

import org.seasar.framework.beans.BeanDesc;
import org.seasar.framework.beans.factory.BeanDescFactory;
import org.seasar.jsf.JsfConstants;
import org.seasar.jsf.component.UIElementNode;
import org.seasar.jsf.util.BindingUtil;
import org.seasar.jsf.util.RenderUtil;

public class HtmlTagRenderer extends Renderer {

	public void encodeBegin(FacesContext facesContext, UIComponent component)
			throws IOException {

		if (!component.isRendered()) {
			return;
		}
		ResponseWriter writer = facesContext.getResponseWriter();
		if (!(component instanceof UIElementNode)) {
			throw new IllegalArgumentException("not UIElementNode");
		}
		String tagName = ((UIElementNode) component).getTagName();
		writer.startElement(tagName, component);
		renderPathThroughAttributes(writer, component);
	}

	public void encodeEnd(FacesContext facesContext, UIComponent component)
			throws IOException {

		if (!component.isRendered()) {
			return;
		}
		ResponseWriter writer = facesContext.getResponseWriter();
		writer.endElement(((UIElementNode) component).getTagName());
	}

	/**
	 * @see javax.faces.render.Renderer#getRendersChildren()
	 */
	public boolean getRendersChildren() {
		return true;
	}
	
	/**
	 * @see javax.faces.render.Renderer#encodeChildren(javax.faces.context.FacesContext, javax.faces.component.UIComponent)
	 */
	public void encodeChildren(FacesContext context, UIComponent component)
			throws IOException {

		if (!component.isRendered()) {
			return;
		}
		RenderUtil.encodeChildren(context, component);
	}
	
	protected void renderPathThroughAttributes(ResponseWriter writer,
			UIComponent component) throws IOException {

		Map attrs = component.getAttributes();
		for (Iterator i = attrs.keySet().iterator(); i.hasNext();) {
			String attrName = (String) i.next();
			if (JsfConstants.BINDING_PROPERTY_NAMES.equals(attrName)) {
				continue;
			}
			Object value = getValue(component, attrName);
			renderAttribute(writer, attrName, value, attrName);
		}
		BeanDesc beanDesc = BeanDescFactory.getBeanDesc(component.getClass());
		List bindingPropertyNames = (List) attrs
				.get(JsfConstants.BINDING_PROPERTY_NAMES);
		for (int i = 0; i < bindingPropertyNames.size(); ++i) {
			String propertyName = (String) bindingPropertyNames.get(i);
			if (beanDesc.hasPropertyDesc(propertyName)) {
				continue;
			}
			Object value = BindingUtil.getBindingValue(component, propertyName);
			renderAttribute(writer, propertyName, value, propertyName);
		}
	}
	
	protected Object getValue(UIComponent component, String propertyName) {
		Object value = component.getAttributes().get(propertyName);
		if (value != null) {
			return value;
		}
		return BindingUtil.getBindingValue(component, propertyName);
	}
	
	protected void renderAttribute(ResponseWriter writer, String attributeName, Object value, String propertyName) throws IOException {
		if (value == null) {
			return;
		}
		writer.writeAttribute(changeAttributeName(attributeName), value, propertyName);
	}
	
	protected String changeAttributeName(String attributeName) {
		if (JsfConstants.STYLE_CLASS_ATTR.equalsIgnoreCase(attributeName)) {
			return JsfConstants.CLASS_ATTR;
		}
		return attributeName;
	}
}