/**
 * License Agreement.
 *
 * Rich Faces - Natural Ajax for Java Server Faces (JSF)
 *
 * Copyright (C) 2007 Exadel, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 */
/*

 * Created on 25.09.2004

 *

 * Copyright 1999-2004 The Apache Software Foundation.

 * 

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 * 

 *      http://www.apache.org/licenses/LICENSE-2.0

 * 

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */
package org.ajax4jsf.portlet.context;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.el.ELContext;
import javax.faces.FactoryFinder;
import javax.faces.application.Application;
import javax.faces.application.ApplicationFactory;
import javax.faces.application.FacesMessage;
import javax.faces.application.FacesMessage.Severity;
import javax.faces.component.UIViewRoot;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseStream;
import javax.faces.context.ResponseWriter;
import javax.faces.lifecycle.Lifecycle;
import javax.faces.render.RenderKit;
import javax.faces.render.RenderKitFactory;
import javax.portlet.PortletResponse;
import javax.portlet.faces.component.PortletNamingContainer;

/**
 * 
 * Implementation for <code>FacesContext</code> in Portlet Environment.
 * 
 * @author shura
 * 
 * 
 */
public class FacesContextImpl extends FacesContext {

	private boolean released = true;

	private boolean renderResponse;

	private boolean responseComplete;

	private Application application;

	private UIViewRoot viewRoot;

	private Map messages;

	private AbstractExternalContext externalContext;

	private Lifecycle lifecycle;

	private ResponseWriter responseWriter;

	private ResponseStream responseStream;

	private ELContext elContext;

	public FacesContextImpl(AbstractExternalContext externalContext,
			Lifecycle lifecycle) {
		super();
		setCurrentInstance(this);
		this.externalContext = externalContext;
		this.lifecycle = lifecycle;
		this.released = false;
	}

	/*
	 * 
	 * (non-Javadoc)
	 * 
	 * 
	 * 
	 * @see org.apache.cocoon.components.faces.context.CocoonFacesContext#getLifecycle()
	 * 
	 */
	public Lifecycle getLifecycle() {
		checkReleased();
		return this.lifecycle;
	}

	public ELContext getELContext() {
		checkReleased();
		if (this.elContext == null) {
			Application application = getApplication();
			this.elContext = new ELContextImpl(application.getELResolver());
			this.elContext.putContext(FacesContext.class, this);
			UIViewRoot root = getViewRoot();
			if (null != root) {
				this.elContext.setLocale(root.getLocale());
			}
		}
		return this.elContext;
	}

	/*
	 * 
	 * (non-Javadoc)
	 * 
	 * 
	 * 
	 * @see javax.faces.context.FacesContext#getExternalContext()
	 * 
	 */
	public ExternalContext getExternalContext() {
		checkReleased();
		return this.externalContext;
	}

	/**
	 * 
	 * @param externalContext
	 * 
	 * The externalContext to set.
	 * 
	 */
	// public void setExternalContext(ExternalContext externalContext) {
	// this.externalContext = externalContext;
	// }
	/*
	 * 
	 * (non-Javadoc)
	 * 
	 * 
	 * 
	 * @see javax.faces.context.FacesContext#getResponseStream()
	 * 
	 */
	public ResponseStream getResponseStream() {
		checkReleased();
		return this.responseStream;
	}

	/*
	 * 
	 * (non-Javadoc)
	 * 
	 * 
	 * 
	 * @see javax.faces.context.FacesContext#setResponseStream(javax.faces.context.ResponseStream)
	 * 
	 */
	public void setResponseStream(ResponseStream responseStream) {
		checkReleased();
		this.responseStream = responseStream;
	}

	/*
	 * 
	 * (non-Javadoc)
	 * 
	 * 
	 * 
	 * @see javax.faces.context.FacesContext#getResponseWriter()
	 * 
	 */
	public ResponseWriter getResponseWriter() {
		checkReleased();
		return this.responseWriter;
	}

	/*
	 * 
	 * (non-Javadoc)
	 * 
	 * 
	 * 
	 * @see javax.faces.context.FacesContext#setResponseWriter(javax.faces.context.ResponseWriter)
	 * 
	 */
	public void setResponseWriter(ResponseWriter responseWriter) {
		checkReleased();
		this.responseWriter = responseWriter;
	}

