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

import it.mice.voila.runtime.springmvc.bean.PageStatus;
import it.mice.voila.runtime.web.filter.SubmitButtonFilter;
import it.mice.voila.runtime.web.menu.MenuFilterManager;

import java.net.MalformedURLException;
import java.net.URL;

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

import org.apache.commons.lang.StringUtils;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.servlet.tags.form.TagWriter;


public class SubmitTag extends
		org.springframework.web.servlet.tags.form.InputTag {

	/**
	 * serial version UID
	 */
	private static final long serialVersionUID = 3232323232L;
	
	private String value = null;
	private String id = null;
	private String disableDojo = null;
	private String ajaxCall = null;
	private String protectIfPageStatusIs = null;
	private String protectIfPageStatusIsNot = null;
    private String skipValidation = null;
    private String submitParameter = null;
    private String menuFilterManagerBeanName = "menuFilterManager";
    private String renderAsLink = null;
    private String surroundWithTag = "li";
    private String jumpTo = null;
    private boolean jumpHere = false;
    private String iconName = null;
    private boolean defaultButton = false;
    
    /**
     * Possible values are:
     * 1) leave;
     * 2) hide;
     * 3) disable (default).
     */
    private String ifNotAuthorized = "hide";
    
	public SubmitTag() {
		super();
		if (TagUtils.isBootstrapTheme()) {
			surroundWithTag = "";
		}
	}

	public String getJumpTo() {
		return jumpTo;
	}


	public void setJumpTo(String jumpTo) {
		this.jumpTo = jumpTo;
	}


	public String getIconName() {
		return iconName;
	}

	public void setIconName(String iconName) {
		this.iconName = iconName;
	}

	public boolean isDefaultButton() {
		return defaultButton;
	}

	public void setDefaultButton(boolean defaultButton) {
		this.defaultButton = defaultButton;
	}

	public String getValue() {
		return value;
	}

	public void setValue(String value) {
		this.value = value;
	}

	public String getIfNotAuthorized() {
		return ifNotAuthorized;
	}

	public void setIfNotAuthorized(String ifNoAuthorized) {
		this.ifNotAuthorized = ifNoAuthorized;
	}

	public String getSubmitParameter() {
		return submitParameter;
	}

	public void setSubmitParameter(String submitParameter) {
		this.submitParameter = submitParameter;
	}

	public String getMenuFilterManagerBeanName() {
		return menuFilterManagerBeanName;
	}

	public void setMenuFilterManagerBeanName(String menuFilterManagerBeanName) {
		this.menuFilterManagerBeanName = menuFilterManagerBeanName;
	}

	public String getSkipValidation() {
		return skipValidation;
	}

	public void setSkipValidation(String skipValidation) {
		this.skipValidation = skipValidation;
	}

	public String getRenderAsLink() {
		return renderAsLink;
	}

	public void setRenderAsLink(String renderAsLink) {
		this.renderAsLink = renderAsLink;
	}

	private boolean isSkipValidation() throws JspException {
		if (getSkipValidation() == null) {
			return evaluateBoolean("renderAsLink", getRenderAsLink());
		}
		return evaluateBoolean("skipValidation", getSkipValidation());
	}

	private boolean isRenderAsLink() throws JspException {
		return evaluateBoolean("renderAsLink", getRenderAsLink());
	}

	public String getSurroundWithTag() {
		return surroundWithTag;
	}

	public void setSurroundWithTag(String surroundWithTag) {
		this.surroundWithTag = surroundWithTag;
	}

	public String getProtectIfPageStatusIs() {
		return protectIfPageStatusIs;
	}

	public void setProtectIfPageStatusIs(String protectIfPageStatusIs) {
		this.protectIfPageStatusIs = protectIfPageStatusIs;
	}

	public String getProtectIfPageStatusIsNot() {
		return protectIfPageStatusIsNot;
	}

	public void setProtectIfPageStatusIsNot(String protectIfPageStatusIsNot) {
		this.protectIfPageStatusIsNot = protectIfPageStatusIsNot;
	}

	public String getDisableDojo() {
		return disableDojo;
	}

	public void setDisableDojo(String disableDojo) {
		this.disableDojo = disableDojo;
	}

	public String getAjaxCall() {
		return ajaxCall;
	}

	public void setAjaxCall(String ajaxCall) {
		this.ajaxCall = ajaxCall;
	}

	/**
	 * @return the jumpHere
	 */
	public boolean isJumpHere() {
		return jumpHere;
	}

	/**
	 * @param jumpHere the jumpHere to set
	 */
	public void setJumpHere(boolean jumpHere) {
		this.jumpHere = jumpHere;
	}

	@Override
	protected int writeTagContent(TagWriter tagWriter) throws JspException {
		if (isJumpHere()) {
			setJumpTo(resolveId());
		}
		if (getJumpTo() != null) {
			String jumpHereParm = "jumpTo=" + getJumpTo();
			if (getSubmitParameter() == null) {
				setSubmitParameter(jumpHereParm);
			} else {
				setSubmitParameter(getSubmitParameter() + "&amp;" + jumpHereParm);
			}
		}
		
        String finalUrl = null;
		try {
			finalUrl = buildFinalUrl();
		} catch (Exception e) {
			logger.error("Error calculating SubmitTag final URI. Skipping authorization check...", e);
		}
        boolean isAuthorizationDenied = false;

		if (!ifNotAuthorized.equals("leave") && finalUrl != null) {
	        MenuFilterManager menuFilterManager =
            	(MenuFilterManager)WebApplicationContextUtils
            	.getRequiredWebApplicationContext(
            			this.pageContext.getServletContext())
            			.getBean(getMenuFilterManagerBeanName());
		    isAuthorizationDenied = menuFilterManager.disableMenuItem(finalUrl);
		}
		
		if (isAuthorizationDenied) {
		    if (ifNotAuthorized.equals("hide")) {
		    	return SKIP_BODY;
		    } else {
		        protectField();
		    }
		} else {
			PageStatus pageStatus = TagUtils.getPageStatus(getNestedPath());
		    if (protectIfPageStatusIs != null && pageStatus.equals(protectIfPageStatusIs)) {
		        protectField();
		    } else if (protectIfPageStatusIsNot != null && !pageStatus.equals(protectIfPageStatusIsNot)) {
		        protectField();
		    }
		}
        
        if (!StringUtils.isEmpty(getSurroundWithTag()) && !TagUtils.isBootstrapTheme()) {
        	tagWriter.startTag(getSurroundWithTag());
        	tagWriter.writeAttribute("class", isRenderAsLink() ? "link" : "submit");
        }
        
        if (TagUtils.isBootstrapTheme()) {
    		setCssClass(TagUtils.safeAppend(getCssClass(), " btn"));
        	if (isDefaultButton()) {
        		setCssClass(TagUtils.safeAppend(getCssClass(), " btn-primary"));
        	} else {
        		setCssClass(TagUtils.safeAppend(getCssClass(), " btn-default"));
        	}
        	if (isSkipValidation()) {
        		setCssClass(TagUtils.safeAppend(getCssClass(), " cancel"));
        	}
        } else {
        	if (isDefaultButton()) {
        		setCssClass(TagUtils.safeAppend(getCssClass(), " default"));
        	}
        	if (getIconName() != null) {
        		setCssClass(TagUtils.safeAppend(getCssClass(), " " + getIconName()));
        	}
        }
        
        if (isRenderAsLink()) {
        	renderAsLink(tagWriter, finalUrl);
        } else {
        	if (TagUtils.isBootstrapTheme()) {
            	renderAsButton(tagWriter, finalUrl);
        	} else {
            	super.writeTagContent(tagWriter);
        	}
        }

		if (!StringUtils.isEmpty(getSurroundWithTag()) && !TagUtils.isBootstrapTheme()) {
        	tagWriter.endTag();
        }

		if (!evaluateBoolean("disableDojo", getDisableDojo()) && !TagUtils.isBootstrapTheme()) {
			writeDojoScript(tagWriter);
		}

		if (evaluateBoolean("ajaxCall", getAjaxCall())) {
			writeDojoAjaxCall(tagWriter);
		}

		return SKIP_BODY;
	}

	private String buildFinalUrl() throws JspException {
		String finalUrl = null;
    	String baseUri = buildRequestUri();
    	URL baseURL = buildBaseURL();
		HttpServletRequest request = (HttpServletRequest)pageContext.getRequest(); 
        FormTag formTag = (FormTag)pageContext.getAttribute(FormTag.FORM_BEAN_NAME, PageContext.REQUEST_SCOPE);
        if (isRenderAsLink()) {
            if (getPath().startsWith("/")) {
            	finalUrl = getPath();
            } else {
				if (formTag != null) {
					finalUrl = formTag.getActionUrl() + getPath();
					if (finalUrl.startsWith(request.getContextPath())) {
						finalUrl = finalUrl.substring(request.getContextPath().length());
					}
				} else {
	            	URL currentUrl = buildURL(baseURL, baseUri);
	            	currentUrl = buildURL(currentUrl, getPath());
	            	finalUrl = currentUrl.toExternalForm().substring(baseURL.toExternalForm().length() - 1);
				}
            }
        } else {
            String methodName = "";
            int startMethodName = getPath().lastIndexOf(SubmitButtonFilter.BUTTON_SUBMIT_PREFIX);
            if (startMethodName > -1) {
                methodName = getPath().substring(startMethodName + SubmitButtonFilter.BUTTON_SUBMIT_PREFIX.length());
            }
            if (formTag != null) {
                String formAction = formTag.getActionUrl();
                if (formAction.startsWith("/")) {
                	//URL currentUrl = buildURL(baseURL, getRequestContext().getContextUrl(formAction));
                	URL currentUrl = buildURL(baseURL, formAction);
                	currentUrl = buildURL(currentUrl, methodName);
                	finalUrl = currentUrl.toExternalForm().substring(baseURL.toExternalForm().length() - 1);
                } else {
                	URL currentUrl = buildURL(baseURL, baseUri);
                	currentUrl = buildURL(currentUrl, formAction);
                	currentUrl = buildURL(currentUrl, methodName);
                	finalUrl = currentUrl.toExternalForm().substring(baseURL.toExternalForm().length() - 1);
                }
            }
        }
        finalUrl = appendSubmitParameters(finalUrl);
        
        if (logger.isDebugEnabled()) {
            logger.debug("SubmitTag calculated url associated to the button and to be checked about security autorization is: " + finalUrl);
        }
		return finalUrl;
	}

	/**
	 * @param finalUrl
	 * @return
	 */
	private String appendSubmitParameters(String finalUrl) {
		if (!StringUtils.isEmpty(getSubmitParameter())) {
        	String prefix = (finalUrl.indexOf("?") > -1 ? "&amp;" : "?");
        	finalUrl = finalUrl + prefix + getSubmitParameter();
        }
		return finalUrl;
	}

	private URL buildBaseURL() throws JspException {
		HttpServletRequest request = (HttpServletRequest)pageContext.getRequest(); 
		String baseURL = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + "/";
		return buildURL(null, baseURL);
	}

	private String buildRequestUri() throws JspException {
		String baseURL = getRequestContext().getRequestUri();
		return baseURL;
	}

	private URL buildURL(URL base, String relativePath) throws JspException {
		try {
			return new URL(base, relativePath);
		} catch (MalformedURLException e) {
			throw new JspException(e);
		}
	}

	private void renderAsLink(TagWriter tagWriter, String finalUrl) throws JspException {
		String value = TagUtils.getMessage(getRequestContext(), evaluate("value", getValue()));
		
		tagWriter.startTag("a");
		if (isDisabled()) {
			tagWriter.writeAttribute("href", "#");
		} else {
			HttpServletRequest request = (HttpServletRequest)pageContext.getRequest(); 
			tagWriter.writeAttribute("href", request.getContextPath() + finalUrl);
		}
		writeOptionalAttributes(tagWriter);

		// custom optional attributes
		writeOptionalAttribute(tagWriter, ALT_ATTRIBUTE, getAlt());

		// custom optional attributes
		writeOptionalAttribute(tagWriter, "id", getId());
		
		if (TagUtils.isBootstrapTheme()) {
			writeSpanIcon(tagWriter);
			value = "&nbsp;" + value;
		}
		tagWriter.appendValue(value);
		tagWriter.endTag();
	}

	private void renderAsButton(TagWriter tagWriter, String finalUrl) throws JspException {
		String value = TagUtils.getMessage(getRequestContext(), evaluate("value", getValue()));
		String path = getPath();
		if (!StringUtils.isEmpty(getSubmitParameter())) {
			path += "?" + getSubmitParameter();
		}
		tagWriter.startTag("button");
		tagWriter.writeAttribute("id", resolveId());
		tagWriter.writeAttribute("name", path);
		tagWriter.writeAttribute("value", getPath());
		tagWriter.writeAttribute("type", "submit");
		
		writeOptionalAttributes(tagWriter);

		// custom optional attributes
		writeOptionalAttribute(tagWriter, ALT_ATTRIBUTE, getAlt());

		// custom optional attributes
		writeOptionalAttribute(tagWriter, "id", getId());
		
		writeSpanIcon(tagWriter);
		
		tagWriter.appendValue("&nbsp;" + value);
		tagWriter.endTag();
		
		TagUtils.getSecuritySessionBean().addSubmitFormField(path, getPath());
	}

	private void writeSpanIcon(TagWriter tagWriter) throws JspException {
		if (getIconName() != null) {
			tagWriter.startTag("span");
			tagWriter.writeAttribute(CLASS_ATTRIBUTE, getIconName());
			tagWriter.endTag(true);
		}
	}

	@Override
	protected void writeValue(TagWriter tagWriter) throws JspException {
		if (isRenderAsLink()) {
			return;
		}
		tagWriter.writeAttribute("value", TagUtils.getMessage(getRequestContext(), evaluate("value", getValue())));
	}

	@Override
	public String getTitle() {
		String title = super.getTitle();
		if (title != null) {
			return TagUtils.getMessage(getRequestContext(), title);
		} else {
			return null;
		}
	}

	/**
	 * Gets the appropriate CSS class to use based on the state of the current
	 * {@link org.springframework.web.servlet.support.BindStatus} object.
	 */
	protected String resolveCssClass() throws JspException {
		String cssAddOn = "button " + (isRenderAsLink() ? "buttonLink " : "buttonSubmit ");
		if (getCssClass() != null) {
			return cssAddOn + getCssClass();
		}
		return cssAddOn;
	}

	protected String getName() throws JspException {
		if (!isRenderAsLink()) {
			return appendSubmitParameters(getPath());
		}
		return getPath();
	}

	private void protectField() throws JspException {
		setDisabled("true");
	}

	private void writeDojoScript(TagWriter tagWriter) throws JspException {
		String decorator = "ValidateAllDecoration";
		if (isSkipValidation()) {
			if (isRenderAsLink()) {
				return;
			}
			decorator = "SkipValidationDecoration";
		}
		StringBuffer script = new StringBuffer();
		script.append("Spring.addDecoration(new Spring.").append(decorator).append("({");
		script.append("elementId : '" + resolveId() + "',");
		script.append("event: 'onclick'");
		script.append("}));");

		TagUtils.getCurrentForm(pageContext).addScript(script);
	}

	private void writeDojoAjaxCall(TagWriter tagWriter) throws JspException {
		StringBuffer script = new StringBuffer();
		script.append("Spring.addDecoration(new Spring.AjaxEventDecoration({");
		script.append("elementId : '" + resolveId() + "',");
		script.append("event: 'onclick',");
		script.append("formId: 'form'");
		script.append("}));");

		TagUtils.getCurrentForm(pageContext).addScript(script);
	}

	/**
	 * Determine the '<code>id</code>' attribute value for this tag,
	 * autogenerating one if none specified.
	 * @see #getId()
	 * @see #autogenerateId()
	 */
	@Override
	protected String resolveId() throws JspException {
		if (id == null) {
			id = super.resolveId();
			id = TagUtils.normalizeId(id);
		}
		return id;
	}

	/**
	 * Autogenerate the '<code>id</code>' attribute value for this tag.
	 * <p>The default implementation simply delegates to {@link #getName()},
	 * deleting invalid characters (such as "[" or "]").
	 */
	protected String autogenerateId() throws JspException {
		String result = super.autogenerateId();
		int i = result.indexOf("?");
		if (i > -1) {
			return result.substring(0, i);
		}
		return result;
	}

	@Override
	protected String getType() {
		return "submit";
	}

	@Override
	public void doFinally() {
		setDisabled("false");
		setDisableDojo(null);
		setAjaxCall(null);
		setTabindex(null);
		setProtectIfPageStatusIs(null);
		setProtectIfPageStatusIsNot(null);
		setSkipValidation(null);
		setSubmitParameter(null);
		setMenuFilterManagerBeanName("menuFilterManager");
		setRenderAsLink(null);
		setSurroundWithTag("li");
		setOnclick(null);
		setDefaultButton(false);
		setIconName(null);
		setCssClass(null);
		id = null;
		jumpTo = null;
		jumpHere = false;

		super.doFinally();
	}
}
