/**
 * 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
 */

package org.ajax4jsf.webapp;

import java.io.Serializable;

import javax.faces.context.FacesContext;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;

import org.ajax4jsf.config.WebXMLParser;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.richfaces.VersionBean;
import org.richfaces.VersionBean.Version;

/**
 * Parse at startup application web.xml and store servlet and filter mappings.
 * at runtime, used for convert resource key to uri, and vice versa.
 * 
 * @author asmirnov@exadel.com (latest modification by $Author: alexsmirnov $)
 * @version $Revision: 1.1.2.1 $ $Date: 2007/01/09 18:58:59 $
 * 
 */
public class WebXml extends WebXMLParser implements Serializable {

	public static final String CONTEXT_ATTRIBUTE = WebXml.class.getName();

	/**
	 * 
	 */
	private static final long serialVersionUID = -9042908418843695017L;

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

	public static final String RESOURCE_URI_PREFIX = "a4j";

	public static final String GLOBAL_RESOURCE_URI_PREFIX = "a4j/g";
	
	public static final String SESSION_RESOURCE_URI_PREFIX = "a4j/s";

	public static final String RESOURCE_URI_PREFIX_VERSIONED;

	public static final String GLOBAL_RESOURCE_URI_PREFIX_VERSIONED;

	public static final String SESSION_RESOURCE_URI_PREFIX_VERSIONED;

	static {
		VersionBean versionBean = new VersionBean();
		Version version = versionBean.getVersion();

		String suffix = "/" + version.getMajor() + "_" + version.getMinor() + "_"
			+ version.getRevision();
		
		//that's to prevent static compile-time linkage to constant values
		RESOURCE_URI_PREFIX_VERSIONED = RESOURCE_URI_PREFIX + suffix;
		GLOBAL_RESOURCE_URI_PREFIX_VERSIONED = GLOBAL_RESOURCE_URI_PREFIX + suffix;
		SESSION_RESOURCE_URI_PREFIX_VERSIONED = SESSION_RESOURCE_URI_PREFIX + suffix;
	}
	
	public static final String RESOURCE_URI_PREFIX_PARAM = "org.ajax4jsf.RESOURCE_URI_PREFIX";

	public static final String GLOBAL_RESOURCE_URI_PREFIX_PARAM = "org.ajax4jsf.GLOBAL_RESOURCE_URI_PREFIX";

	public static final String SESSION_RESOURCE_URI_PREFIX_PARAM = "org.ajax4jsf.SESSION_RESOURCE_URI_PREFIX";

	/**
	 * Prefix for resources handled by Chameleon framework.
	 */
	String _resourcePrefix = RESOURCE_URI_PREFIX;

	String _globalResourcePrefix;

	String _sessionResourcePrefix;

	protected boolean _prefixMapping = false;

	public static WebXml getInstance() {
		return getInstance(FacesContext.getCurrentInstance());
	}
	
	public static WebXml getInstance(FacesContext context) {
		WebXml webXml = (WebXml) context.getExternalContext()
				.getApplicationMap().get(WebXml.CONTEXT_ATTRIBUTE);
		return webXml;
	}
	
	@Override
	public void init(ServletContext context, String filterName)
			throws ServletException {
		super.init(context, filterName);
		setupResourcePrefixes(context);
		// Store Instance to context attribute.
		context.setAttribute(CONTEXT_ATTRIBUTE, this);

	}
	
