/* DataValidationCtrl.java

{{IS_NOTE
	Purpose:
		
	Description:
		
	History:
		March 26, 2015 11:32:52 AM , Created by JerryChen
}}IS_NOTE

Copyright (C) 2015 Potix Corporation. All Rights Reserved.

*/
package org.zkoss.zssex.ui.dialog;

import java.util.Map;

import org.zkoss.util.resource.Labels;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.select.annotation.Listen;
import org.zkoss.zk.ui.select.annotation.Wire;
import org.zkoss.zk.ui.util.Clients;
import org.zkoss.zss.api.model.Validation;
import org.zkoss.zss.api.model.Validation.OperatorType;
import org.zkoss.zss.api.model.Validation.ValidationType;
import org.zkoss.zss.model.InvalidDataValidationException;
import org.zkoss.zss.ui.Spreadsheet;
import org.zkoss.zssex.ui.dialog.impl.DialogCallbackEvent;
import org.zkoss.zssex.ui.dialog.impl.RefocusDialogCtrlBase;
import org.zkoss.zul.Checkbox;
import org.zkoss.zul.Combobox;
import org.zkoss.zul.Label;
import org.zkoss.zul.ListModelList;
import org.zkoss.zul.Messagebox;
import org.zkoss.zul.Textbox;
import org.zkoss.zul.Window;

/**
 * @author JerryChen
 */
public class DataValidationCtrl extends RefocusDialogCtrlBase {

	private static final long serialVersionUID = 1L;
	protected final static String URI = "~./zssex/dlg/dataValidation.zul";
	public static final String ARG_VALIDATION_CODE = "validationCode";
	public static final String ARG_VALIDATION_TYPE_CODE = "validationTypeCode";
	public static final String ARG_OPERATOR_TYPE_CODE = "operatorTypeCode";
	public static final String ARG_IGNORE_BLANK_CODE = "ignoreBlankCode";
	public static final String ARG_IN_CELL_DROPDOWN_CODE = "inCellDropdownCode";
	public static final String ARG_PARAM1_CODE = "param1Code";
	public static final String ARG_PARAM2_CODE = "param2Code";
	public static final String ARG_SHOW_INPUT_CODE = "showInputCode";
	public static final String ARG_INPUT_TITLE_CODE = "inputTitleCode";
	public static final String ARG_INPUT_MESSAGE_CODE = "inputMessageCode";
	public static final String ARG_SHOW_ERROR_CODE = "showErrorCode";
	public static final String ARG_ALERT_STYLE_CODE = "alertStyleCode";
	public static final String ARG_ERROR_TITLE_CODE = "errorTitleCode";
	public static final String ARG_ERROR_MESSAGE_CODE = "errorMessageCode";
		
	protected ListModelList<AllowPair> allowListmodel = new ListModelList<AllowPair>(AllowPair.values());
	protected ListModelList<DataPair> dataListmodel = new ListModelList<DataPair>(DataPair.values());
	protected ListModelList<AlertStyle> styleListmodel = new ListModelList<AlertStyle>(AlertStyle.values());
	
	@Wire
	protected Combobox allowCombobox;
	@Wire
	protected Combobox dataCombobox;
	@Wire
	protected Checkbox ignoreBlankCheckbox;
	@Wire
	protected Checkbox inCellDropdownCheckbox;
	@Wire
	protected Label inCellDropdownLabel;
	@Wire
	protected Textbox param1Textbox;
	@Wire
	protected Textbox param2Textbox;
	@Wire
	protected Label param1Label;
	@Wire
	protected Label param2Label;
	
	@Wire
	protected Checkbox inputMsgCheckbox;
	@Wire
	protected Textbox inputTitleTextbox;
	@Wire
	protected Textbox inputMsgTextbox;
	
	@Wire
	protected Checkbox errorMsgCheckbox;
	@Wire
	protected Combobox errorStyleCombobox;
	@Wire
	protected Textbox errorTitleTextbox;
	@Wire
	protected Textbox errorMsgTextbox;
	
	public static void show(EventListener<DialogCallbackEvent> callback, Validation validation, Spreadsheet ss) {
		Map arg = newArg(callback);
		arg.put(ARG_VALIDATION_CODE, validation);
		arg.put(ARG_SPREADSHEET, ss);
		
		Window comp = (Window)Executions.createComponents(URI, null, arg);
		comp.doModal();
		return;
	}
	
