package org.seasar.jsf.render.html;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import javax.faces.component.UIComponent;
import javax.faces.component.UIOutput;
import javax.faces.component.UISelectItem;
import javax.faces.component.UISelectItems;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.convert.Converter;
import javax.faces.model.SelectItem;
import javax.faces.model.SelectItemGroup;
import javax.faces.render.Renderer;

import org.apache.myfaces.renderkit.html.HTML;
import org.seasar.jsf.JsfConstants;
import org.seasar.jsf.exception.NoSelectItemRuntimeException;
import org.seasar.jsf.util.UIValueUtil;

public class HtmlSelectRenderer extends Renderer {

	protected void renderSelectOptions(FacesContext context,
			UIOutput component, List selectItemList, List lookupAsString) throws IOException {

		for (Iterator it = selectItemList.iterator(); it.hasNext();) {
			SelectItem selectItem = (SelectItem) it.next();
			renderSelectItem(context, component, selectItem, lookupAsString);
		}
	}
	
	protected void renderSelectItem(FacesContext context,
			UIOutput component, SelectItem selectItem, List lookupAsString) throws IOException {

		ResponseWriter writer = context.getResponseWriter();
		Converter converter = component.getConverter();
		if (selectItem instanceof SelectItemGroup) {
			writer.startElement(JsfConstants.OPTGROUP_ELEM, null);
			writer.writeAttribute(JsfConstants.LABEL_ATTR, selectItem
					.getLabel(), null);
			SelectItem[] selectItems = ((SelectItemGroup) selectItem)
					.getSelectItems();
			renderSelectOptions(context, component, Arrays.asList(selectItems),
					lookupAsString);
			writer.endElement(JsfConstants.OPTGROUP_ELEM);
		} else {
			Object itemValue = selectItem.getValue();
			String itemStrValue = UIValueUtil.getValueAsString(context, component,
					itemValue, converter);
			writer.write("\t\t");
			writer.startElement(JsfConstants.OPTION_ELEM, null);
			if (itemStrValue != null) {
				writer.writeAttribute(JsfConstants.VALUE_ATTR, itemStrValue, null);
			}
			if (lookupAsString.contains(itemStrValue)) {
				writer.writeAttribute(JsfConstants.SELECTED_ATTR,
						JsfConstants.SELECTED_VALUE, null);
			}

			writer.writeText(selectItem.getLabel(), null);

			if (selectItem.isDisabled()) {
				writer.writeAttribute(JsfConstants.DISABLED_ATTR,
						JsfConstants.DISABLED_VALUE, null);
			}

			writer.endElement(HTML.OPTION_ELEM);
		}
	}

	protected List getSelectItemList(UIComponent component) {
		List list = new ArrayList(component.getChildCount());
		for (Iterator children = component.getChildren().iterator(); children
				.hasNext();) {
			UIComponent child = (UIComponent) children.next();
			if (child instanceof UISelectItem) {
				addSelectItem(list, (UISelectItem) child);
			} else if (child instanceof UISelectItems) {
				addSelectItems(list, (UISelectItems) child);
			}
		}
		return list;
	}

	protected void addSelectItem(List list, UISelectItem component) {
		Object value = component.getValue();
		if (value != null) {
			addIfSelectItem(list, value, component);
		} else {
			Object itemValue = component.getItemValue();
			String label = component.getItemLabel();
			String description = component.getItemDescription();
			boolean disabled = component.isItemDisabled();
			SelectItem selectItem = createSelectItem(itemValue, label,
					description, disabled);
			list.add(selectItem);
		}
	}

	protected void addIfSelectItem(List list, Object value,
			UIComponent component) {
		if (value instanceof SelectItem) {
			list.add(value);
		} else {
			FacesContext context = FacesContext.getCurrentInstance();
			String clientId = component.getClientId(context);
			throw new NoSelectItemRuntimeException(clientId);
		}
	}

	protected SelectItem createSelectItem(Object value, String label,
			String description, boolean disabled) {

		if (label == null && value != null) {
			label = value.toString();
		}
		SelectItem selectItem = new SelectItem();
		if (value != null) {
			selectItem.setValue(value);
		}
		if (label != null) {
			selectItem.setLabel(label);
		}
		selectItem.setDescription(description);
		selectItem.setDisabled(disabled);
		return selectItem;
	}

	protected void addSelectItems(List list, UISelectItems component) {
		Object value = component.getValue();
		if (value instanceof SelectItem) {
			list.add(value);
		} else if (value instanceof SelectItem[]) {
			SelectItem[] items = (SelectItem[]) value;
			for (int i = 0; i < items.length; i++) {
				list.add(items[i]);
			}
		} else if (value instanceof Collection) {
			for (Iterator it = ((Collection) value).iterator(); it.hasNext();) {
				Object item = it.next();
				addIfSelectItem(list, item, component);
			}
		} else {
			FacesContext context = FacesContext.getCurrentInstance();
			String clientId = component.getClientId(context);
			throw new NoSelectItemRuntimeException(clientId);
		}
	}
}