package it.mice.voila.runtime.springmvc.taglibs;

import it.mice.voila.runtime.springmvc.bean.ChooseInfoBean;
import it.mice.voila.runtime.springmvc.bean.PageStatus;
import it.mice.voila.runtime.springmvc.csrf.CSRFHandlerInterceptor;
import it.mice.voila.runtime.springmvc.csrf.SecuritySessionBean;
import it.mice.voila.runtime.springmvc.form.JfFormBean;
import it.mice.voila.runtime.springmvc.util.SpringMvcUtil;

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;

import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.PropertyAccessor;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.ThemeResolver;
import org.springframework.web.servlet.support.BindStatus;
import org.springframework.web.servlet.support.RequestContext;
import org.springframework.web.servlet.support.RequestContextUtils;
import org.springframework.web.servlet.tags.NestedPathTag;
import org.springframework.web.servlet.tags.RequestContextAwareTag;

public class TagUtils {
	public static final String STANDARD_THEME = "standard";
	public static final String BOOTSTRAP_THEME = "bootstrap";

	public static final Object[] EMPTY_ARRAY = {};
	/**
	 * The prefix for all {@link PageContext} attributes created by this tag.
	 */
	private static final String PAGE_CONTEXT_ATTRIBUTE_PREFIX = "org.springframework.web.servlet.tags.form.TagIdGenerator.";
	public static final String SECURITY_SESSION_BEAN_NAME = "SECURITY_SESSION_BEAN_NAME";
	public static final String SECURITY_SESSION_MODE = "SECURITY_SESSION_MODE";

	public static String getCurrentTheme() {
		ThemeResolver themeResolver = RequestContextUtils.getThemeResolver(getCurrentRequest());
		if (themeResolver == null) {
			return BOOTSTRAP_THEME;
		}

		return themeResolver.resolveThemeName(getCurrentRequest());
	}
	
	public static boolean isBootstrapTheme() {
		return BOOTSTRAP_THEME.equals(getCurrentTheme());
	}
	
	public static boolean isStandardTheme() {
		return STANDARD_THEME.equals(getCurrentTheme());
	}

	public static boolean isMessageCode(String text) {
		if (text == null) return false;
		if (text.startsWith("{") && text.endsWith("}")) {
			return true;
		}
		return false;
	}

	public static String getMessageCode(String code) {
		if (isMessageCode(code)) {
			int length = code.length() - 1;
			return code.substring(1, length);
		}
		throw new IllegalArgumentException("The supplied code doesn't seem to be a valid message code entry such as: {example.code}");
	}

	public static void safeWrite(PageContext pageContext, String text) throws JspException {
		if (text == null) return;
		try {
			pageContext.getOut().append(text);
		} catch (IOException e) {
			throw new JspException("Unable to write to JspWriter", e);
		}
	}
	
	public static String safeAppend(String appendTo, String text) {
		if (text == null) return appendTo;
		return (StringUtils.isEmpty(appendTo) ? text : appendTo + text);
	}
	
	public static String safePrepend(String prependTo, String text) {
		if (text == null) return prependTo;
		return (StringUtils.isEmpty(prependTo) ? text : text + prependTo);
	}
	
	/**
	 * Get the {@link BindStatus} for this tag.
	 * @param pageContext the pageContext.
	 * @return the result.
	 * @throws JspException the exception.
	 */
	public static JfFormBean getFormBean(PageContext pageContext) throws JspException {
		String beanName = "";
		RequestContext requestContext = (RequestContext) pageContext.getAttribute(RequestContextAwareTag.REQUEST_CONTEXT_PAGE_ATTRIBUTE);
		String nestedPath = (String) pageContext.getAttribute(NestedPathTag.NESTED_PATH_VARIABLE_NAME, PageContext.REQUEST_SCOPE);
		JfFormBean result = (JfFormBean)getBindObject(nestedPath, beanName, requestContext);
		if (result == null) {
			result = (JfFormBean)pageContext.findAttribute(normalizePath(nestedPath, beanName));
		}
		return result;
	}
	
	/**
	 * Get the {@link BindStatus} for this tag.
	 * @param pageContext the pageContext.
	 * @return the result.
	 * @throws JspException the exception.
	 */
	public static PageStatus getPageStatus(PageContext pageContext) throws JspException {
		RequestContext requestContext = (RequestContext) pageContext.getAttribute(RequestContextAwareTag.REQUEST_CONTEXT_PAGE_ATTRIBUTE);
		String nestedPath = (String) pageContext.getAttribute(NestedPathTag.NESTED_PATH_VARIABLE_NAME, PageContext.REQUEST_SCOPE);

		return getPageStatus(getForm(nestedPath));
	}
	
	/**
	 * Get the {@link BindStatus} for this tag.
	 * @param nestedPath the nestedPath.
	 * @return the result.
	 * @throws JspException the exception.
	 */
	public static PageStatus getPageStatus(String nestedPath) throws JspException {
		return getPageStatus(getForm(nestedPath));
	}
	