	/**
	 * Convert {@link org.ajax4jsf.resource.InternetResource } key to real URL
	 * for handle by chameleon filter, depend of mapping in WEB.XML . For prefix
	 * or * mapping, prepend servlet prefix and default Resource prefix to key.
	 * For suffix mapping, prepend with resource prefix and append default faces
	 * suffix to URL ( before request param ). After conversion, call
	 * {@link javax.faces.application.ViewHandler#getResourceURL(javax.faces.context.FacesContext, java.lang.String)}
	 * and
	 * {@link javax.faces.context.ExternalContext#encodeResourceURL(java.lang.String)} .
	 * 
	 * @param context
	 * @param Url
	 * @return
	 */
	public String getFacesResourceURL(FacesContext context, String Url, boolean isGlobal) {
		StringBuffer buf = new StringBuffer();
		buf.append(isGlobal ? getGlobalResourcePrefix() : getSessionResourcePrefix()).append(Url);
		// Insert suffix mapping
		if (isPrefixMapping()) {
			buf.insert(0, getFacesFilterPrefix());
		} else {
			int index;
			if ((index = buf.indexOf("?")) >= 0) {
				buf.insert(index, getFacesFilterSuffix());
			} else {
				buf.append(getFacesFilterSuffix());
			}
		}
		String resourceURL = context.getApplication().getViewHandler()
				.getResourceURL(context, buf.toString());
		return resourceURL;

	}

	@Deprecated
	public String getFacesResourceURL(FacesContext context, String Url) {
		return getFacesResourceURL(context, Url, false);
	}

	public String getFacesResourceKey(String resourcePath) {
		// Remove JSESSIONID - for expired sessions it will merged to path.
		int jsesionidStart;
		if ((jsesionidStart = resourcePath.lastIndexOf(";jsessionid")) >= 0) {
			resourcePath = resourcePath.substring(0, jsesionidStart);
		}
		String resourcePrefix = getResourcePrefix();
		if (isPrefixMapping()) {
			String facesFilterPrefix = getFacesFilterPrefix();
			
			if (resourcePath.startsWith(facesFilterPrefix)) {
				String sessionResourcePrefix = getSessionResourcePrefix();
				if (resourcePath.startsWith(sessionResourcePrefix, facesFilterPrefix.length())) {
					return resourcePath.substring(facesFilterPrefix.length()
							+ sessionResourcePrefix.length());
				} else {
					String globalResourcePrefix = getGlobalResourcePrefix();
					if (!sessionResourcePrefix.equals(globalResourcePrefix) && 
							resourcePath.startsWith(globalResourcePrefix, facesFilterPrefix.length())) {
						
						return resourcePath.substring(facesFilterPrefix.length()
								+ globalResourcePrefix.length());
					} else if (!globalResourcePrefix.equals(resourcePrefix) && 
							resourcePath.startsWith(resourcePrefix, facesFilterPrefix.length())) {
						
						return resourcePath.substring(facesFilterPrefix.length()
								+ resourcePrefix.length());
					}
				}
			}
		} else {
			String sessionResourcePrefix = getSessionResourcePrefix();
			if (resourcePath.startsWith(sessionResourcePrefix)) {
				return resourcePath.substring(sessionResourcePrefix.length(),
						resourcePath.length() - getFacesFilterSuffix().length());
			} else {
				String globalResourcePrefix = getGlobalResourcePrefix();
				if (!sessionResourcePrefix.equals(globalResourcePrefix) && 
						resourcePath.startsWith(globalResourcePrefix)) {
					
					return resourcePath.substring(globalResourcePrefix.length(),
							resourcePath.length() - getFacesFilterSuffix().length());
				} else if (!globalResourcePrefix.equals(resourcePrefix) && 
						resourcePath.startsWith(resourcePrefix)) {
					
					return resourcePath.substring(resourcePrefix.length(),
							resourcePath.length() - getFacesFilterSuffix().length());
				}
			}
		}
			
		return null;
	}
	
	/**
	 * Detect request to resource and extract key from request
	 * 
	 * @param request
	 *            current http request
	 * @return resource key, or null for ordinary faces request.
	 */
	public String getFacesResourceKey(HttpServletRequest request) {
		String resourcePath = request.getRequestURI().substring(
				request.getContextPath().length());// isPrefixMapping()?request.getPathInfo():request.getServletPath();

		return getFacesResourceKey(resourcePath);
	}

