package nl.quintor.jsf;

import java.io.IOException;
import java.util.Locale;

import javax.el.ExpressionFactory;
import javax.el.ValueExpression;
import javax.faces.FacesException;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;

import nl.quintor.jsf.util.GenericUtils;

/**
 * This ViewHandler decorates the Facelets ViewHandler (which is final) so that we  
 * can intercept the view resolution and generate GET urls with embedded parameters.
 */ 
public class DynamicGETurlGeneratingViewHandler extends ViewHandler {

	public static final String COUPLINGSIGN = "-";
	public static final String PARAMETERINDICATOR = "?";
	
	/**
	 * The decoratee, which handles everything but the view resolution.
	 */	 	
	private com.sun.facelets.FaceletViewHandler faceletViewHandler;

	public DynamicGETurlGeneratingViewHandler(ViewHandler parent) {
		faceletViewHandler = new com.sun.facelets.FaceletViewHandler(parent);
	}

	/**
	 * Generates a url with the GET parameters embedded (something like 
	 * <code>http://www.peanutbutter.com/faces/newsitems/params/id-22234.xhtml</code>) 	 
	 */	 	
	@Override
	public String getActionURL(final FacesContext context, final String viewId) {
		String resolvedViewId = solveValueBindings(context, viewId);
		String result = faceletViewHandler.getActionURL(context, resolvedViewId);
		int queryStart = result.indexOf(PARAMETERINDICATOR);
		if (result.indexOf(PARAMETERINDICATOR) != -1) {
			result = convertToJsfCompatibleUrl(result.substring(0, queryStart), result.substring(queryStart + 1));
		}
		return result;
	}

	private String convertToJsfCompatibleUrl(String viewid, String parameters) {
		return viewid.replace(".xhtml", "/params/" + parameters + ".xhtml");
	}

	/**
	 * Solves all valuebindings in the given String, using a 
	 * <code>ExpressionFactory</code> from the <code>FacesContext</code>.
	 * 
	 * @param context The context with the resourcepool to solve expressions with.
	 * @param input The inputstring with optional valuebindings.
	 * @return A new String with all valuebindings solved.
	 */
	private String solveValueBindings(final FacesContext context, final String input) {
		// identify all valuebindings and dynamically replace each one with its own result
		String resolvedViewId = GenericUtils.replaceStrings("[\\#\\$]\\{.*\\}", input, new GenericUtils.Replacer() {
			public String solve(String oldValue) {
				ExpressionFactory expressionFactory = context.getApplication().getExpressionFactory();
				ValueExpression expression = expressionFactory.createValueExpression(context.getELContext(), oldValue, String.class);
				return expression.getValue(context.getELContext()).toString();
			}
		});
		return resolvedViewId;
	}

	/*
	 * overridden non-abstract methods
	 */

	@Override
	public void initView(FacesContext context)
			throws FacesException {
		faceletViewHandler.initView(context);
	}

	@Override
	public String calculateCharacterEncoding(FacesContext context) {
		return faceletViewHandler.calculateCharacterEncoding(context);
	}

	/*
	 * implemented abstract methods
	 */

	@Override
	public Locale calculateLocale(FacesContext context) {
		return faceletViewHandler.calculateLocale(context);
	}

	@Override
	public String calculateRenderKitId(FacesContext context) {
		return faceletViewHandler.calculateRenderKitId(context);
	}

	@Override
	public UIViewRoot createView(FacesContext context, String viewId) {
		return faceletViewHandler.createView(context, viewId);
	}

	@Override
	public String getResourceURL(FacesContext context, String path) {
		return faceletViewHandler.getResourceURL(context, path);
	}

	@Override
	public void renderView(FacesContext context, UIViewRoot viewRoot)
			throws IOException, FacesException {
		faceletViewHandler.renderView(context, viewRoot);
	}

	@Override
	public UIViewRoot restoreView(FacesContext context, String viewId) {
		return faceletViewHandler.restoreView(context, viewId);
	}

	@Override
	public void writeState(FacesContext context)
			throws IOException {
		faceletViewHandler.writeState(context);
	}
}

