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

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.faces.FacesException;
import javax.faces.context.FacesContext;
import javax.portlet.PortletSession;
import javax.portlet.PortletSessionUtil;
import javax.portlet.RenderResponse;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * @author asmirnov
 * 
 */
public class ServletContextImpl extends AbstractExternalContext {
    
    private static final Log _log = LogFactory.getLog(ServletContextImpl.class);
    
    private String namespace;

    /**
     * @param context
         * @param request
         * @param response
         */
    public ServletContextImpl(ServletContext context, HttpServletRequest request, HttpServletResponse response) {
	super(context, request, response);
    }
    
    public void setResponseCharacterEncoding(String encoding) {
    	
    	getHttpResponse().setCharacterEncoding(encoding);
    }
    
    public String getResponseCharacterEncoding() {
    	return getHttpResponse().getCharacterEncoding();
    }
    
    
    public String getResponseContentType() {
    	return getHttpResponse().getContentType();
    }

    public void setRequestCharacterEncoding(String encoding)
    		throws UnsupportedEncodingException {
    	getHttpRequest().setCharacterEncoding(encoding);
    }
    
    public String getRequestCharacterEncoding() {
    	return getHttpRequest().getCharacterEncoding();
    }
    
    
    public String getRequestContentType() {
    	return getHttpRequest().getContentType();
    }

    private HttpServletRequest getHttpRequest() {
	return (HttpServletRequest) getRequest();
    }

    private ServletContext getServletContext() {
	return (ServletContext) getContext();
    }

    private HttpServletResponse getHttpResponse() {
	return (HttpServletResponse) getResponse();
    }

    protected String getNamespace() {
	if (null == namespace) {
	    Object requestParameter = getRequestParameter(NAMESPACE_PARAMETER);
	    if (null != requestParameter) {
		namespace = (String) requestParameter;
		if (_log.isDebugEnabled()) {
		    _log.debug("Namespace for a portlet instance is "+namespace);
		}
	    } else {
		throw new FacesException("AJAX call to portlet without namespace parameter");
	    }
	}
	return namespace;
    }

    /*
         * (non-Javadoc)
         * 
         * @see javax.faces.context.ExternalContext#getInitParameter(java.lang.String)
         */
    public String getInitParameter(String name) {
	return getServletContext().getInitParameter(name);
    }

    protected Enumeration getInitParametersNames() {
	return getServletContext().getInitParameterNames();
    }

    /*
         * (non-Javadoc)
         * 
         * @see javax.faces.context.ExternalContext#getResource(java.lang.String)
         */
    public URL getResource(String path) throws MalformedURLException {
	return getServletContext().getResource(path);
    }

    /*
         * (non-Javadoc)
         * 
         * @see javax.faces.context.ExternalContext#getResourceAsStream(java.lang.String)
         */
    public InputStream getResourceAsStream(String path) {
	return getServletContext().getResourceAsStream(path);
    }

    /*
         * (non-Javadoc)
         * 
         * @see javax.faces.context.ExternalContext#getResourcePaths(java.lang.String)
         */
    public Set getResourcePaths(String path) {
	return getServletContext().getResourcePaths(path);
    }

    protected Object getContextAttribute(String name) {
	return getServletContext().getAttribute(name);
    }

    protected Enumeration getContextAttributeNames() {
	return getServletContext().getAttributeNames();
    }

    protected void setContextAttribute(String name, Object value) {
	getServletContext().setAttribute(name, value);
    }

    public String getAuthType() {
	return getHttpRequest().getAuthType();
    }

    public String getRemoteUser() {
	return getHttpRequest().getRemoteUser();
    }

    public String getRequestContextPath() {
	return getHttpRequest().getContextPath();
    }

    public String getRequestPathInfo() {
	return getHttpRequest().getPathInfo();
    }

    public String getRequestServletPath() {
	return getHttpRequest().getServletPath();
    }

    protected Enumeration enumerateRequestParameterNames() {
	return getHttpRequest().getParameterNames();
    }

    protected Object getRequestAttribute(String name) {
	return getHttpRequest().getAttribute(name);
    }

    protected Enumeration getRequestAttributeNames() {
	return getHttpRequest().getAttributeNames();
    }

    protected Object getRequestParameterValues(String name) {
	return getHttpRequest().getParameterValues(name);
    }

    protected Object getRequestHeader(String name) {
	return getHttpRequest().getHeader(name);
    }

