/**
 * 
 */
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.net.URLDecoder;
import java.net.URLEncoder;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import javax.faces.application.ViewHandler;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.context.FacesContextFactory;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.PortletContext;
import javax.portlet.PortletException;
import javax.portlet.PortletRequest;
import javax.portlet.PortletRequestDispatcher;
import javax.portlet.PortletResponse;
import javax.portlet.PortletSession;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.portlet.PortletURL;
import javax.portlet.faces.Bridge;

import org.ajax4jsf.webapp.WebXml;

/**
 * Version of the {@link ExternalContext} for a Portlet request.
 * {@link FacesContextFactory} will create instance of this class for a portal
 * <code>action</code> phase.
 * 
 * @author asmirnov
 * 
 */
public class PortletContextImpl extends AbstractExternalContext {

	public static final String ACTION_URL_DO_NOTHITG = "/ajax4jsfPortletBridge/actionUrl/do/nothitg";
	private String namespace;

	public PortletContextImpl(PortletContext context, PortletRequest request,
			PortletResponse response) {
		super(context, request, response);
		WebXml webXml = (WebXml) context.getAttribute(WebXml.CONTEXT_ATTRIBUTE);
		if (null != webXml) {
			_servletPath = webXml.getFacesServletPrefix();
		}
		ArrayList excludedAttributes = Collections.list(request
				.getAttributeNames());
		request.setAttribute(EXCLUDED_PARAMETERS_ATTRIBUTE, excludedAttributes);
	}

	public void setResponseCharacterEncoding(String encoding) {
		// Do nothing
	}

	public String getResponseCharacterEncoding() {
		return null;
	}

	public String getResponseContentType() {
		return null;
	}

	public void setRequestCharacterEncoding(String encoding)
			throws UnsupportedEncodingException {
		try {
			ActionRequest actionRequest = (ActionRequest) getPortletRequest();
			actionRequest.setCharacterEncoding(encoding);
		} catch (IllegalStateException e) {
			// TODO: handle exception
		}
	}

	public String getRequestCharacterEncoding() {
		ActionRequest actionRequest = (ActionRequest) getPortletRequest();
		return actionRequest.getCharacterEncoding();
	}

	public String getRequestContentType() {
		return null;
	}

	protected PortletContext getPortletContext() {
		return (PortletContext) getContext();
	}

	protected PortletRequest getPortletRequest() {
		return (PortletRequest) getRequest();
	}

	protected PortletResponse getPortletResponse() {
		return (PortletResponse) getResponse();
	}

	public String getInitParameter(String name) {
		return getPortletContext().getInitParameter(name);
	}

	protected String getNamespace() {
		if (null == namespace) {
			namespace = (String) getRequestParameter(AbstractExternalContext.NAMESPACE_PARAMETER);
			if (null == namespace) {
				throw new IllegalStateException(
						"Can not determine portlet namespace");
			}
		}
		return namespace;
	}

	public URL getResource(String path) throws MalformedURLException {
		return getPortletContext().getResource(path);
	}

	public InputStream getResourceAsStream(String path) {
		return getPortletContext().getResourceAsStream(path);
	}

	public Set getResourcePaths(String path) {
		return getPortletContext().getResourcePaths(path);
	}

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

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

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

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

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

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

	public Map getRequestParameterValuesMap() {
		return getPortletRequest().getParameterMap();
	}

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

	protected Object getRequestHeader(String name) {
		return getPortletRequest().getProperty(name);
	}

	protected Enumeration getRequestHeaderNames() {
		return getPortletRequest().getPropertyNames();
	}

	protected String[] getRequestHeaderValues(String name) {
		Enumeration properties = getPortletRequest().getProperties(name);
		List values = new ArrayList();
		while (properties.hasMoreElements()) {
			Object value = (Object) properties.nextElement();
			values.add(value);
		}
		return (String[]) values.toArray(EMPTY_STRING_ARRAY);
	}

	protected Object getRequestParameter(String name) {
		return getPortletRequest().getParameter(name);
	}