	public void doAfterCompose(Window comp) throws Exception {
		super.doAfterCompose(comp);
		
		Map arg = Executions.getCurrent().getArg();
		Validation validation = (Validation) arg.get(ARG_VALIDATION_CODE);
		
		initSettings(validation);
		initInputMsg(validation);
		initErrorAlert(validation);
		updateCascade();
		onCheck$msgCheckbox();
		onCheck$errorMsgCheckbox();
	}

	private void initErrorAlert(Validation validation) {
		if(validation != null) {
			errorMsgCheckbox.setChecked(validation.isShowError());
			styleListmodel.addToSelection(AlertStyle.getAlertStyle(validation.getAlertStyle()));
			errorStyleCombobox.setModel(styleListmodel);
			errorTitleTextbox.setValue(validation.getErrorTitle());
			errorMsgTextbox.setValue(validation.getErrorMessage());
		} else {
			// default value
			initDefaultErrorAlert();
		}
	}

	private void initDefaultErrorAlert() {
		errorMsgCheckbox.setChecked(true);
		styleListmodel.addToSelection(AlertStyle.STOP);
		errorStyleCombobox.setModel(styleListmodel);
		errorTitleTextbox.setValue("");
		errorMsgTextbox.setValue("");
	}

	private void initInputMsg(Validation validation) {
		if(validation != null) {
			inputMsgCheckbox.setChecked(validation.isShowInput());
			inputTitleTextbox.setValue(validation.getInputTitle());
			inputMsgTextbox.setValue(validation.getInputMessage());
		} else {
			// default value
			initDefaultInputMsg();
		}
	}

	private void initDefaultInputMsg() {
		inputMsgCheckbox.setChecked(true);
		inputTitleTextbox.setValue("");
		inputMsgTextbox.setValue("");
	}

	private void initSettings(Validation validation) {
		if(validation != null) {
			AllowPair allowPair = AllowPair.getAllowPair(validation.getValidationType());
			DataPair dataPair = DataPair.getDataPair(validation.getOperatorType());
			
			allowListmodel.addToSelection(allowPair);
			allowCombobox.setModel(allowListmodel);
			
			dataListmodel.addToSelection(dataPair);
			dataCombobox.setModel(dataListmodel);
			
			param1Textbox.setValue(validation.getFormula1());
			param2Textbox.setValue(validation.getFormula2());
			
			ignoreBlankCheckbox.setChecked(validation.isIgnoreBlank());
			inCellDropdownCheckbox.setChecked(validation.isInCellDropdown());
		} else {
			// use default value
			AllowPair allowPair = AllowPair.ANY_VALUE;
			DataPair dataPair = DataPair.BETWEEN;
			
			allowListmodel.addToSelection(allowPair);
			allowCombobox.setModel(allowListmodel);
			
			dataListmodel.addToSelection(dataPair);
			dataCombobox.setModel(dataListmodel);
			
			param1Textbox.setValue("");
			param2Textbox.setValue("");
			
			ignoreBlankCheckbox.setChecked(true);
			inCellDropdownCheckbox.setChecked(true);
		}
	}
	
	private void updateCascade() {
		updateDataType();
		updateParameter();
		updateCheckbox();
	}

	private void updateCheckbox() {
		AllowPair allowPair = AllowPair.values()[allowListmodel.getSelection().iterator().next().ordinal()];
		
		boolean ignore = !allowPair.isIgnoreBlankEnabled();
		ignoreBlankCheckbox.setDisabled(ignore);
		boolean inCell = allowPair.isInCellCheckEnabled();
		inCellDropdownCheckbox.setVisible(inCell);
		inCellDropdownLabel.setVisible(inCell);
		inCellDropdownCheckbox.setChecked(inCell);
	}

