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

import javax.faces.FacesException;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.faces.lifecycle.Lifecycle;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * @author asmirnov
 *
 */
public abstract class LifecyclePhase {
	
	
	private static final Log _log = LogFactory.getLog(LifecyclePhase.class);
	
	
	private final Lifecycle _lifecycle;
	
	public LifecyclePhase(Lifecycle lifecycle) {
		this._lifecycle = lifecycle;
	}
	

	/**
	 * Execute phase methods with 
	 * @param context
	 */
	public void execute(FacesContext context){
		int listenerToCall = 0;
		PhaseEvent event = new PhaseEvent(context,getPhaseId(),getLifecycle());
		PhaseListener[] listeners = getLifecycle().getPhaseListeners();
		// Execute listeners before phase.
		try {
			for(;listenerToCall<listeners.length;listenerToCall++){
				PhaseListener listener = listeners[listenerToCall];
				if(isExecutableListener(listener)){
					listener.beforePhase(event);
				}
			}
		} catch (Exception e) {
			_log.error("Error to execute beforePhase "+getPhaseId().toString()+ " method for listener", e);
		}
		try {
			executePhase(context);
		} catch (Exception e) {
			_log.error("Error execute phase "+getPhaseId().toString());
			if(PhaseId.RENDER_RESPONSE.compareTo(getPhaseId())==0){
				throw new FacesException(e);
			}
		} finally {
			// Execute listeners after phase, in the reverse order of the before phase.
			try {
				for(listenerToCall--;listenerToCall>=0;listenerToCall--){
					PhaseListener listener = listeners[listenerToCall];
					if(isExecutableListener(listener)){
						listener.afterPhase(event);
					}
				}
			} catch (Exception e) {
				_log.error("Error to execute afterPhase "+getPhaseId().toString()+ " method for listener", e);
			}
			
		}
		if(!context.getRenderResponse() && !context.getResponseComplete()){
			executeNextPhase(context);
		}
	}

	/**
	 * @param context
	 */
	protected abstract void executeNextPhase(FacesContext context);


	/**
	 * @param context
	 */
	public abstract void executePhase(FacesContext context);


	/**
	 * @return
	 */
	protected  abstract PhaseId getPhaseId();

	private boolean isExecutableListener(PhaseListener listener) {
		PhaseId phaseId = listener.getPhaseId();
		return 0 == getPhaseId().compareTo(phaseId) || 0 == PhaseId.ANY_PHASE.compareTo(phaseId);
	}


	/**
	 * @return the lifecycle
	 */
	public Lifecycle getLifecycle() {
		return _lifecycle;
	}

}
