/**
 * 
 */
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.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import javax.faces.FacesException;
import javax.faces.context.FacesContext;
import javax.portlet.PortletSession;
import javax.portlet.PortletSessionUtil;
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.ajax4jsf.context.AjaxContext;
import org.ajax4jsf.portlet.AjaxPortletBridge;
import org.ajax4jsf.portlet.application.PortletStateHolder;
import org.ajax4jsf.portlet.application.PortletViewState;
import org.ajax4jsf.portlet.application.PortletStateHolder.WindowIDRetriver;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * @author asmirnov
 * 
 */
public class ServletExternalContextImpl extends AbstractExternalContext {

	private static final Log _log = LogFactory.getLog(ServletExternalContextImpl.class);

	private String namespace;

	/**
	 * @param context
	 * @param request
	 * @param response
	 */
	public ServletExternalContextImpl(ServletContext context,
			HttpServletRequest request, HttpServletResponse response) {
		super(context, request, response);
		ArrayList excludedAttributes = Collections.list(request
				.getAttributeNames());
		request.setAttribute(EXCLUDED_PARAMETERS_ATTRIBUTE, excludedAttributes);
		namespace="";
		sessionPrefix="";
		PortletStateHolder portletStateHolder = (PortletStateHolder) context
				.getAttribute(PortletStateHolder.STATE_HOLDER);
		if (null != portletStateHolder) {
			String stateId = request
					.getParameter(PortletStateHolder.STATE_ID_PARAMETER);
			if (null != stateId) {
				PortletViewState windowState = portletStateHolder
						.getWindowState(stateId);
				namespace = windowState.getNamespace();
				sessionPrefix = WindowIDRetriver.PORTLET_SCOPE_PREFIX
						+ windowState.getWindowId() + '?';
				portalActionUrl = windowState.getPortalActionURL();
			}
		}
	}

	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() {
		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() {
		Principal userPrincipal = getUserPrincipal();
		if( null != userPrincipal){
			return userPrincipal.getName();
		}
		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 PortalActionURL portalActionUrl;

	private String getSessionPrefix() {
		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);
		}

	}

	@Override
	protected String createActionUrl(Map<String, String> parameters) {
		PortalActionURL actionUrl = new PortalActionURL(portalActionUrl);
		for (Entry<String, String> parameterEntry : parameters.entrySet()) {
			actionUrl.addParameter(parameterEntry.getKey(), parameterEntry.getValue());
		}
		return actionUrl.toString();
	}

	/**
	 * @param url
	 * @return
	 */
	protected String encodeURL(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() {
        Principal userPrincipal = getHttpRequest().getUserPrincipal();
		HttpSession httpSession = getHttpRequest().getSession(false);
		if(null != httpSession){
			Principal portalPrincipal = (Principal) httpSession.getAttribute(PORTAL_USER_PRINCIPAL);
			if(null != portalPrincipal){
				userPrincipal = portalPrincipal;
			}
		}
		return userPrincipal;
	}

	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();
	}

}
