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

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.faces.FacesException;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseListener;
import javax.faces.lifecycle.Lifecycle;
import javax.portlet.PortletRequest;

import org.ajax4jsf.portlet.application.PortletStateHolder;
import org.ajax4jsf.portlet.application.PortletViewState;

/**
 * @author asmirnov
 *
 */
public class PortletLifecycle extends Lifecycle {
	
	//List<PhaseListener>
	private final List _phaseListeners = new ArrayList();
	
	private final LifecyclePhase executePhase = new RestoreViewPhase(this);
	
	private final LifecyclePhase renderPhase = new RenderResponsePhase(this);
	
	private PhaseListener[] _phaseListenersArray = null;
    
    public PortletLifecycle() {
    }

    /**
     * @param listener
     * @see javax.faces.lifecycle.Lifecycle#addPhaseListener(javax.faces.event.PhaseListener)
     */
    public void addPhaseListener(PhaseListener listener) {
		if(null == listener){
			throw new NullPointerException("PhaseListener for add to Lifecycle can't be null");
		}
		synchronized (_phaseListeners) {
			_phaseListeners.add(listener);
			_phaseListenersArray = null;
		}
    }

 
    /**
	 * @param listener
	 * @see javax.faces.lifecycle.Lifecycle#removePhaseListener(javax.faces.event.PhaseListener)
	 */
	public void removePhaseListener(PhaseListener listener) {
		if(null == listener){
			throw new NullPointerException("PhaseListener for remove from Lifecycle can't be null");
		}
		synchronized (_phaseListeners) {
			_phaseListeners.remove(listener);
			_phaseListenersArray = null;
		}
	}

	/**
	 * @return
	 * @see javax.faces.lifecycle.Lifecycle#getPhaseListeners()
	 */
	public PhaseListener[] getPhaseListeners() {
		if (_phaseListenersArray == null) {
			// Lazy creation of a working copy listeners Array.
			synchronized (_phaseListeners) {
				_phaseListenersArray = (PhaseListener[]) _phaseListeners.toArray(new PhaseListener[_phaseListeners.size()]);				
			}			
		}
	
		return _phaseListenersArray;
	}

	/**
     * @param context
     * @throws FacesException
     * @see javax.faces.lifecycle.Lifecycle#execute(javax.faces.context.FacesContext)
     */
    public void execute(FacesContext context) throws FacesException {
	    executePhase.execute(context);
	    // TODO - in the portlet mode, save state
	    Object request = context.getExternalContext().getRequest();
	    if(request instanceof PortletRequest){
	    	context.getApplication().getStateManager().saveSerializedView(context);
	    }
	    // TODO - save request scope variables and Faces Messages.
    	PortletViewState windowState = PortletStateHolder.getInstance(context).getWindowState(context);
    	windowState.saveMessages(context);
    	HashMap requestScopeBeans = new HashMap();
    	for (Iterator iterator = context.getExternalContext().getRequestMap().entrySet().iterator(); iterator.hasNext();) {	
			Entry entry = (Entry) iterator.next();
			String paramName = entry.getKey().toString();
			Object bean = entry.getValue();
			if(!paramName.contains(".") && bean instanceof Serializable){
				requestScopeBeans.put(paramName, bean);
			}
		}
    	if(requestScopeBeans.size()>0){
    		windowState.setRequestScopeBeans(requestScopeBeans);
    	}
    }

    /**
     * @param context
     * @throws FacesException
     * @see javax.faces.lifecycle.Lifecycle#render(javax.faces.context.FacesContext)
     */
    public void render(FacesContext context) throws FacesException {
	    // TODO - in the portlet mode, restore state, request scope variables and Faces Messages.
	    Object request = context.getExternalContext().getRequest();
	    if(request instanceof PortletRequest){
	    	executePhase.executePhase(context);
	    	PortletViewState windowState = PortletStateHolder.getInstance(context).getWindowState(context);
	    	windowState.restoreMessages(context);
	    	Map requestScopeBeans = windowState.getRequestScopeBeans();
	    	if(null != requestScopeBeans){
	    		context.getExternalContext().getRequestMap().putAll(requestScopeBeans);
	    	}
	    }
		renderPhase.execute(context);
    }
    
}
