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

import javax.faces.FacesException;
import javax.faces.FactoryFinder;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.context.FacesContextFactory;
import javax.faces.lifecycle.Lifecycle;
import javax.faces.lifecycle.LifecycleFactory;
import javax.faces.webapp.FacesServlet;
import javax.portlet.PortletMode;
import javax.portlet.PortletRequest;
import javax.portlet.RenderResponse;
import javax.servlet.ServletResponse;

import org.ajax4jsf.portlet.context.AbstractExternalContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * @author asmirnov
 * 
 */
public abstract class AbstractAjaxBridge {
	protected static final Log log = LogFactory.getLog(AbstractAjaxBridge.class);

	/**
	 * <p>
	 * The key used to store/retrive the PortletConfig object.
	 * </p>
	 */
	public static final String PORTLET_CONFIG = "javax.portlet.PortletConfig";

	private Lifecycle lifecycle;

	private FacesContextFactory facesContextFactory;

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.portlet.GenericPortlet#init(javax.portlet.PortletConfig)
	 */
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.ajax4jsf.portlet.Brige#init(javax.portlet.PortletConfig)
	 */
	public void init() throws FacesException {
			// get faces lifecycle instance. Name of the Lifecycle can be
			// changed by the init parameter, as described in the JSR 301 PLT
			// 3.2
			LifecycleFactory factory = (LifecycleFactory) FactoryFinder
					.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
			String lifecycleId = getInitParameter(FacesServlet.LIFECYCLE_ID_ATTR);
			if (null == lifecycleId) {
					lifecycleId = LifecycleFactory.DEFAULT_LIFECYCLE;
			}
			if (log.isDebugEnabled()) {
				log.debug("Create instance of a JSF lifecycle " + lifecycleId);
			}
			this.lifecycle = factory.getLifecycle(lifecycleId);
			// get faces context factory instance
			this.facesContextFactory = (FacesContextFactory) FactoryFinder
					.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
	}

	protected abstract String getInitParameter(String lifecycleIdAttr);

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.portlet.GenericPortlet#destroy()
	 */
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.ajax4jsf.portlet.Brige#destroy()
	 */
	public void destroy() {
		this.lifecycle = null;
		this.facesContextFactory = null;
	}

	/**
	 * Get currenf JSF lifecycle instance.
	 * 
	 * @return
	 */
	protected Lifecycle getLifecycle() {
		return this.lifecycle;
	}

	/**
	 * Create new faces context instance.
	 * 
	 * @param request
	 * @param response
	 * @return new instance of faces context.
	 */
	protected FacesContext getFacesContext(Object request,
			Object response) {
		FacesContext facesContext = this.facesContextFactory.getFacesContext(
				getContext(), request, response,
				getLifecycle());
		return facesContext;
	}

	protected abstract Object getContext();

	protected void execute(FacesContext context) throws FacesException {
		getLifecycle().execute(context);
	}

	protected void render(FacesContext context) throws FacesException {
		getLifecycle().render(context);
	}


	/**
	 * @param facesContext
	 * @return
	 */
	public static String getPortletNamespace(FacesContext facesContext) {
		ExternalContext externalContext = facesContext.getExternalContext();
		Object response = externalContext.getResponse();
		String namespace = null;
		if (response instanceof RenderResponse) {
			namespace = ((RenderResponse) response).getNamespace();
		} else if (response instanceof ServletResponse) {
			namespace = (String) externalContext.getRequestParameterMap().get(
					AbstractExternalContext.NAMESPACE_PARAMETER);
		} else {
			throw new IllegalStateException(
					"portlet Namespace not availible at this phase");
		}
		return namespace;
	}

	/**
	 * @param facesContext
	 * @return
	 */
	public static PortletMode getPortletMode(FacesContext facesContext) {
		ExternalContext externalContext = facesContext.getExternalContext();
		Object request = externalContext.getRequest();
		PortletMode mode = null;
		if (request instanceof PortletRequest) {
			mode = ((PortletRequest) request).getPortletMode();
		} else if (null != externalContext.getRequestParameterMap().get(
				AbstractExternalContext.PORTLET_MODE_PARAMETER)) {
			String modeName = (String) externalContext.getRequestParameterMap()
					.get(AbstractExternalContext.PORTLET_MODE_PARAMETER);
			if (null != modeName) {
				mode = new PortletMode(modeName);
			}
		}
		return mode;
	}

}
