package nl.quintor.jsf;

import java.util.HashMap;
import java.util.Map;

import javax.faces.application.ViewHandler;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.el.VariableResolver;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.servlet.http.HttpServletRequest;

import com.sun.org.apache.commons.beanutils.BeanUtils;

@SuppressWarnings({ "serial", "deprecation" })
public class URLBasedGETParameterToBeanMapperPhaseListener implements PhaseListener {
	
	private static final int INDEX_BEAN = 0;
	private static final int INDEX_PROPERTY = 1;
	
	/**
	 * List of parameters our listener should be able to map
	 */	 	
	private static final Map<String, String[]> parameters;

	static {
		parameters = new HashMap<String, String[]>();
		parameters.put("paramname1", new String[] { "beanname1", "propertyname1" });
		parameters.put("paramname2", new String[] { "beanname2", "propertyname2" });
		parameters.put("paramname3", new String[] { "beanname3", "propertyname3" });
	}

	public PhaseId getPhaseId() {
		return PhaseId.RESTORE_VIEW;
	}

	public void afterPhase(PhaseEvent arg0) {
		//
	}

	/**
	 * Extracts the GET parameters from the url (something like <code>http://www.peanutbutter.com/
	 * faces/newsitems/params/id-22234.xhtml</code>), uses the resting url to determine original view url, 
	 * map the parameters in an automated fashion and let's JSF deal with the rest.
	 */	 	
	public void beforePhase(PhaseEvent arg0) {
		FacesContext context = FacesContext.getCurrentInstance();
		HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();
		String jsfCompatibleParameterizedUrl = request.getRequestURI();
		int paramsIndex = jsfCompatibleParameterizedUrl.indexOf("/params/");
		if (paramsIndex != -1) {
			// extract original view id and a string of encoded parameters
			String originalViewid = jsfCompatibleParameterizedUrl.substring(0, paramsIndex) + ".xhtml";
			originalViewid = originalViewid.substring(originalViewid.indexOf("/pages/"));
			String encodedParameters = jsfCompatibleParameterizedUrl.substring(paramsIndex + "/params/".length());
			encodedParameters = encodedParameters.substring(0, encodedParameters.indexOf(".xhtml"));

			// process each name-value pair
			String[] params = encodedParameters.split("/");
			for (String param : params) {
				String[] paramNameValue = param.split("-");
				String paramName = paramNameValue[0];
				String paramValue = paramNameValue[1];
				mapParameter(paramName, parameters.get(paramName), paramValue);
			}
			// finally create a view with the original view id (as JSF would've done)
			ViewHandler viewHandler = context.getApplication().getViewHandler();
			UIViewRoot view = viewHandler.createView(context, originalViewid);
			context.setViewRoot(view);
		}
	}

	/**
	 * Takes a paramaeter name and value and a bean name and property, and maps the parameter onto the bean.
 	 */
	private void mapParameter(String param, String[] mapping, String paramValue) {
		try {
			final FacesContext context = FacesContext.getCurrentInstance();
			final VariableResolver resolver = context.getApplication().getVariableResolver();
			final Object bean = resolver.resolveVariable(context, mapping[INDEX_BEAN]);
			BeanUtils.setProperty(bean, mapping[INDEX_PROPERTY], paramValue);
		} catch (Exception e) {
			final String msg = "Kon GET parameter '%s' niet mappen op bean property %s.%s";
			throw new RuntimeException(String.format(msg, param, mapping[INDEX_BEAN], mapping[INDEX_PROPERTY]), e);
		}
	}
}