    protected Enumeration getRequestHeaderNames() {
	return getHttpRequest().getHeaderNames();
    }

    protected String[] getRequestHeaderValues(String name) {
	Enumeration values = getHttpRequest().getHeaders(name);
	ArrayList valuesList = new ArrayList();
	while (values.hasMoreElements()) {
	    valuesList.add(values.nextElement());
	}
	return (String[]) valuesList.toArray(EMPTY_STRING_ARRAY);
    }

    protected Object getRequestParameter(String name) {
	return getHttpRequest().getParameter(name);
    }
    
    private String sessionPrefix;
    
    private String getSessionPrefix(){
	if (sessionPrefix == null) {
	    HttpSession session = getHttpRequest().getSession(false);
	    String namespase = getNamespace();
	    if(null == namespase || null == session){
		throw new FacesException("JSF request called without portlet namespace parameter");
	    }
	    Enumeration attributeNames = session.getAttributeNames();
	    while (attributeNames.hasMoreElements() && null == sessionPrefix) {
		String name = (String) attributeNames.nextElement();
		Object attribute = session.getAttribute(name);
		if (PortletSessionUtil.decodeScope(name) == PortletSession.PORTLET_SCOPE
			&& PortletSessionUtil.decodeAttributeName(name).equals(
				NAMESPACE_PARAMETER)
			&& namespase.equals(attribute)) {
		    sessionPrefix = name.substring(0, name.length()-NAMESPACE_PARAMETER.length());
		    if (_log.isDebugEnabled()) {
			_log.debug("Prefix for a PORTLET_SCOPE session attributes: "+sessionPrefix);
		    }
		}
	    }
	    if(null == sessionPrefix){
		throw new FacesException("Prefix for attributes in portlet session scope not found");
	    }
	}
	return sessionPrefix;
    }

    protected Object getSessionAttribute(String name) {
	return getHttpRequest().getSession(true).getAttribute(getSessionPrefix()+name);
    }

    protected Enumeration getSessionAttributeNames() {
	return new SessionAttributesNames(getHttpRequest().getSession(true).getAttributeNames(),getSessionPrefix());
    }

    protected void removeSessionAttribute(String name) {
        getHttpRequest().getSession(true).removeAttribute(getSessionPrefix()+name);
    }

    protected void setSessionAttribute(String name, Object value) {
        getHttpRequest().getSession(true).setAttribute(getSessionPrefix()+name, value);
    }

    protected void removeContextAttribute(String name) {
	getHttpRequest().getSession(true).removeAttribute(name);
    }

    protected void removeRequestAttribute(String name) {
	getHttpRequest().removeAttribute(name);
    }

    protected void setRequestAttribute(String name, Object value) {
	getHttpRequest().setAttribute(name, value);
    }

    public void dispatch(String path) throws IOException {
        RequestDispatcher requestDispatcher = getHttpRequest().getRequestDispatcher(path);
            if (requestDispatcher == null) {
                (getHttpResponse()).
                        sendError(HttpServletResponse.SC_NOT_FOUND);
                return;
            }
            try {
                requestDispatcher.forward(getHttpRequest(), getHttpResponse());
            } catch (IOException ioe) {
                throw ioe;
            } catch (ServletException se) {
                throw new FacesException(se);
            }

    }

    public String encodeActionURL(String url) {
	return getHttpResponse().encodeURL(url);
    }

    public String encodeResourceURL(String url) {
	return getHttpResponse().encodeURL(url);
    }

    public Locale getRequestLocale() {
	return getHttpRequest().getLocale();
    }

    public Iterator getRequestLocales() {
	return new EnumerationIterator(getHttpRequest().getLocales());
    }

    public Object getSession(boolean create) {
	HttpSession session = getHttpRequest().getSession(create);
	if(null != session){
	    session = new ServletSessionWrapper(session,getSessionPrefix());
	}
	return session;
    }

    public Principal getUserPrincipal() {
	return getHttpRequest().getUserPrincipal();
    }

    public boolean isUserInRole(String role) {
	return getHttpRequest().isUserInRole(role);
    }

    public void log(String message) {
	getServletContext().log(message);
    }

    public void log(String message, Throwable exception) {
	getServletContext().log(message, exception);
    }

    public void redirect(String url) throws IOException {
	getHttpResponse().sendRedirect(url);
        FacesContext.getCurrentInstance().responseComplete();
    }
}
