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

import it.mice.voila.runtime.springmvc.bean.PageStatus;
import it.mice.voila.runtime.springmvc.util.ExpressionEvaluationUtils;

import java.math.BigDecimal;

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

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.servlet.tags.form.PasswordInputTag;
import org.springframework.web.servlet.tags.form.TagWriter;


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

	/**
	 * serial version UID
	 */
	private static final long serialVersionUID = 3232323232L;
	
	public static final String FIELD_TYPE_TEXT = "text";
	public static final String FIELD_TYPE_CODE = "code";
	public static final String FIELD_TYPE_VALUE = "value";
	public static final String FIELD_TYPE_CURRENCY = "currency";
	public static final String FIELD_TYPE_PERCENTAGE = "percentage";
	public static final String FIELD_TYPE_DATE = "date";
	public static final String FIELD_TYPE_TIME = "time";
	public static final String FIELD_TYPE_DATETIME = "datetime";
	public static final String FIELD_TYPE_PASSWORD = "password";
	public static final String FIELD_TYPE_FILE = "file";
	public static final String FIELD_TYPE_ICON_IMAGE = "iconImage";
	public static final String FIELD_TYPE_BANNER_IMAGE = "bannerImage";
	
	private String min;
	private String max;
	private Integer integerDigit;
	private Integer decimalDigit;
	private boolean grouping = true;
	private boolean signed = true;
	
	private String fieldType = FIELD_TYPE_TEXT;
	private String required = "false";
	private String regExp = null;
	private String disableDojo = null;
	private String forceEdit = null;
	private boolean showPassword = false;
	private String protectIfPageStatusIs = null;
	private String protectIfPageStatusIsNot = null;
	private String name = null;
	private String defaultImagePath = null;

	/**
	 * @return the name
	 */
	public String getName() {
		try {
			if (name == null) {
				return super.getName();
			}
			return (String)evaluate("name", name);
		} catch (JspException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * @param name the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

	public String getDefaultImagePath() {
		return defaultImagePath;
	}

	public void setDefaultImagePath(String defaultImagePath) {
		this.defaultImagePath = defaultImagePath;
	}

	public String getMin() {
		try {
			if (min == null) {
				return min;
			}
			return evaluate("min", min).toString();
		} catch (JspException e) {
			throw new RuntimeException(e);
		}
	}

	public BigDecimal getMinAsNumber() {
		String result = getMin();
		try {
			if (result == null) {
				return null;
			}
			return new BigDecimal(result);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public void setMin(String min) {
		this.min = min;
	}

	public String getMax() {
		try {
			if (max == null) {
				return max;
			}
			return evaluate("max", max).toString();
		} catch (JspException e) {
			throw new RuntimeException(e);
		}
	}

	public BigDecimal getMaxAsNumber() {
		String result = getMax();
		try {
			if (result == null) {
				return null;
			}
			return new BigDecimal(result);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public void setMax(String max) {
		this.max = max;
	}

	public Integer getIntegerDigit() {
		return integerDigit;
	}

	public void setIntegerDigit(Integer integerDigit) {
		this.integerDigit = integerDigit;
	}

	public Integer getDecimalDigit() {
		return decimalDigit;
	}

	public void setDecimalDigit(Integer decimalDigit) {
		this.decimalDigit = decimalDigit;
	}

	public boolean isGrouping() {
		return grouping;
	}

	public void setGrouping(boolean grouping) {
		this.grouping = grouping;
	}

	public boolean isSigned() {
		return signed;
	}

	public void setSigned(boolean signed) {
		this.signed = signed;
	}

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

	/**
	 * Is the password value to be rendered?
	 * @return <code>true</code> if the password value to be rendered.
	 */
	public boolean isShowPassword() {
		return this.showPassword;
	}

	/**
	 * Is the password value to be rendered?
	 * @param showPassword <code>true</code> if the password value is to be rendered.
	 */
	public void setShowPassword(boolean showPassword) {
		this.showPassword = showPassword;
	}
	
	public String getFieldType() {
		return fieldType;
	}

	public void setFieldType(String fieldType) {
		this.fieldType = fieldType;
	}

	public String getRequired() {
		return required;
	}

	public void setRequired(String required) {
		this.required = required;
	}

	public String getRegExp() {
		return regExp;
	}

	public void setRegExp(String regExp) {
		this.regExp = regExp;
	}

	public String getDisableDojo() {
		return disableDojo;
	}

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

	public String getForceEdit() {
		return forceEdit;
	}

	public void setForceEdit(String forceEdit) {
		this.forceEdit = forceEdit;
	}

	@Override
	protected int writeTagContent(TagWriter tagWriter) throws JspException {
		//FIXME Null safe check required !!!!!!!!!!!!!!!
		PageStatus pageStatus;
		try {
			pageStatus = TagUtils.getPageStatus(getNestedPath());
		} catch (javax.servlet.jsp.JspException e) {
			e.printStackTrace();
			throw new JspException(e);
		}
		if (pageStatus.isReadOnly()) {
			protectField();
		}
        if (protectIfPageStatusIs != null && pageStatus.equals(protectIfPageStatusIs)) {
            protectField();
        } else if (protectIfPageStatusIsNot != null && !pageStatus.equals(protectIfPageStatusIsNot)) {
            protectField();
        }
        
		if ((FIELD_TYPE_ICON_IMAGE.equalsIgnoreCase(getFieldType()) || FIELD_TYPE_BANNER_IMAGE.equalsIgnoreCase(getFieldType())) && isReadonly()) {
			tagWriter.startTag("img");
			tagWriter.writeAttribute(CLASS_ATTRIBUTE, "img-responsive " + getFieldType());
			tagWriter.writeAttribute("src", buildFinalUrl());
			tagWriter.writeAttribute("onError", "hide" + (FIELD_TYPE_ICON_IMAGE.equalsIgnoreCase(getFieldType()) ? "Icon" : "Banner") + "ImageBroken(this);");
			tagWriter.endTag();
			return SKIP_BODY;
		}
        
		fixParameters();
		
//		if (TagUtils.isBootstrapTheme()) {
			if (!StringUtils.isEmpty(getFieldType())) {
				setCssClass(TagUtils.safeAppend(getCssClass(), " " + getFieldType()));
			}
//		}
		super.writeTagContent(tagWriter);
		
		if (ExpressionEvaluationUtils.evaluateBoolean("disableDojo", getDisableDojo(), this.pageContext)) {
			return SKIP_BODY;
		}
		
		if (TagUtils.isBootstrapTheme()) {
			writeJQueryScript(tagWriter);			
		} else {
			writeDojoScript(tagWriter);
		}
		writePicEditScript(tagWriter);
		return SKIP_BODY;
	}

	private void fixParameters() throws JspException {
		if (FIELD_TYPE_DATE.equalsIgnoreCase(getFieldType())) {
			setMaxlength("10");
		}
		if (FIELD_TYPE_DATETIME.equalsIgnoreCase(getFieldType())) {
			setMaxlength("16");
		}
		if (FIELD_TYPE_CURRENCY.equalsIgnoreCase(getFieldType())) {
			setGrouping(true);
			if (getDecimalDigit() == null) {
				setDecimalDigit(2);
			}
		}
		if (FIELD_TYPE_CURRENCY.equalsIgnoreCase(getFieldType()) || FIELD_TYPE_VALUE.equalsIgnoreCase(getFieldType()) || FIELD_TYPE_CODE.equalsIgnoreCase(getFieldType())) {
			if (getIntegerDigit() == null || getIntegerDigit() == 0) {
				throw new JspException("integerDigit tag attribute must be specified and must be greater than zero for input fieldType equals to 'value' and 'currency'");
			}
			computeMinMax();
			computeMaxLength();
		}
		
		if (getMaxlength() != null && getSize() == null) {
			setSize(getMaxlength());
		}
		
		if (isRequired()) {
			String[] classi = StringUtils.defaultString(getCssClass()).split("\\ ");
			if (!ArrayUtils.contains(classi, "required")) {
				setCssClass(StringUtils.defaultString(getCssClass()) + " required");
			}
		}
	}

	private void computeMinMax() {
		if (getMax() == null) {
			setMax(new BigDecimal(Math.pow(10, getIntegerDigit()) - 1).toString());
			if (getDecimalDigit() != null && getDecimalDigit() > 0) {
				String decimals = "0." + StringUtils.repeat("9", getDecimalDigit());
				setMax(getMaxAsNumber().add(new BigDecimal(decimals)).toString());
			}
		}
		if (getMin() == null) {
			if (isSigned()) {
				setMin(getMaxAsNumber().multiply(new BigDecimal(-1)).toString());
			} else {
				setMin(new BigDecimal(0).toString());
			}
		}
	}

	private void computeMaxLength() {
		int result = getIntegerDigit();
		if (getDecimalDigit() != null && getDecimalDigit() > 0) {
			result += getDecimalDigit() + 1;
		}
		if (isSigned()) {
			result++;
		}
		
		if (isGrouping() && TagUtils.isBootstrapTheme()) {
			result += (getIntegerDigit() - 1) / 3;
		}
		setMaxlength(result + "");
	}

	private void protectField() throws JspException {
		if (!ExpressionEvaluationUtils.evaluateBoolean("forceEdit", getForceEdit(), this.pageContext)) {
			setReadonly(true);
			setTabindex("-1");
		    if (getCssClass() == null || getCssClass().indexOf("protected") == -1) {
		        setCssClass(StringUtils.defaultString(getCssClass()) + " protected");
		    }
		}
	}

	private void writeJQueryScript(TagWriter tagWriter) throws JspException {
		//writeJQueryValidationRule();
		writeJQueryInputMask();
	}

	private void writePicEditScript(TagWriter tagWriter) throws JspException {
		if (FIELD_TYPE_ICON_IMAGE.equalsIgnoreCase(getFieldType()) || FIELD_TYPE_BANNER_IMAGE.equalsIgnoreCase(getFieldType())) {
			/*	$(function() {
					$('#iconImage').picEdit({
						maxWidth: 120,
						maxHeight: 120,
						defaultImage: '<c:url value="/images/${customerForm.theCustomer.iconImageName}" />',
						boxClass: 'picedit_box_icon',
						readOnly: ${customerForm.pageStatus.readOnly}
					});
				});
			*/
			StringBuffer script = new StringBuffer();
			script.append("$(function() {");
			script.append("$('#" + resolveId() + "').picEdit({");
			script.append("maxWidth : " + (FIELD_TYPE_ICON_IMAGE.equalsIgnoreCase(getFieldType()) ? 120 : 600) + ",");
			script.append("maxHeight : 'auto',");
			script.append("defaultImage : '" + buildFinalUrl() + "',");
			script.append("boxClass : '" + (FIELD_TYPE_ICON_IMAGE.equalsIgnoreCase(getFieldType()) ? "picedit_box_icon" : "picedit_box_banner") + "',");
			script.append("readOnly : " + isReadonly());
			script.append("});});");
			TagUtils.getCurrentForm(pageContext).addScript(script);
			
			TagUtils.getSecuritySessionBean().addDeleteFlagFormField(getName());
		}
	}

	private String buildFinalUrl() throws JspException {
		HttpServletRequest request = (HttpServletRequest)pageContext.getRequest(); 
		return request.getContextPath() + "/images/" + getDefaultImagePath();
	}

	private void writeJQueryInputMask() throws JspException {
		//	$('input.number').autoNumeric('init', {aSign:'', pSign:'s' }); 
		if (FIELD_TYPE_CODE.equalsIgnoreCase(getFieldType()) || FIELD_TYPE_VALUE.equalsIgnoreCase(getFieldType()) || FIELD_TYPE_CURRENCY.equalsIgnoreCase(getFieldType()) || FIELD_TYPE_PERCENTAGE.equalsIgnoreCase(getFieldType())) {
			StringBuffer script = new StringBuffer();

//			String groupSep = "";
//			String decimalSep = ",";
			
			script.append("$n(\"" + resolveId() + "\").autoNumeric('init', {");
//			if (FIELD_TYPE_VALUE.equalsIgnoreCase(getFieldType()) || FIELD_TYPE_CURRENCY.equalsIgnoreCase(getFieldType())) {
//				if (isGrouping()) {
//					groupSep = ".";
//				}
//				if (getDecimalDigit() > 0) {
//					decimalSep = ",";
//				}
//			}

//			script.append("aSep: '" + groupSep + "'");
//			script.append(", aDec: '" + decimalSep + "'");
			script.append("vMin: '" + getMin() + "', ");
			script.append("vMax: '" + getMax() + "', ");
			if (!isGrouping()) {
				script.append("aSep: '', ");
			}
//			if (getDecimalDigit() == 0) {
//				script.append("aDec: '', ");
//			}

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

	private void writeDojoScript(TagWriter tagWriter) throws JspException {
		if (FIELD_TYPE_FILE.equals(getFieldType()) || FIELD_TYPE_ICON_IMAGE.equals(getFieldType()) || FIELD_TYPE_BANNER_IMAGE.equals(getFieldType())) {
			return;
		}
		StringBuffer script = new StringBuffer();
		script.append("Spring.addDecoration(new Spring.ElementDecoration({");
		script.append("elementId : '" + resolveId() + "',");
		script.append("widgetType : '" + getWidgetType() + "',");
		script.append("widgetAttrs : {");

		writeWidgetAttrs(script);

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

	private void writeWidgetAttrs(StringBuffer script) throws JspException {
		script.append("required : " + isRequired());

		Object regExpResult = evaluate("regExp", getRegExp());
		
		if (FIELD_TYPE_CODE.equalsIgnoreCase(getFieldType())) {
			if (regExpResult == null) {
				regExpResult = "\\\\d+";
			}
		} else if (FIELD_TYPE_CURRENCY.equalsIgnoreCase(getFieldType())) {
			script.append(",currency : 'EUR'");
			computeOtherConstraints(script);
		} else if (FIELD_TYPE_DATE.equalsIgnoreCase(getFieldType())) {
			script.append(",datePattern : 'dd/MM/yyyy'");
		} else if (FIELD_TYPE_DATETIME.equalsIgnoreCase(getFieldType())) {
			script.append(",datePattern : 'dd/MM/yyyy HH:mm'");
		} else if (FIELD_TYPE_PERCENTAGE.equalsIgnoreCase(getFieldType())) {
			script.append(",value : " + getBindStatus().getActualValue());
		} else if (FIELD_TYPE_TIME.equalsIgnoreCase(getFieldType())) {
			script.append(",datePattern : 'HH:mm'");
		} else if (FIELD_TYPE_VALUE.equalsIgnoreCase(getFieldType())) {
			computeOtherConstraints(script);
		}
		
		if (regExpResult != null) {
			script.append(",regExp : '" + regExpResult + "'");
		}
	}

	private boolean isRequired() throws JspException {
		return ExpressionEvaluationUtils.evaluateBoolean("required", getRequired(), this.pageContext);
	}

	private void computeOtherConstraints(StringBuffer script) throws JspException {
		if (getBindStatus().getActualValue() != null) {
			script.append(",value : " + getBindStatus().getActualValue());
		}
		script.append(",constraints : {min : " + getMin() + ",max : " + getMax());
		if (getDecimalDigit() != null) {
			if (FIELD_TYPE_VALUE.equalsIgnoreCase(getFieldType())) {
				script.append(",pattern : '" + buildPattern() + "'");
			} else {
				script.append(",places : " + getDecimalDigit());
			}
		}
		script.append("}");
	}

	private String buildPattern() {
		String pattern = StringUtils.repeat("#", getIntegerDigit() - 1) + "0";
		if (getIntegerDigit() < 4) {
			pattern = StringUtils.repeat("#", getIntegerDigit() - 1) + "0";
		} else {
			if (isGrouping()) {
				pattern = "#,##0";
			} else {
				pattern = StringUtils.repeat("#", getIntegerDigit() - 1) + "0";
			}
		}
		pattern = pattern + "." + StringUtils.repeat("#", getDecimalDigit());
		return pattern;
	}

	private String getWidgetType() {
		String widgetType = "dijit.form.ValidationTextBox";
		if (FIELD_TYPE_CODE.equalsIgnoreCase(getFieldType())) {
			widgetType = "dijit.form.ValidationTextBox";
		} else if (FIELD_TYPE_CURRENCY.equalsIgnoreCase(getFieldType())) {
			widgetType = "dijit.form.CurrencyTextBox";
		} else if (FIELD_TYPE_DATE.equalsIgnoreCase(getFieldType())) {
			widgetType = "dijit.form.DateTextBox";
		} else if (FIELD_TYPE_DATETIME.equalsIgnoreCase(getFieldType())) {
			widgetType = "dijit.form.DateTextBox";
		} else if (FIELD_TYPE_PERCENTAGE.equalsIgnoreCase(getFieldType())) {
			widgetType = "dijit.form.PercentageTextBox";
		} else if (FIELD_TYPE_TIME.equalsIgnoreCase(getFieldType())) {
			widgetType = "dijit.form.TimeTextBox";
		} else if (FIELD_TYPE_VALUE.equalsIgnoreCase(getFieldType())) {
			widgetType = "dijit.form.NumberTextBox";
		}
		return widgetType;
	}

	@Override
	protected String getType() {
		if (FIELD_TYPE_PASSWORD.equalsIgnoreCase(getFieldType())) {
			return "password";
		} else if (FIELD_TYPE_FILE.equalsIgnoreCase(getFieldType()) || FIELD_TYPE_ICON_IMAGE.equalsIgnoreCase(getFieldType()) || FIELD_TYPE_BANNER_IMAGE.equalsIgnoreCase(getFieldType())) {
			return "file";
		} else {
			return "text";
		}
	}

	/**
	 * The {@link PasswordInputTag} only writes it's value if the
	 * {@link #setShowPassword(boolean) 'showPassword'} property value is
	 * {@link Boolean#TRUE true}.
	 */
	@Override
	protected void writeValue(TagWriter tagWriter) throws JspException {
		String value = getDisplayString(getBoundValue(), getPropertyEditor());

		if (!FIELD_TYPE_PASSWORD.equalsIgnoreCase(getFieldType()) || this.showPassword) {
			super.writeValue(tagWriter);
		} else {
			value = "";
			tagWriter.writeAttribute("value", value);
			processFieldValue(getName(), value, getFieldType());
		}
		TagUtils.getSecuritySessionBean().setReadOnly(getName(), isReadonly(), value, getBindStatus().getActualValue());
	}

	/**
	 * Gets the appropriate CSS class to use based on the state of the current
	 * {@link org.springframework.web.servlet.support.BindStatus} object.
	 * Overriden in order to concatenate error css list to the base list.
	 */
	protected String resolveCssClass() throws JspException {
		if (getBindStatus().isError() && org.springframework.util.StringUtils.hasText(getCssErrorClass())) {
			return ObjectUtils.getDisplayString(evaluate("cssClass", getCssClass()) + " " + evaluate("cssErrorClass", getCssErrorClass()));
		}
		else {
			return ObjectUtils.getDisplayString(evaluate("cssClass", getCssClass()));
		}
	}

	@Override
	public void doFinally() {
		setReadonly(false);
		setRequired("false");
		setTabindex(null);
		setCssClass(null);
		setFieldType(FIELD_TYPE_TEXT);
		setSize(null);
		setForceEdit(null);
		setDisableDojo(null);
		setMaxlength(null);
		setTabindex(null);
		setRegExp(null);
		setShowPassword(false);
		setProtectIfPageStatusIs(null);
		setProtectIfPageStatusIsNot(null);
		setMin(null);
		setMax(null);
		setIntegerDigit(null);
		setDecimalDigit(null);
		setGrouping(true);
		setSigned(true);
		setName(null);
		setDefaultImagePath(null);
		super.doFinally();
	}
	
}
