package org.seasar.jsf.template.impl;

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

import javax.faces.application.Application;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;

import org.seasar.framework.beans.BeanDesc;
import org.seasar.framework.beans.PropertyDesc;
import org.seasar.framework.beans.factory.BeanDescFactory;
import org.seasar.framework.util.StringUtil;
import org.seasar.jsf.JsfConstants;
import org.seasar.jsf.template.ComponentTreeBuilder;
import org.seasar.jsf.util.BindingUtil;
import org.xml.sax.Attributes;

/**
 * @author higa
 *
 */
public abstract class AbstractUIComponentBuilder extends AbstractComponentTreeBuilder {

	private String namespaceURI_;
	private String localName_;
	private String qName_;
	private Map properties_ = new HashMap();
	private Set customPropertyNames_ = new HashSet();

	public AbstractUIComponentBuilder(String namespaceURI, String localName,
			String qName, Attributes attributes) {
		
		namespaceURI_ = namespaceURI;
		localName_ = localName;
		qName_ = qName;
		setupProperties(attributes);
		renameProperties();
	}
	
	public String getNamespaceURI() {
		return namespaceURI_;
	}
	
	public String getLocalName() {
		return localName_;
	}
	
	public String getQName() {
		return qName_;
	}
	
	public void buildTree(UIComponent parent) {
		UIComponent component = createComponent();
		setProperties(component);
		parent.getChildren().add(component);
		for (int i = 0; i < getChildCount(); ++i) {
			ComponentTreeBuilder child = getChild(i);
			child.buildTree(component);
		}
	}
	
	protected Map getProperties() {
		return properties_;
	}
	
	protected Object getProperty(String propertyName) {
		return properties_.get(propertyName);
	}
	
	protected void setupProperties(Attributes attributes) {
		int length = attributes.getLength();
		for (int i = 0; i < length; ++i) {
			if (JsfConstants.S2JSF_NSURI.equals(attributes.getURI(i))) {
				properties_.put(attributes.getLocalName(i), attributes.getValue(i));
			} else if (properties_.get(attributes.getQName(i)) == null) {
				properties_.put(attributes.getQName(i), attributes.getValue(i));
			}
		}
	}
	
	protected void renameProperties() {
		renameProperty(JsfConstants.CLASS_ATTR, JsfConstants.STYLE_CLASS_ATTR);
	}
	
	protected void renameProperty(String from, String to) {
		if (properties_.containsKey(from)) {
			Object value = properties_.remove(from);
			properties_.put(to, value);
		}
	}
	
	protected void addCustomPropertyName(String propertyName) {
		customPropertyNames_.add(propertyName);
	}
	
	protected boolean isCustomProperty(String propertyName) {
		return customPropertyNames_.contains(propertyName);
	}
	
	protected void setProperties(UIComponent component) {
		List bindingPropertyNames = new ArrayList();
		BeanDesc beanDesc = BeanDescFactory.getBeanDesc(component.getClass());
		for (Iterator i = getProperties().keySet().iterator(); i.hasNext(); ) {
			String propertyName = (String) i.next();
			Object value = getProperties().get(propertyName);
			String s = (String) value;
			if (StringUtil.isEmpty(s) || isCustomProperty(propertyName)) {
				continue;
			}
			if (BindingUtil.isValueReference(s)) {
				FacesContext ctx = FacesContext.getCurrentInstance();
				Application app = ctx.getApplication();
				ValueBinding binding = app.createValueBinding(s);
				component.setValueBinding(propertyName, binding);
				bindingPropertyNames.add(propertyName);
			} else {
				if (beanDesc.hasPropertyDesc(propertyName)) {
					PropertyDesc pd = beanDesc.getPropertyDesc(propertyName);
					value = pd.convertIfNeed(value);
				}
				component.getAttributes().put(propertyName, value);
			}
		}
		component.getAttributes().put(JsfConstants.BINDING_PROPERTY_NAMES, bindingPropertyNames);
	}
}