	private void updateParameter() {
		AllowPair allowPair = AllowPair.values()[allowListmodel.getSelection().iterator().next().ordinal()];
		DataPair dataPair = DataPair.values()[dataListmodel.getSelection().iterator().next().ordinal()];
		
		boolean isParam1Enabled = allowPair.isParam1Enabled();
		boolean isParam2Enabled = allowPair.isParam2Enabled();
		
		boolean twoParams = dataPair == DataPair.BETWEEN || dataPair == DataPair.NOT_BETWEEN;
		boolean pointParam = dataPair == DataPair.EQUAL_TO || dataPair == DataPair.NOT_EQUAL_TO || allowPair == AllowPair.CUSTOM || allowPair == AllowPair.LIST;
		boolean minParam = twoParams || dataPair == DataPair.GREATER_EUQAL || dataPair == DataPair.GREATER_THAN;
		boolean maxParam = !(pointParam || minParam);
		
		param1Label.setVisible(isParam1Enabled);
		param1Textbox.setVisible(isParam1Enabled);
		param2Label.setVisible(isParam2Enabled && twoParams);
		param2Textbox.setVisible(isParam2Enabled && twoParams);
		
		if(isParam1Enabled) {
			if(pointParam)
				param1Label.setValue(allowPair.getPointName());
			else if(minParam) {
				param1Label.setValue(allowPair.getMinimumName());				
			} else if(maxParam) {
				param1Label.setValue(allowPair.getMaximumName());
			}
		}
			
		
		if(isParam2Enabled)
			param2Label.setValue(allowPair.getMaximumName());
	}

	private void updateDataType() {
		AllowPair allowPair = AllowPair.values()[allowListmodel.getSelection().iterator().next().ordinal()];
		dataCombobox.setDisabled(!allowPair.isDataEnabled());
	}
	
	@Listen("onSelect=#allowCombobox")
	public void onSelect$allowCombobox() {
		updateCascade();
	}
	
	@Listen("onSelect=#dataCombobox")
	public void onSelect$dataCombobox() {
		updateCheckbox();
		updateParameter();
	}
	
	@Listen("onCheck=#inputMsgCheckbox")
	public void onCheck$msgCheckbox() {
		boolean checked = inputMsgCheckbox.isChecked();
		inputTitleTextbox.setDisabled(!checked);
		inputMsgTextbox.setDisabled(!checked);
	}
	
	@Listen("onCheck=#errorMsgCheckbox")
	public void onCheck$errorMsgCheckbox() {
		boolean checked = errorMsgCheckbox.isChecked();
		errorStyleCombobox.setDisabled(!checked);
		errorTitleTextbox.setDisabled(!checked);
		errorMsgTextbox.setDisabled(!checked);
	}

	@Listen("onOK=window; onClick=#okBtn")
	public void onClick$okBtn() {
		ValidationType validationType = allowListmodel.getSelection().iterator().next().getType();
		OperatorType operatorType = dataListmodel.getSelection().iterator().next().getType();
		boolean ignoreBlank = ignoreBlankCheckbox.isChecked();
		boolean inCellDropDown = inCellDropdownCheckbox.isChecked();
		String param1 = param1Textbox.getValue();
		String param2 = param2Textbox.getValue();
		
		boolean isShowInput = inputMsgCheckbox.isChecked();
		String inputTitle = inputTitleTextbox.getValue();
		String inputMessage = inputMsgTextbox.getValue();
		
		boolean isShowError = errorMsgCheckbox.isChecked();
		org.zkoss.zss.api.model.Validation.AlertStyle alertStyle = styleListmodel.getSelection().iterator().next().getStyle();
		String errorTitle = errorTitleTextbox.getValue();
		String errorMessage = errorMsgTextbox.getValue();
		
		try{
			sendCallback(ON_OK, newMap(
				newEntry(ARG_VALIDATION_TYPE_CODE, validationType),
				newEntry(ARG_OPERATOR_TYPE_CODE, operatorType),
				newEntry(ARG_IGNORE_BLANK_CODE, ignoreBlank),
				newEntry(ARG_IN_CELL_DROPDOWN_CODE, inCellDropDown),
				newEntry(ARG_PARAM1_CODE, param1),
				newEntry(ARG_PARAM2_CODE, param2),
				newEntry(ARG_SHOW_INPUT_CODE, isShowInput),
				newEntry(ARG_INPUT_TITLE_CODE, inputTitle),
				newEntry(ARG_INPUT_MESSAGE_CODE, inputMessage),
				newEntry(ARG_SHOW_ERROR_CODE, isShowError),
				newEntry(ARG_ALERT_STYLE_CODE, alertStyle),
				newEntry(ARG_ERROR_TITLE_CODE, errorTitle),
				newEntry(ARG_ERROR_MESSAGE_CODE, errorMessage)
			));
		} catch(InvalidDataValidationException e) {
			Messagebox.show(e.getMessage(), "Warning", Messagebox.OK, Messagebox.EXCLAMATION);
			return;
		}
		
		detach();
	}
	
