/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.rjconsultores.ventaboletos.web.utilerias;

import java.math.BigDecimal;
import java.text.MessageFormat;

import org.zkoss.util.resource.Labels;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.WrongValueException;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.InputEvent;
import org.zkoss.zk.ui.ext.AfterCompose;
import org.zkoss.zk.ui.util.Clients;
import org.zkoss.zul.Constraint;
import org.zkoss.zul.Textbox;

/**
 * Textbox con validaciones de valores numericos.
 * 
 * Validaciones extras: maxValue - Indica cuál es el valor máximo permitido en el campo precision - Cuál es la precision de número scale - Cuál es la scala obligatorio - Indica se es obligatorio. El valo inicial es false. allowZero - Indica si es permitido valor cero allowNegative -Indica si es permitido valores negativos defaultZero - Indica se el valor inicial es cero
 * 
 * 
 * Además de las validaciones extras, si puede ocupar las validaciones del ZK (no emptu, etc)
 * 
 * @author gleimar
 */
public class MyTextboxDecimal extends Textbox implements AfterCompose {

	private static final long serialVersionUID = 1L;
	private String maxValue;
	private Integer precision;
	private Integer scale;
	private Boolean obligatorio = false;
	private Boolean allowZero = false;
	private Boolean allowNegative = true;
	private Boolean defaultZero = false;
	private Constraint myConstraint;
	private Constraint constraintTag; // variable con las constraints informadas
										// en constraint="no empty"

	public MyTextboxDecimal(String value) throws WrongValueException {
		super(value);
		this.registrarEventos();
	}

	public MyTextboxDecimal() {
		this.registrarEventos();
	}

	private void registrarEventos() {

		this.addEventListener("onChanging", new EventListener() {

			public void onEvent(Event event) throws Exception {
				if (event instanceof InputEvent) {

					InputEvent ev = (InputEvent) event;
					String val = ev.getValue();
					myConstraint.validate(MyTextboxDecimal.this, val);
				}
			}
		});
	}

	private BigDecimal getBigDecimal(String val) {
		if ((val == null) || (val.trim().equals(""))) {
			return (BigDecimal) null;
		}
		val = val.trim();

		boolean negativo = val.startsWith("-");
		if (negativo) {
			val = val.replace("-", "").replace(".", "").replace(",", ".");
			return (val.isEmpty() ? (BigDecimal) null : new BigDecimal(val).negate());
		} else {
			BigDecimal b = new BigDecimal(val.replace(".", "").replace(",", "."));
			if(b.scale() == 0 && scale != null){
				b = b.setScale(scale);
			}
			return b;
		}
	}

	private void aplicarMascara() {
		Clients.evalJavaScript("jQuery('#" + this.getUuid()
				+ "').maskMoney({allowZero:" + allowZero + ", allowNegative:"
				+ allowNegative + ", defaultZero:" + defaultZero
				+ ",thousands:',', decimal:'.', precision:" + (scale == null ? 2 : scale) + "})");
	}

	public String getMaxValue() {
		return maxValue;
	}

	public void setMaxValue(String maxValue) {
		this.maxValue = maxValue;
	}

	public Boolean getObligatorio() {
		return obligatorio;
	}

	public void setObligatorio(Boolean obligatorio) {
		this.obligatorio = obligatorio;
	}

	public Integer getPrecision() {
		return precision;
	}

	public void setPrecision(Integer precision) {
		this.precision = precision;
	}

	public Integer getScale() {
		return scale;
	}

	public void setScale(Integer scale) {
		this.scale = scale;
	}

	public BigDecimal getValueDecimal() {
		return this.getBigDecimal(this.getValue());
	}

	public Boolean getAllowZero() {
		return allowZero;
	}

	public void setAllowZero(Boolean allowZero) {
		this.allowZero = allowZero;
	}

	public Boolean getDefaultZero() {
		return defaultZero;
	}

	public void setDefaultZero(Boolean defaultZero) {
		this.defaultZero = defaultZero;
	}

	public void afterCompose() {
		aplicarMascara();

		myConstraint = new MyTextboxDecimalConstraint();

		super.setConstraint(myConstraint);
	}

	/**
	 * Agrega la constraint a las validaciones que ya existen
	 * 
	 * @param Constraint
	 *            - La constraint que se va a agregar
	 */
	@Override
	public void setConstraint(Constraint c) {
		if ((c instanceof MyTextboxDecimalConstraint) || (c == null)) {
			super.setConstraint(c);
		} else {
			this.constraintTag = c;
		}
	}

	public Boolean getAllowNegative() {
		return allowNegative;
	}

	public void setAllowNegative(Boolean allowNegative) {
		this.allowNegative = allowNegative;
	}

	class MyTextboxDecimalConstraint implements Constraint {
		@Override
		public void validate(Component comp, Object oVal)
				throws WrongValueException {

			// Si fue informado alguna constraint, esa es validada primer
			if (constraintTag != null) {
				constraintTag.validate(comp, oVal);
			}

			String val = oVal.toString();
			if (obligatorio && ((val == null) || (val.trim().equals("")))) {
				throw new WrongValueException(
						comp,
						Labels.getLabel("MSG.CONSTRAINT.CONSTRAINTNUMBERBDOBLIGATORIO"));
			} else if (maxValue != null) {
				BigDecimal bd = getBigDecimal(val);

				if (bd == null) {
					return;
				}
				BigDecimal bdMaxValue = new BigDecimal(maxValue);

				if (bd.compareTo(bdMaxValue) > 0) {
					MyTextboxDecimal.this.setRawValue((String) null);
					Constraint c = getConstraint();
					setConstraint((String) null);
					MyTextboxDecimal.this.setText((String) null);
					setConstraint(c);

					throw new WrongValueException(comp, new MessageFormat(Labels.getLabel("MSG.CONSTRAINT.MyTextboxDecimalConstraint.valorMaximo")).format(new String[] { maxValue }));
				}
			} else if ((precision != null) && (scale != null)) {
				BigDecimal valor = getBigDecimal(val);

				if (valor == null) {
					return;
				}

				// o decremento de -2 na precision e feito, porque o formato de precision/scale(7,2) como o oracle
				// para corrigir a validação para o zk, fazemos a conta da precision - 2 mais o valor do scale
				// assim nao permitindo que ultrapasse o limite de numeros inteiros.
				if (((valor.precision()) > (precision - 2) + valor.scale()) || (valor.scale() > scale)) {
					MyTextboxDecimal.this.setRawValue((String) null);
					Constraint c = getConstraint();
					setConstraint((String) null);
					MyTextboxDecimal.this.setText((String) null);
					setConstraint(c);
					throw new WrongValueException(comp, Labels.getLabel(
							"MSG.CONSTRAINT.CONSTRAINTNUMBERBD", new Object[] { precision - 2, scale }));
				}
			}
		}
	}
}