	/*
	 * 
	 * (non-Javadoc)
	 * 
	 * 
	 * 
	 * @see javax.faces.context.FacesContext#release()
	 * 
	 */
	public void release() {
		// Release this and default instances...
		this.released = true;
		FacesContext.setCurrentInstance(null);
		// defaultContext = null;
		this.lifecycle = null;
		this.externalContext = null;
		this.responseWriter = null;
		this.application = null;
		this.viewRoot = null;
		this.messages = null;
		this.responseWriter = null;
	}

	public Application getApplication() {
		checkReleased();
		if (this.application == null) {
			ApplicationFactory aFactory = (ApplicationFactory) FactoryFinder
					.getFactory(FactoryFinder.APPLICATION_FACTORY);
			this.application = aFactory.getApplication();
		}
		return this.application;
	}

	public Iterator getClientIdsWithMessages() {
		checkReleased();
		if (this.messages == null) {
			return Collections.EMPTY_LIST.iterator();
		} else {
			return this.messages.keySet().iterator();
		}
	}

	public Severity getMaximumSeverity() {
		checkReleased();
		Severity max = null;
		Iterator messages = getMessages();
		while (messages.hasNext()) {
			FacesMessage msg = (FacesMessage) messages.next();
			Severity severity = msg.getSeverity();
			if ((null == max) || (max.getOrdinal() < severity.getOrdinal())) {
				max = severity;
			}
		}
		return max;
	}

	public Iterator getMessages() {
		checkReleased();
		if (this.messages == null) {
			return Collections.EMPTY_LIST.iterator();
		}
		List messages = new ArrayList();
		for (Iterator i = this.messages.values().iterator(); i.hasNext();) {
			final List list = (List) i.next();
			messages.addAll(list);
		}
		if (messages.size() > 0) {
			return messages.iterator();
		}
		return Collections.EMPTY_LIST.iterator();
	}

	public Iterator getMessages(String clientID) {
		checkReleased();
		if (this.messages != null) {
			final List list = (List) this.messages.get(clientID);
			if (list != null) {
				return list.iterator();
			}
		}
		return Collections.EMPTY_LIST.iterator();
	}

	public RenderKit getRenderKit() {
		checkReleased();
		UIViewRoot viewRoot = getViewRoot();
		if (viewRoot == null) {
			return null;
		}
		String renderKitId = viewRoot.getRenderKitId();
		if (renderKitId == null) {
			return null;
		} else {
			RenderKitFactory rkFactory = (RenderKitFactory) FactoryFinder
					.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
			return rkFactory.getRenderKit(this, renderKitId);
		}
	}

	public boolean getRenderResponse() {
		checkReleased();
		return this.renderResponse;
	}

	public boolean getResponseComplete() {
		checkReleased();
		return this.responseComplete;
	}

	public UIViewRoot getViewRoot() {
		checkReleased();
		return this.viewRoot;
	}

	public void setViewRoot(UIViewRoot viewRoot) {
		checkReleased();
		this.viewRoot = viewRoot;
		// JSR-301 PLT 6.1.2 FacesContext
		if (viewRoot instanceof PortletNamingContainer) {
			Object response = getExternalContext().getResponse();
			if (response instanceof PortletResponse) {
				PortletResponse portletResponse = (PortletResponse) response;
				portletResponse.setProperty("X-JAVAX-PORTLET-IS-NAMESPACED",
						"true");
			}
		}
	}

	public void addMessage(String clientID, FacesMessage message) {
		checkReleased();
		if (message == null) {
			throw new NullPointerException("Message can't be null");
		}
		if (this.messages == null) {
			this.messages = new HashMap();
		}
		List list = (List) this.messages.get(clientID);
		if (list == null) {
			list = new ArrayList();
			this.messages.put(clientID, list);
		}
		list.add(message);
	}

	public void renderResponse() {
		checkReleased();
		this.renderResponse = true;
	}

	public void responseComplete() {
		checkReleased();
		if (externalContext.isHasNavigationRedirect()) {
			this.renderResponse = true;
		} else {
			this.responseComplete = true;
		}
	}

	private void checkReleased() {
		if (this.released) {
			throw new IllegalStateException("Context is released.");
		}
	}
}