	/**
	 * Get the {@link BindStatus} for this tag.
	 * @param form the form.
	 * @return the result.
	 * @throws JspException the exception.
	 */
	public static PageStatus getPageStatus(JfFormBean form) throws JspException {
		PageStatus pageStatus = null;
		if (form != null) {
			pageStatus = form.getPageStatus();
		}
		if (pageStatus == null) {
			return new PageStatus("", false);
		}
		return pageStatus;
	}
	
	public static JfFormBean getForm(String nestedPath) {
		String formName = nestedPath.substring(0, nestedPath.length() - 1);
		Object form = SpringMvcUtil.getRequest().getAttribute(formName);
		if (form != null && form instanceof JfFormBean) {
			return (JfFormBean)form;
		}
		form = SpringMvcUtil.getSession().getAttribute(formName);
		if (form != null && form instanceof JfFormBean) {
			return (JfFormBean)form;
		}
		return null;
	}
	
	/**
	 * Get the {@link BindStatus} for this tag.
	 * @param nestedPath the nestedPath.
	 * @param beanName the beanName.
	 * @param requestContext the requestContext.
	 * @return the result.
	 * @throws JspException the exception.
	 */
	public static Object getBindObject(String nestedPath, String beanName, RequestContext requestContext) throws JspException {
		String pathToUse = normalizePath(nestedPath, beanName);
		Object result = null;
		try {
			result = new BindStatus(requestContext, pathToUse, false).getValue();
		} catch (Exception e) {
			//Ignore this.
		}
		return result;
	}
	
	public static String normalizePath(String nestedPath, String beanName) {
		String pathToUse = (nestedPath != null ? nestedPath + beanName : beanName);
		if (pathToUse.endsWith(PropertyAccessor.NESTED_PROPERTY_SEPARATOR)) {
			pathToUse = pathToUse.substring(0, pathToUse.length() - 1);
		}
		return pathToUse;
	}
	
	public static String getMessage(RequestContext requestContext, String key, String value) {
		if (StringUtils.isEmpty(key)) {
			return value;
		} else {
    		return requestContext.getMessage(key);
		}
	}
	
	public static String getMessage(RequestContext requestContext, Object key, Object value) {
		return getMessage(requestContext, ObjectUtils.toString(key), ObjectUtils.toString(value));
	}
	
	public static String getMessage(RequestContext requestContext, String input) {
    	if (isMessageCode(input)) {
    		return requestContext.getMessage(getMessageCode(input));
    	}
    	return input;
	}
	
	public static String getMessage(RequestContext requestContext, Object input) {
		return getMessage(requestContext, ObjectUtils.toString(input));
	}
	
	/**
	 * Get the current unique ID (within the given {@link PageContext}) for the supplied name.
	 * @param name the name.
	 * @param pageContext the pageContext.
	 * @return the result.
	 */
	public static String currentId(String name, PageContext pageContext) {
		String attributeName = PAGE_CONTEXT_ATTRIBUTE_PREFIX + name;
		Integer currentCount = (Integer) pageContext.getAttribute(attributeName);
		return (name + currentCount);
	}
	
	/**
	 * Get the current unique ID (within the given {@link PageContext}) for the supplied name.
	 * @param id the id.
	 * @return the result.
	 */
	public static String normalizeId(String id) {
		return StringUtils.replaceChars(id, "/\\[]$&<>", "________");
	}
	
	public static FormTag getCurrentForm(PageContext pageContext) {
		return (FormTag)pageContext.getAttribute(FormTag.FORM_BEAN_NAME, PageContext.REQUEST_SCOPE);
	}

	public static String determineClassAttributeForSize(String input) {
		int size = Integer.parseInt(input) / 6;
		int cells = Math.max(2, Math.min(9, size));
		
		return "col-sm-" + cells;
	}
	
	public static HttpServletRequest getCurrentRequest() {
		HttpServletRequest curRequest = 
				((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
				.getRequest();
		return curRequest;
	}
	
	public static HttpSession getCurrentSession() {
		return getCurrentRequest().getSession();
	}
	
	public static SecuritySessionBean getSecuritySessionBean() {
		SecuritySessionBean result = (SecuritySessionBean)getCurrentSession().getAttribute(getSecuritySessionName());
		if (result == null) {
			result = new SecuritySessionBean();
			getCurrentSession().setAttribute(getSecuritySessionName(), result);
		}
		return result;
	}
	
	public static void removeSecuritySessionBean() {
		getCurrentSession().removeAttribute(getSecuritySessionName());
	}
	
	private static String getSecuritySessionName() {
		return SECURITY_SESSION_BEAN_NAME + getCurrentRequest().getAttribute(SECURITY_SESSION_MODE);
	}
}
