package org.seasar.jsf.util;

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

import javax.faces.FacesException;
import javax.faces.component.UIComponent;
import javax.faces.component.UIOutput;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import javax.faces.el.ValueBinding;

import org.seasar.framework.log.Logger;
import org.seasar.jsf.JsfConstants;

/**
 * @author higa
 *  
 */
public final class RenderUtil {

	private static Logger logger = Logger.getLogger(RenderUtil.class);

	private RenderUtil() {
	}

	public static void encodeChildren(FacesContext context,
			UIComponent component) throws IOException {

		Iterator i = component.getChildren().iterator();
		while (i.hasNext()) {
			encodeChild(context, (UIComponent) i.next());
			if (context.getResponseComplete()) {
				break;
			}
		}
	}

	public static void encodeChild(FacesContext context, UIComponent component)
			throws IOException {

		component.encodeBegin(context);
		if (component.getRendersChildren()) {
			component.encodeChildren(context);
		} else {
			encodeChildren(context, component);
		}
		component.encodeEnd(context);
	}

	public static boolean renderAttributes(ResponseWriter writer,
			UIComponent component, String[] attributeNames) throws IOException {

		boolean somethingDone = false;
		for (int i = 0, len = attributeNames.length; i < len; i++) {
			String attrName = attributeNames[i];
			if (renderAttribute(writer, component, attrName)) {
				somethingDone = true;
			}
		}
		return somethingDone;
	}

	public static boolean renderAttribute(ResponseWriter writer,
			UIComponent component, String attributeName) throws IOException {

		Object value = component.getAttributes().get(attributeName);
		return renderAttribute(writer, attributeName, value, attributeName);
	}

	public static boolean renderAttribute(ResponseWriter writer,
			String attributeName, Object value, String propertyName)
			throws IOException {

		if (value == null) {
			return false;
		}
		if (attributeName.equalsIgnoreCase(JsfConstants.STYLE_CLASS_ATTR)) {
			attributeName = JsfConstants.CLASS_ATTR;
		}
		writer.writeAttribute(attributeName, value, propertyName);
		return true;
	}

	public static void renderIdIfNecessary(ResponseWriter writer,
			UIComponent component, FacesContext context) throws IOException {

		String id = component.getId();
		if (id != null && !id.startsWith(UIViewRoot.UNIQUE_ID_PREFIX)) {
			writer.writeAttribute(JsfConstants.ID_ATTR, component
					.getClientId(context), null);
		}
	}

	public static Object getConvertedUIOutputValue(FacesContext context,
			UIOutput output, Object submittedValue) throws ConverterException {

		if (submittedValue == null) {
			return null;
		}
		if (!(submittedValue instanceof String)) {
			submittedValue = submittedValue.toString();
		}
		Converter converter = findConverterForSubmittedValue(context, output);
		if (converter == null) {
			return submittedValue;
		} else {
			return converter.getAsObject(context, output,
					(String) submittedValue);
		}
	}

	public static Converter findConverterForSubmittedValue(
			FacesContext context, UIOutput component) {

		Converter converter = component.getConverter();
		if (converter != null) {
			return converter;
		}
		ValueBinding vb = component.getValueBinding("value");
		if (vb == null) {
			return null;
		}
		Class valueType = vb.getType(context);
		if (valueType == null) {
			return null;
		}
		if (String.class.equals(valueType) || Object.class.equals(valueType)) {
			return null;
		}
		try {
			return context.getApplication().createConverter(valueType);
		} catch (FacesException ex) {
			logger.log(ex);
			return null;
		}
	}
}