/**
 * 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.HashMap;
import java.util.Collections;
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;

/**
 * 
 * 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 ExternalContext externalContext;

    private Lifecycle lifecycle;

    private ResponseWriter responseWriter;

    private ResponseStream responseStream;

    private ELContext elContext;

    public FacesContextImpl(ExternalContext 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;
    }

    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();
	this.responseComplete = true;
    }

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