package org.richfaces.picklist.util;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.el.ValueExpression;
import javax.faces.FacesException;
import javax.faces.component.UIComponent;
import javax.faces.component.UISelectMany;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.model.SelectItem;

import org.ajax4jsf.util.SelectUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class PickListUtils {

    private static final Log log = LogFactory.getLog(PickListUtils.class);

    public static Converter findUISelectManyConverter(FacesContext context, UISelectMany component) {

	Converter converter = component.getConverter();
	if (converter != null) {
	    return converter;
	}

	ValueExpression ve = component.getValueExpression("value");
	if (ve == null) {
	    return null;
	}

	Class valueType = ve.getType(context.getELContext());
	if (valueType == null) {
	    return null;
	}

	if (List.class.isAssignableFrom(valueType)) {
	    List selectItems = SelectUtils.getSelectItems(context, component);
	    if (selectItems != null && selectItems.size() > 0) {
		SelectItem selectItem = (SelectItem) selectItems.get(0);
		Class listComponentType = selectItem.getValue().getClass();
		if (!(String.class.equals(listComponentType))) {
		    try {
			return context.getApplication().createConverter(listComponentType);
		    } catch (FacesException e) {
			log.error("No Converter for type " + listComponentType.getName() + " found", e);
			return null;
		    }
		}
	    }
	    return null;
	}

	if (!valueType.isArray()) {
	    throw new IllegalArgumentException("ValueBinding for UISelectMany :  must be of type List or Array");
	}

	Class arrayComponentType = valueType.getComponentType();
	if (String.class.equals(arrayComponentType))
	    return null;
	if (Object.class.equals(arrayComponentType))
	    return null;

	try {
	    return context.getApplication().createConverter(arrayComponentType);
	} catch (FacesException e) {
	    log.error("No Converter for type " + arrayComponentType.getName() + " found", e);
	    return null;
	}
    }

    public static Converter findUISelectManyConverterFailsafe(FacesContext facesContext, UIComponent uiComponent) {
	Converter converter;
	try {
	    converter = findUISelectManyConverter(facesContext, (UISelectMany) uiComponent);
	} catch (FacesException e) {
	    log.error("Error finding Converter for component with id " + uiComponent.getClientId(facesContext), e);
	    converter = null;
	}
	return converter;
    }

    public static List getValuesList(UIComponent uiComponent, FacesContext facesContext, Converter converter) {
	List lookupList = null;
	if (uiComponent instanceof UISelectMany) {
	    UISelectMany uiSelectMany = (UISelectMany) uiComponent;
	    lookupList = getSelectValuesList(facesContext, uiComponent, converter, uiSelectMany);
	}
	return lookupList;
    }

    public static List getSelectValuesList(FacesContext context, UIComponent component, Converter converter, UISelectMany uiSelectMany) {
	Object selectedValues = uiSelectMany.getValue();
	return internalSelectValuesList(context, component, converter, uiSelectMany, selectedValues);
    }

    private static List internalSelectValuesList(FacesContext context, UIComponent component, Converter converter, UISelectMany uiSelectMany, Object values) {
	List valuesList = new ArrayList();

	if (values == null || "".equals(values)) {

	    return valuesList;

	} else if (values instanceof Object[]) {

	    Object[] ar = (Object[]) values;
	    if (ar.length == 0) {
		return valuesList;
	    }
	    valuesList = new ArrayList();
	    for (int i = 0; i < ar.length; i++) {
		valuesList.add(getConvertedStringValue(context, component, converter, ar[i]));
	    }
	    return valuesList;

	} else if (values.getClass().isArray()) {

	    int len = Array.getLength(values);
	    valuesList = new ArrayList();
	    for (int i = 0; i < len; i++) {
		valuesList.add(getConvertedStringValue(context, component, converter, Array.get(values, i)));
	    }
	    return valuesList;

	} else if (values instanceof List) {

	    List lst = (List) values;
	    if (lst.size() == 0) {
		return lst;
	    } else {
		valuesList = new ArrayList();
		for (Iterator i = lst.iterator(); i.hasNext();) {
		    valuesList.add(getConvertedStringValue(context, component, converter, i.next()));
		}
		return valuesList;
	    }

	} else {
	    throw new IllegalArgumentException("Value of UISelectMany component : is not of type Array or List");
	}
    }

    public static String getConvertedStringValue(FacesContext context, UIComponent component, Converter converter, Object value) {
	if (converter == null) {
	    if (value == null) {
		return "";
	    } else if (value instanceof String) {
		return (String) value;
	    } else {
		throw new IllegalArgumentException("Value is not String (class=" + value.getClass().getName() + ", value=" + value + ") and component "
			+ component.getClientId(context) + " does not have a Converter");
	    }
	}
	return converter.getAsString(context, component, value);
    }
}
