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.ComponentFactory;
import org.seasar.jsf.util.BindingUtil;
import org.w3c.dom.Attr;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

/**
 * @author higa
 *
 */
public abstract class AbstractComponentFactory implements ComponentFactory {

	private Node node_;
	private List children_ = new ArrayList();
	private Map properties_ = new HashMap();
	private Set customPropertyNames_ = new HashSet();
	
	public AbstractComponentFactory(Node node) {
		node_ = node;
		setupProperties();
		renameProperties();
	}
	
	protected Node getNode() {
		return node_;
	}

	/**
	 * @see org.seasar.jsf.template.ComponentFactory#getChildCount()
	 */
	public int getChildCount() {
		return children_.size();
	}

	/**
	 * @see org.seasar.jsf.template.ComponentFactory#getChild(int)
	 */
	public ComponentFactory getChild(int index) {
		return (ComponentFactory) children_.get(index);
	}

	/**
	 * @see org.seasar.jsf.template.ComponentFactory#addChild(org.seasar.jsf.template.ComponentFactory)
	 */
	public void addChild(ComponentFactory componentFactory) {
		children_.add(componentFactory);
	}
	
	protected Map getProperties() {
		return properties_;
	}
	
	protected Object getProperty(String propertyName) {
		return properties_.get(propertyName);
	}
	
	protected void setupProperties() {
		NamedNodeMap nnm = node_.getAttributes();
		if (nnm == null) {
			return;
		}
		int length = nnm.getLength();
		for (int i = 0; i < length; ++i) {
			Attr attr = (Attr) nnm.item(i);
			if (JsfConstants.BINDING_NSURI.equals(attr.getNamespaceURI())) {
				properties_.put(attr.getLocalName(), BindingUtil.getValueReference(attr.getValue()));
			} else if (properties_.get(attr.getName()) == null) {
				properties_.put(attr.getName(), attr.getValue());
			}
		}
	}
	
	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 = properties_.keySet().iterator(); i.hasNext(); ) {
			String propertyName = (String) i.next();
			Object value = properties_.get(propertyName);
			String s = (String) value;
			if (StringUtil.isEmpty(s) || isCustomProperty(s)) {
				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);
	}
}