	@Listen("onCancel=window; onClick=#cancelBtn")
	public void onCancel() {
		detach();
		postCallback(ON_CANCEL, null);
	}
	
	@Listen("onClick=#clearBtn")
	public void onClear() {
		allowListmodel.clearSelection();
		allowListmodel.addToSelection(AllowPair.ANY_VALUE);
		
		initDefaultInputMsg();
		initDefaultErrorAlert();
		
		updateCascade();
		onCheck$msgCheckbox();
		onCheck$errorMsgCheckbox();
	}
	
	enum AllowPair {
		ANY_VALUE(Labels.getLabel("zssex.dlg.format.dataValidation.settings.allow.anyValue"), ValidationType.ANY, false, false, false, 
				"", "", "", false, false),
		WHOLE_NUMBER(Labels.getLabel("zssex.dlg.format.dataValidation.settings.allow.wholeNumber"), ValidationType.INTEGER, true, true, false, 
				Labels.getLabel("zssex.dlg.format.dataValidation.settings.value"), Labels.getLabel("zssex.dlg.format.dataValidation.settings.minimum"), Labels.getLabel("zssex.dlg.format.dataValidation.settings.maximum"), true, true),
		DECIMAL(Labels.getLabel("zssex.dlg.format.dataValidation.settings.allow.decimal"), ValidationType.DECIMAL, true, true, false, 
				Labels.getLabel("zssex.dlg.format.dataValidation.settings.value"), Labels.getLabel("zssex.dlg.format.dataValidation.settings.minimum"), Labels.getLabel("zssex.dlg.format.dataValidation.settings.maximum"), true, true),
		LIST(Labels.getLabel("zssex.dlg.format.dataValidation.settings.allow.list"), ValidationType.LIST, false, true, true, 
				Labels.getLabel("zssex.dlg.format.dataValidation.settings.source"), "", "", true, false),
		DATE(Labels.getLabel("zssex.dlg.format.dataValidation.settings.allow.date"), ValidationType.DATE, true, true, false, 
				Labels.getLabel("zssex.dlg.format.dataValidation.settings.date"), Labels.getLabel("zssex.dlg.format.dataValidation.settings.startDate"), Labels.getLabel("zssex.dlg.format.dataValidation.settings.endDate"), true, true),
		TIME(Labels.getLabel("zssex.dlg.format.dataValidation.settings.allow.time"), ValidationType.TIME, true, true, false, 
				Labels.getLabel("zssex.dlg.format.dataValidation.settings.time"), Labels.getLabel("zssex.dlg.format.dataValidation.settings.startTime"), Labels.getLabel("zssex.dlg.format.dataValidation.settings.endTime"), true, true),
		TEXT_LENGTH(Labels.getLabel("zssex.dlg.format.dataValidation.settings.allow.textLength"), ValidationType.TEXT_LENGTH, true, true, false, 
				Labels.getLabel("zssex.dlg.format.dataValidation.settings.length"), Labels.getLabel("zssex.dlg.format.dataValidation.settings.minimum"), Labels.getLabel("zssex.dlg.format.dataValidation.settings.maximum"), true, true),
		CUSTOM(Labels.getLabel("zssex.dlg.format.dataValidation.settings.allow.custom"), ValidationType.CUSTOM, false, true, false, 
				Labels.getLabel("zssex.dlg.format.dataValidation.settings.formula"), "", "", true, false);
		
		String title;
		ValidationType type;
		boolean dataEnabled;
		boolean ignoreBlankEnabled;
		boolean inCellCheckEnabled;
		String pointName;
		String minimumName;
		String maximumName;
		boolean param1Enabled;
		boolean param2Enabled;
		
