package org.zkoss.zssex.ui;

import org.zkoss.util.resource.Labels;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.UiException;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.SerializableEventListener;
import org.zkoss.zss.api.Range;
import org.zkoss.zss.api.Ranges;
import org.zkoss.zss.api.model.Sheet;
import org.zkoss.zss.api.model.Validation;
import org.zkoss.zss.api.model.Validation.AlertStyle;
import org.zkoss.zul.Messagebox;

/**
 * A validation helper to help cell validation
 * @author dennis
 *
 */
//Code refer from Spreadsheet.validate
public class ValidationHelper {

	private boolean isEventThreadEnabled() {
		return Executions.getCurrent().getDesktop().getWebApp()
				.getConfiguration().isEventThreadEnabled();
	}

	/**
	 * To help you validate the cell edit text, it returns true if validation passed or no validation rule.</br>
	 * You can also assign a callback listener if the cell is configured to show a error-box to ask user re-input the edit value again when validation fail.
	 * In this case, it will shows a dialog to ask user's input, and call the callback listener to let you process the new edit text again.
	 * @param sheet
	 * @param row
	 * @param col
	 * @param editText
	 * @param callback the callback listener for validation 
	 * @return true if validation passed, otherwise false.
	 */
	public boolean validate(Sheet sheet, final int row, final int col,
			final String editText, final EventListener callback) {
		final Range range = Ranges.range(sheet, row, col);
		final Validation dv = range.validate(editText);
		if (dv != null) {
			if (dv.isShowError() && callback!=null) {
				String errTitle = dv.getErrorTitle();
				String errText = dv.getErrorMessage();
				if (errTitle == null || errTitle.length() == 0) {
					errTitle = Labels.getLabel("zss.validation.warn_title");
				}
				if (errText == null || errText.length() == 0) {
					errText = Labels.getLabel("zss.validation.msg.invalid_value");
				}
				final AlertStyle errStyle = dv.getAlertStyle();
				switch (errStyle) {
				case STOP: {
					final int btn = Messagebox.show(errText, errTitle,
							Messagebox.RETRY | Messagebox.CANCEL,
							Messagebox.ERROR, Messagebox.RETRY,
							new EventListener() {
								public void onEvent(Event event)
										throws Exception {
									final String evtname = event.getName();
									if (Messagebox.ON_RETRY.equals(evtname)) {
										retry(callback);
									} else if (Messagebox.ON_CANCEL
											.equals(evtname)) {
										cancel(callback);
									}
								}
							});
				}
					break;
				case WARNING: {
					errText += "\n\nContinue?";
					final int btn = Messagebox.show(errText, errTitle,
							Messagebox.YES | Messagebox.NO | Messagebox.CANCEL,
							Messagebox.EXCLAMATION, Messagebox.NO,
							new SerializableEventListener() {
								private static final long serialVersionUID = -190628368458891084L;

								public void onEvent(Event event)
										throws Exception {
									final String evtname = event.getName();
									if (Messagebox.ON_NO.equals(evtname)) {
										retry(callback);
									} else if (Messagebox.ON_CANCEL
											.equals(evtname)) {
										cancel(callback);
									} else if (Messagebox.ON_YES
											.equals(evtname)) {
										ok(callback);
									}
								}
							});
					if (isEventThreadEnabled() && btn == Messagebox.YES) {
						return true;
					}
				}
					break;
				case INFO: {
					final int btn = Messagebox.show(errText, errTitle,
							Messagebox.OK | Messagebox.CANCEL,
							Messagebox.INFORMATION, Messagebox.OK,
							new SerializableEventListener() {
								private static final long serialVersionUID = 631067465723387202L;

								@Override
								public void onEvent(Event event)
										throws Exception {
									final String evtname = event.getName();
									if (Messagebox.ON_CANCEL.equals(evtname)) {
										cancel(callback);
									} else if (Messagebox.ON_OK.equals(evtname)) {
										ok(callback);
									}
								}
							});
					if (isEventThreadEnabled() && btn == Messagebox.OK) {
						return true;
					}
				}
					break;
				}//end switch (errStyle) {
			}//end if (dv.getShowErrorBox()) {
			else if(callback!=null){
				//should call cancel directly if it is not showError
				cancel(callback);
			}
			return false;
		}//end if (dv != null) {
		return true;
	}

	private void errorBoxCallback(EventListener callback, String eventname) {
		if (!isEventThreadEnabled() && callback != null) {
			try {
				callback.onEvent(new Event(eventname, null));
			} catch (Exception e) {
				throw UiException.Aide.wrap(e);
			}
		}
	}

	// when user press OK/YES button of the validation ErrorBox, have to call
	// back to resend the setEditText() operation
	private void ok(EventListener callback) {
		errorBoxCallback(callback, Messagebox.ON_OK);
	}

	// when user press RETRY/NO button of the validation ErrorBox, have to call
	// back to handle UI operation
	private void retry(EventListener callback) {
		// TODO: shall set focus back to cell at (row, col), select the text,
		// enter edit mode
		errorBoxCallback(callback, Messagebox.ON_RETRY);
	}

	// when user press CANCEL button of the validation ErrorBox, have to call
	// back to handle UI operation
	private void cancel(EventListener callback) {
		// TODO: shall set focus back to cell at (row, col) and restore cell
		// value
		errorBoxCallback(callback, Messagebox.ON_CANCEL);
	}
}