	protected Object getSessionAttribute(String name) {
		return getPortletRequest().getPortletSession(true).getAttribute(name,
				PortletSession.PORTLET_SCOPE);
	}

	protected Enumeration getSessionAttributeNames() {
		return getPortletRequest().getPortletSession(true).getAttributeNames(
				PortletSession.PORTLET_SCOPE);
	}

	protected void removeContextAttribute(String name) {
		getPortletContext().removeAttribute(name);
	}

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

	protected void removeSessionAttribute(String name) {
		getPortletRequest().getPortletSession(true).removeAttribute(name,
				PortletSession.PORTLET_SCOPE);
	}

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

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

	protected void setSessionAttribute(String name, Object value) {
		getPortletRequest().getPortletSession(true).setAttribute(name, value,
				PortletSession.PORTLET_SCOPE);
	}

	public void dispatch(String path) throws IOException {
		if (null == path) {
			throw new NullPointerException("Path to new view is null");
		}
		throw new IllegalStateException(
				"Dispatch to new view not at render phase");
	}

	private static final Pattern absoluteUrl = Pattern.compile("");
	private static final Pattern directLink = Pattern.compile("");
	private static final Pattern viewIdParam = Pattern.compile("");

	/**
	 * @param parameters
	 * @return
	 */
	@Override
	protected String createActionUrl(Map<String, String> parameters) {
		String dummyActionURL = getRequestContextPath() + ACTION_URL_DO_NOTHITG;
		try {
			PortalActionURL actionUrl = new PortalActionURL(dummyActionURL);
			for (Iterator<Map.Entry<String, String>> param = parameters
					.entrySet().iterator(); param.hasNext();) {
				Map.Entry<String, String> parameter = param.next();
				actionUrl
						.addParameter(parameter.getKey(), parameter.getValue());
			}
			return actionUrl.toString();
		} catch (MalformedURLException e) {

		}
		return dummyActionURL;
	}

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

	/**
	 * @param url
	 * @return
	 */
	protected String encodeURL(String url) {
		return getPortletResponse().encodeURL(url);
	}

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

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

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

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

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

	private String _pathInfo = null;

	public String getRequestPathInfo() {
		String pathInfo = (String) getRequestParameter(VIEW_ID_PARAMETER);
		if (null == pathInfo) {
			pathInfo = (String) getRequestAttribute(VIEW_ID_PARAMETER);
		}
		return pathInfo;
	}

	private String _servletPath = null;

	public String getRequestServletPath() {
		return _servletPath;
	}

	public Object getSession(boolean create) {
		return getPortletRequest().getPortletSession();
	}

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

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

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

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

	public void redirect(String url) throws IOException {
		if (null == url || url.length() < 0) {
			throw new NullPointerException("Path to redirect is null");
		}
		PortalActionURL actionURL = new PortalActionURL(url);
		if (url.startsWith("#")
				|| (!actionURL.isInContext(getRequestContextPath()))
				|| "true".equalsIgnoreCase(actionURL
						.getParameter(Bridge.DIRECT_LINK))) {
			((ActionResponse) getResponse()).sendRedirect(url);
		} else {
			internalRedirect(actionURL);
		}
	}

	/**
	 * @param actionURL
	 */
	protected void internalRedirect(PortalActionURL actionURL) {
		// Detect ViewId from URL and create new view for them.
		String viewId = actionURL.getParameter(VIEW_ID_PARAMETER);
		if (null != viewId) {
			try {
				viewId = URLDecoder.decode(viewId, "UTF8");
				FacesContext facesContext = FacesContext.getCurrentInstance();
				ViewHandler viewHandler = facesContext.getApplication()
						.getViewHandler();
				facesContext.setViewRoot(viewHandler.createView(facesContext,
						viewId));
				setHasNavigationRedirect(true);
			} catch (UnsupportedEncodingException e) {
				// Do nothing - UTF-8 encoding is a default.
			}
		}
	}
}
