/**
 * 
 */
package org.ajax4jsf.portlet.lifecycle;

import java.util.Iterator;

import javax.faces.FactoryFinder;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
import javax.faces.event.PhaseId;
import javax.faces.lifecycle.Lifecycle;
import javax.faces.render.RenderKit;
import javax.faces.render.RenderKitFactory;
import javax.servlet.ServletRequest;

import org.ajax4jsf.portlet.application.PortletStateHolder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * @author asmirnov
 * 
 */
public class RestoreViewPhase extends LifecyclePhase {

	private static final Log _log = LogFactory.getLog(RestoreViewPhase.class);

	private final LifecyclePhase nextPhase;

	/**
	 * @param lifecycle
	 */
	public RestoreViewPhase(Lifecycle lifecycle) {
		super(lifecycle);
		nextPhase = new ApplyValuesPhase(lifecycle);
	}

	public void execute(FacesContext context) {
		context.getApplication().getViewHandler().initView(context);
		super.execute(context);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.ajax4jsf.portlet.lifecycle.LifecyclePhase#executeNextPhase(javax.faces.context.FacesContext,
	 *      javax.faces.event.PhaseListener[])
	 */
	protected void executeNextPhase(FacesContext context) {
		nextPhase.execute(context);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.ajax4jsf.portlet.lifecycle.LifecyclePhase#executePhase(javax.faces.context.FacesContext)
	 */
	public void executePhase(FacesContext context) {
		// Check pre-created ViewRoot.
		UIViewRoot viewRoot = context.getViewRoot();
		if (null != viewRoot) {
			if (_log.isDebugEnabled()) {
				_log.debug("Found created UIViewRoot in facesContext");
			}
			viewRoot.setLocale(context.getExternalContext().getRequestLocale());
			processBindings(context, viewRoot);
		} else {
			// Restore or Create new view.
			String viewId = calculateViewId(context);
			ViewHandler viewHandler = context.getApplication().getViewHandler();
			if (isFacesRequest(context)) {
				// TODO - check for a null and throw exception.
				viewRoot = viewHandler.restoreView(context, viewId);
				if (null != viewRoot) {
					processBindings(context, viewRoot);
				}
			} else {
				viewRoot = viewHandler.createView(context, viewId);
			}
			context.setViewRoot(viewRoot);
		}
	}

	private boolean isFacesRequest(FacesContext context) {
		// Check request for a "postback"
		String renderKitId = context.getApplication().getViewHandler()
				.calculateRenderKitId(context);
		return getRenderKit(context, renderKitId).getResponseStateManager()
				.isPostback(context);
	}

	private RenderKit getRenderKit(FacesContext context, String renderKitId) {
		// TODO - cache renderKitFactory instance.
		RenderKitFactory renderKitFactory = (RenderKitFactory) FactoryFinder
				.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
		return renderKitFactory.getRenderKit(context, renderKitId);
	}

	private String calculateViewId(FacesContext context) {
		String viewId;
		if (context.getExternalContext().getRequest() instanceof ServletRequest) {
			viewId = context.getExternalContext().getRequestPathInfo();
			if (null == viewId) {
				viewId = context.getExternalContext().getRequestServletPath();
			}

		} else {
			viewId = PortletStateHolder.getInstance(context).getViewId(context);
		}
		return viewId;
	}

	private void processBindings(FacesContext context, UIComponent component) {
		ValueBinding binding = component.getValueBinding("binding");
		if (null != binding) {
			binding.setValue(context, component);
		}
		for (Iterator iterator = component.getFacetsAndChildren(); iterator
				.hasNext();) {
			UIComponent child = (UIComponent) iterator.next();
			processBindings(context, child);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.ajax4jsf.portlet.lifecycle.LifecyclePhase#getPhaseId()
	 */
	protected PhaseId getPhaseId() {
		return PhaseId.RESTORE_VIEW;
	}

}