	/**
	 * Detect request to {@link javax.faces.webapp.FacesServlet}
	 * 
	 * @param request
	 * @return true if request parsed to JSF.
	 */
	public boolean isFacesRequest(HttpServletRequest request) {
		// String resourcePath =
		// request.getRequestURI().substring(request.getContextPath().length());//isPrefixMapping()?request.getPathInfo():request.getServletPath();
		// if(isPrefixMapping() ) {
		// if (resourcePath.startsWith(getFacesFilterPrefix())) {
		// return true;
		// }
		// } else if (resourcePath.endsWith(getFacesFilterSuffix())) {
		// return true;
		// }
		// return false;
		return true;
	}

	/**
	 * @return Returns the resourcePrefix.
	 */
	@Deprecated
	public String getResourcePrefix() {
		return _resourcePrefix;
	}

	/**
	 * @since 3.2.2
	 * @return
	 */
	public String getGlobalResourcePrefix() {
		return _globalResourcePrefix;
	}

	/**
	 * @since 3.2.2
	 * @return
	 */
	public String getSessionResourcePrefix() {
		return _sessionResourcePrefix;
	}

	/**
	 * @return Returns the prefixMapping.
	 */
	public boolean isPrefixMapping() {
		return _prefixMapping;
	}

	/**
	 * @param resourcePrefix
	 *            The resourcePrefix to set.
	 */
	@Deprecated
	void setResourcePrefix(String resourcePrefix) {
		_resourcePrefix = resourcePrefix;
	}

	/**
	 * @since 3.2.2
	 * @param resourcePrefix
	 *            The resourcePrefix to set.
	 */
	void setGlobalResourcePrefix(String resourcePrefix) {
		_globalResourcePrefix = resourcePrefix;
	}

	/**
	 * @since 3.2.2
	 * @param resourcePrefix
	 *            The resourcePrefix to set.
	 *           
	 */
	void setSessionResourcePrefix(String resourcePrefix) {
		_sessionResourcePrefix = resourcePrefix;
	}

	/**
	 * @param context
	 */
	void setupResourcePrefixes(ServletContext context) {
		String globalResourcePrefix = (String) context
			.getInitParameter(GLOBAL_RESOURCE_URI_PREFIX_PARAM);
	
		String sessionResourcePrefix = (String) context
			.getInitParameter(SESSION_RESOURCE_URI_PREFIX_PARAM);
	
		String resourcePrefix = (String) context
			.getInitParameter(RESOURCE_URI_PREFIX_PARAM);
	
		if (null != resourcePrefix) {
			if (globalResourcePrefix == null) {
				//TODO overriden
				globalResourcePrefix = resourcePrefix;
			}
			
			if (sessionResourcePrefix == null) {
				//TODO overriden
				sessionResourcePrefix = resourcePrefix;
			}
		} else {
			resourcePrefix = RESOURCE_URI_PREFIX_VERSIONED;
		}
		
		if (globalResourcePrefix == null) {
			//TODO overriden
			globalResourcePrefix = GLOBAL_RESOURCE_URI_PREFIX_VERSIONED;
		}
		
		if (sessionResourcePrefix == null) {
			//TODO overriden
			sessionResourcePrefix = SESSION_RESOURCE_URI_PREFIX_VERSIONED;
		}
	
		if (null != getFacesFilterPrefix()) {
			_prefixMapping = true;
			if (getFacesFilterPrefix().endsWith("/")) {
				setGlobalResourcePrefix(globalResourcePrefix);
				setSessionResourcePrefix(sessionResourcePrefix);
				setResourcePrefix(resourcePrefix);
			} else {
				setGlobalResourcePrefix("/" + globalResourcePrefix);
				setSessionResourcePrefix("/" + sessionResourcePrefix);
				setResourcePrefix("/" + resourcePrefix);
			}
		} else if (null != getFacesFilterSuffix()) {
			_prefixMapping = false;
			setResourcePrefix("/" + resourcePrefix);
			setGlobalResourcePrefix("/" + globalResourcePrefix);
			setSessionResourcePrefix("/" + sessionResourcePrefix);
		}
	}
}