		AllowPair(
			String title,
			ValidationType type,
			boolean dataEnabled,
			boolean ignoreBlankEnabled,
			boolean inCellCheckEnabled,
			String pointName,
			String minimumName,
			String maximumName,
			boolean param1Enabled,
			boolean param2Enabled
		) {
			this.title = title;
			this.type = type;
			this.dataEnabled = dataEnabled;
			this.ignoreBlankEnabled = ignoreBlankEnabled;
			this.inCellCheckEnabled = inCellCheckEnabled;
			this.pointName = pointName;
			this.minimumName = minimumName;
			this.maximumName = maximumName;
			this.param1Enabled = param1Enabled;
			this.param2Enabled = param2Enabled;
		}
		
		public static AllowPair getAllowPair(ValidationType type) {
			for (AllowPair pair : AllowPair.values()) {
				if(pair.type == type)
					return pair;
			}
			return null;
		}

		public ValidationType getType() {
			return type;
		}

		public boolean isDataEnabled() {
			return dataEnabled;
		}
		
		public boolean isIgnoreBlankEnabled() {
			return ignoreBlankEnabled;
		}

		public boolean isInCellCheckEnabled() {
			return inCellCheckEnabled;
		}

		public String getPointName() {
			return pointName;
		}

		public String getMinimumName() {
			return minimumName;
		}

		public String getMaximumName() {
			return maximumName;
		}

		public boolean isParam1Enabled() {
			return param1Enabled;
		}

		public boolean isParam2Enabled() {
			return param2Enabled;
		}
		
		public String toString() {
			return title;
		}
	}
	
	enum DataPair {
		BETWEEN(Labels.getLabel("zssex.dlg.format.dataValidation.settings.data.between"), OperatorType.BETWEEN),
		NOT_BETWEEN(Labels.getLabel("zssex.dlg.format.dataValidation.settings.data.notBetween"), OperatorType.NOT_BETWEEN),
		EQUAL_TO(Labels.getLabel("zssex.dlg.format.dataValidation.settings.data.equalTo"), OperatorType.EQUAL),
		NOT_EQUAL_TO(Labels.getLabel("zssex.dlg.format.dataValidation.settings.data.notEqualTo"), OperatorType.NOT_EQUAL),
		GREATER_THAN(Labels.getLabel("zssex.dlg.format.dataValidation.settings.data.greaterThan"), OperatorType.GREATER_THAN),
		LESS_THAN(Labels.getLabel("zssex.dlg.format.dataValidation.settings.data.lessThan"), OperatorType.LESS_THAN),
		GREATER_EUQAL(Labels.getLabel("zssex.dlg.format.dataValidation.settings.data.greaterOrEqual"), OperatorType.GREATER_OR_EQUAL),
		LESS_EQUAL(Labels.getLabel("zssex.dlg.format.dataValidation.settings.data.lessOrEqual"), OperatorType.LESS_OR_EQUAL);
		
		String title;
		OperatorType type;
		
		DataPair(String title, OperatorType type) {
			this.title = title;
			this.type = type;
		}
		
		public static DataPair getDataPair(OperatorType type) {
			for (DataPair pair : DataPair.values()) {
				if(pair.type == type)
					return pair;
			}
			return null;
		}

		public OperatorType getType() {
			return type;
		}
		
		public String toString() {
			return title;
		}
	}
	
	enum AlertStyle {
		STOP(Labels.getLabel("zssex.dlg.format.dataValidation.error.alert.stop"), org.zkoss.zss.api.model.Validation.AlertStyle.STOP),
		WARNING(Labels.getLabel("zssex.dlg.format.dataValidation.error.alert.warning"), org.zkoss.zss.api.model.Validation.AlertStyle.WARNING),
		INFORMATION(Labels.getLabel("zssex.dlg.format.dataValidation.error.alert.info"), org.zkoss.zss.api.model.Validation.AlertStyle.INFO);
		
		org.zkoss.zss.api.model.Validation.AlertStyle style;
		String title;
		
		private AlertStyle(String title, org.zkoss.zss.api.model.Validation.AlertStyle style) {
			this.title = title;
			this.style = style;
		}
		
		public static AlertStyle getAlertStyle(org.zkoss.zss.api.model.Validation.AlertStyle style) {
			for (AlertStyle s : AlertStyle.values()) {
				if(s.style == style)
					return s;
			}
			return null;
		}

		public org.zkoss.zss.api.model.Validation.AlertStyle getStyle() {
			return style;
		}
		
		public String toString() {
			return title;
		}
	}
}