package de.proveo.rts.web.portlet.tags;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javax.el.ValueExpression;
import javax.faces.component.UIComponent;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.sun.facelets.FaceletContext;
import com.sun.facelets.tag.TagAttribute;
import com.sun.facelets.tag.TagConfig;
import com.sun.facelets.tag.TagHandler;

import de.proveo.rts.web.managedBeans.common.operatorSelection.IOperatorSelection;
import de.proveo.rts.web.managedBeans.common.shiftSelection.IShiftSelection;
import de.proveo.rts.web.managedBeans.common.stateSelection.IStateSelection;
import de.proveo.rts.web.managedBeans.common.timeSelection.ITimeSelection;
import de.proveo.rts.web.managedBeans.common.unitSelection.IUnitSelection;
import de.proveo.rts.web.managedBeans.common.unitTypeSelection.IUnitTypeSelection;

/**
 * Maps value binding and type so other tags can access it.
 */
public final class SetManagedBeanValueHandler extends TagHandler {

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

	/** The name of the new variable that this tag defines. */
	private final TagAttribute managedBean;

	/** The actual value binding expression. */
	private final TagAttribute valueBinding;

	private final TagAttribute property;

	private final TagAttribute parameterType;

	/**
	 * Constructor. Set up the attributes for this tag.
	 * 
	 * @param config
	 *            TagConfig
	 */
	public SetManagedBeanValueHandler(final TagConfig config) {
		super(config);
		/* Define var and valueBinding attributes. */
		this.managedBean = this.getRequiredAttribute("managedBean");
		this.property = this.getRequiredAttribute("property");
		this.valueBinding = this.getRequiredAttribute("value");
		this.parameterType = this.getAttribute("parameterType");
	}

	/**
	 * Apply.
	 * 
	 * @param faceletsContext
	 *            faceletsContext
	 * @param parent
	 *            parent
	 * 
	 * @throws IOException
	 *             IOException
	 */
	public void apply(final FaceletContext faceletsContext,
			final UIComponent parent) {
		/* Create the ValueExpression from the valueBinding attribute. */
//		log.debug("Applying value binding...");
		ValueExpression valueExpression = this.valueBinding.getValueExpression(
				faceletsContext, Object.class);

		Object value = valueExpression.getValue(faceletsContext);
		if (value != null) {
			ValueExpression managedBeanExpression = this.managedBean
					.getValueExpression(faceletsContext, Object.class);

			/* Get the name of the new value. */
			Object bean = managedBeanExpression.getValue(faceletsContext);

			String property = this.property.getValue(faceletsContext);

			Class cl = bean.getClass();
			Class[] parameterTypes = new Class[1];
			Object[] objectValues = { value };

			if (parameterType != null && parameterType.getValue() != null) {
				String valueClass = parameterType.getValue();
				if (valueClass.equals("ITimeSelection")) {
					parameterTypes[0] = ITimeSelection.class;
				}
				else if (valueClass.equals("IUnitSelection")) {
					parameterTypes[0] = IUnitSelection.class;
				}
				else if (valueClass.equals("IStateSelection")) {
					parameterTypes[0] = IStateSelection.class;
				}
				else if (valueClass.equals("IOperatorSelection")) {
					parameterTypes[0] = IOperatorSelection.class;
				}
				else if (valueClass.equals("IUnitTypeSelection")) {
					parameterTypes[0] = IUnitTypeSelection.class;
				}
				else if (valueClass.equals("IShiftSelection")) {
					parameterTypes[0] = IShiftSelection.class;
				}
				//Check primitive Types/other types
				else if(parameterTypes[0] == null)
				{
					parameterTypes[0] = value.getClass();
				}

			} else {
				parameterTypes[0] = value.getClass();
			}

			try {
				//log.debug("Invoking Method: " + cl + ".set" + StringUtils.capitalize(property) + "(" + parameterTypes[0] + ")");
				//log.debug("With Parameter: " + objectValues[0] + " on " + bean);
				Method worker = cl.getMethod("set"
						+ StringUtils.capitalize(property), parameterTypes);
				worker.invoke(bean, objectValues);

			} catch (SecurityException e) {
				log.error("runs into", e);
			} catch (NoSuchMethodException e) {
				log.error("runs into", e);
			} catch (IllegalArgumentException e) {
				log.error("runs into", e);
			} catch (IllegalAccessException e) {
				log.error("runs into", e);
			} catch (InvocationTargetException e) {
				log.error("runs into", e);
			}
		}

	}
}
