/**
 * 
 */
package org.jboss.seam.jsf;

import static javax.faces.event.PhaseId.INVOKE_APPLICATION;
import static javax.faces.event.PhaseId.PROCESS_VALIDATIONS;
import static javax.faces.event.PhaseId.RENDER_RESPONSE;
import static javax.faces.event.PhaseId.RESTORE_VIEW;

import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.portlet.faces.Bridge;
import javax.portlet.faces.Bridge.PortletPhase;

import org.ajax4jsf.portlet.application.PortletStateHolder;
import org.ajax4jsf.portlet.application.PortletViewState;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.seam.contexts.Contexts;
import org.jboss.seam.contexts.FacesLifecycle;
import org.jboss.seam.core.Manager;
import org.jboss.seam.exception.Exceptions;
import org.jboss.seam.faces.FacesMessages;

/**
 * @author asmirnov
 * 
 */
public class SeamPhaseListenerWrapper implements PhaseListener {

	private final SeamPhaseListener _defaultListener;

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

	/**
	 * @param defaultListener
	 */
	public SeamPhaseListenerWrapper(PhaseListener defaultListener) {
		_defaultListener = (SeamPhaseListener) defaultListener;
	}

	/**
	 * 
	 */
	private static final long serialVersionUID = -8465467659533393697L;

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.faces.event.PhaseListener#afterPhase(javax.faces.event.PhaseEvent)
	 */
	public void afterPhase(PhaseEvent event) {
		FacesContext facesContext = event.getFacesContext();
		ExternalContext externalContext = facesContext.getExternalContext();
		Bridge.PortletPhase portletPhase = (PortletPhase) externalContext
				.getRequestMap().get(Bridge.PORTLET_LIFECYCLE_PHASE);
		if (null != portletPhase) {
			try {
				_defaultListener.raiseEventsAfterPhase(event);
				afterPortletPhase(event, facesContext, portletPhase);
			} catch (Exception e) {
				log.debug("uncaught exception, try to recovery", e);
				try {
					Exceptions.instance().handle(e);
					// A redirect occurred inside the error handler, and we are
					// in after
					// phase, so we need to clean up now as there are no more
					// after
					// phases to be run
					if (facesContext.getResponseComplete()) {
						_defaultListener.afterResponseComplete(facesContext);
					}
				} catch (Exception ehe) {
					log.error("swallowing exception", e);
				}
			}
			FacesLifecycle.clearPhaseId();

		} else {
			_defaultListener.afterPhase(event);
		}
	}

	private void afterPortletPhase(PhaseEvent event, FacesContext facesContext,
			PortletPhase portletPhase) {

		PhaseId phaseId = event.getPhaseId();
		if (phaseId == RESTORE_VIEW) {
			_defaultListener.afterRestoreView(facesContext);
		} else if (phaseId == INVOKE_APPLICATION) {
			_defaultListener.afterInvokeApplication();
		} else if (phaseId == PROCESS_VALIDATIONS) {
			_defaultListener.afterProcessValidations(facesContext);
		}

		FacesMessages.afterPhase();

		// delegate to subclass:
		_defaultListener.handleTransactionsAfterPhase(event);

		if (phaseId == RENDER_RESPONSE) {
			// writeConversationIdToResponse(
			// facesContext.getExternalContext().getResponse() );
			_defaultListener.afterRenderResponse(facesContext);
		} else if (Bridge.PortletPhase.ActionPhase.equals(portletPhase)
				&& (phaseId == INVOKE_APPLICATION
						|| facesContext.getRenderResponse() || facesContext
						.getResponseComplete())) {
			Manager manager = Manager.instance();
			manager.beforeRedirect();
			// save Seam conversation Id.
			PortletViewState windowState = PortletStateHolder.getInstance(
					facesContext).getWindowState(facesContext);	
			windowState.saveSeamConversationId(facesContext);
			_defaultListener.afterResponseComplete(facesContext);
		}

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.faces.event.PhaseListener#beforePhase(javax.faces.event.PhaseEvent)
	 */
	public void beforePhase(PhaseEvent event) {
		FacesContext facesContext = event.getFacesContext();
		ExternalContext externalContext = facesContext.getExternalContext();
		Bridge.PortletPhase portletPhase = (PortletPhase) externalContext
				.getRequestMap().get(Bridge.PORTLET_LIFECYCLE_PHASE);
		if (null != portletPhase) {
			log.trace("before phase: " + event.getPhaseId());

			FacesLifecycle.setPhaseId(event.getPhaseId());

			try {
				beforePortletPhase(event, facesContext, portletPhase);
				_defaultListener.raiseEventsBeforePhase(event);
			} catch (Exception e) {
				log.debug("uncaught exception, try to recovery", e);
				try {
					Exceptions.instance().handle(e);
				} catch (Exception ehe) {
					log.error("swallowing exception", e);
				}
			}
		} else {
			_defaultListener.beforePhase(event);
		}
	}

	private void beforePortletPhase(PhaseEvent event,
			FacesContext facesContext, PortletPhase portletPhase) {

		boolean notInitialised=false;
		if (event.getPhaseId() == RESTORE_VIEW ) {
			_defaultListener.beforeRestoreView(facesContext);
		}

		if (event.getPhaseId() == RENDER_RESPONSE && !Contexts.isApplicationContextActive()) {
			_defaultListener.beforeRestoreView(facesContext);
			notInitialised = true;
		}
		// delegate to subclass:
		_defaultListener.handleTransactionsBeforePhase(event);

		if (event.getPhaseId() == RENDER_RESPONSE) {
			if(notInitialised){
				_defaultListener.afterRestoreView(facesContext);
			}
			_defaultListener.beforeRenderResponse(facesContext);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.faces.event.PhaseListener#getPhaseId()
	 */
	public PhaseId getPhaseId() {
		return _defaultListener.getPhaseId();
	}

}
