/* Clients.java

	Purpose:
		
	Description:
		
	History:
		Fri May 26 14:25:06     2006, Created by tomyeh

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

{{IS_RIGHT
	This program is distributed under LGPL Version 2.1 in the hope that
	it will be useful, but WITHOUT ANY WARRANTY.
}}IS_RIGHT
*/
package org.zkoss.zk.ui.util;

import java.util.List;
import java.util.Locale;

import org.zkoss.web.servlet.http.Encodes;
import org.zkoss.zk.au.AuResponse;
import org.zkoss.zk.au.out.AuAlert;
import org.zkoss.zk.au.out.AuClearBusy;
import org.zkoss.zk.au.out.AuClearWrongValue;
import org.zkoss.zk.au.out.AuConfirmClose;
import org.zkoss.zk.au.out.AuInvoke;
import org.zkoss.zk.au.out.AuLoadCSS;
import org.zkoss.zk.au.out.AuLoadScript;
import org.zkoss.zk.au.out.AuLog;
import org.zkoss.zk.au.out.AuMoveBy;
import org.zkoss.zk.au.out.AuMoveTo;
import org.zkoss.zk.au.out.AuNotification;
import org.zkoss.zk.au.out.AuPrint;
import org.zkoss.zk.au.out.AuResizeBy;
import org.zkoss.zk.au.out.AuResizeTo;
import org.zkoss.zk.au.out.AuResizeWidget;
import org.zkoss.zk.au.out.AuScript;
import org.zkoss.zk.au.out.AuScrollBy;
import org.zkoss.zk.au.out.AuScrollIntoView;
import org.zkoss.zk.au.out.AuScrollTo;
import org.zkoss.zk.au.out.AuShowBusy;
import org.zkoss.zk.au.out.AuSubmitForm;
import org.zkoss.zk.au.out.AuSyncErrorbox;
import org.zkoss.zk.au.out.AuWrongValue;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Execution;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.Page;
import org.zkoss.zk.ui.sys.ExecutionCtrl;

/**
 * Utilities to send {@link AuResponse} to the client.
 *
 * <p>Utilities here are mainly to control how the client (a.k.a., the browser window)
 * behaves. To get the status, you might refer to {@link org.zkoss.zk.ui.event.ClientInfoEvent}.
 *
 * @author tomyeh
 * @see org.zkoss.zk.ui.event.ClientInfoEvent
 */
public class Clients {
	/** Sends an AU response ({@link AuResponse}) to the client.
	 * It is the same as <code>response(response.getOverrideKey(), response)</code>.
	 *
	 * @since 3.0.0
	 */
	public static final void response(AuResponse response) {
		Executions.getCurrent().addAuResponse(response);
	}

	/** Sends an AU response ({@link AuResponse}) to the client
	 * with the given key (instead of {@link AuResponse#getOverrideKey}).
	 *
	 * @param key could be anything. The second invocation of this method
	 * in the same execution with the same key will override the previous one.
	 * In other words, the previous one will be dropped.
	 * If null is specified, the response is simply appended to the end
	 * without overriding any previous one.
	 * @since 3.0.4
	 * @see #response
	 */
	public static final void response(String key, AuResponse response) {
		Executions.getCurrent().addAuResponse(key, response);
	}

	/** Asks the browser to confirm users whether to close the browser window.
	 *
	 * <p>If an non-null (non-empty) string is set, the browser will show up
	 * a confirmation dialog when an user tries to close the browser window,
	 * or browse to another URL.
	 * To reset (i.e., not showing any confirmation dialog), just call this
	 * method again with null.
	 *
	 * @param mesg the message to show when confirming users.
	 * If null (default) or empty, users can close the browser window directly.
	 */
	public static final void confirmClose(String mesg) {
		response(new AuConfirmClose(mesg));
	}

	/** Shows an error message at the browser.
	 * It works and looks similar to {@link org.zkoss.zul.Messagebox}.
	 * However, it is not customizable (at the server), but it is
	 * much faster and light-weighted.
	 * @since 5.0.3
	 */
	public static final void alert(String msg) {
		response(new AuAlert(msg));
	}

	/** Shows an error message at the browser.
	 * It is similar to {@link org.zkoss.zul.Messagebox}.
	 * @param msg the message to display.
	 * @param title the title of the message box
	 * @param icon the icon to show. It could null,
	 "QUESTION", "EXCLAMATION", "INFORMATION", "ERROR", "NONE".
	 * If null, "ERROR" is assumed
	 * @since 5.0.3
	 */
	public static final void alert(String msg, String title, String icon) {
		response(new AuAlert(msg, title, icon));
	}

	/** Shows an error message for the specified component, if any,
	 * at the browser.
	 * <p>You have to clear the error message manually with {@link #clearWrongValue}.
	 * @since 5.0.0
	 */
	public static final void wrongValue(Component comp, String msg) {
		response(new AuWrongValue(comp, msg));
	}

	/** Closes the error message of the specified component, if any,
	 * at the browser.
	 * @since 5.0.0
	 */
	public static final void clearWrongValue(Component comp) {
		response(new AuClearWrongValue(comp));
	}

	/** Closes the error message of the specified components, if any,
	 * at the browser.
	 * @since 5.0.0
	 */
	public static final void clearWrongValue(List<Component> comps) {
		response(new AuClearWrongValue(comps)); //append, not overwrite
	}

	/** Closes the error message of the specified components, if any,
	 * at the browser.
	 * @since 5.0.0
	 */
	public static final void clearWrongValue(Component[] comps) {
		response(new AuClearWrongValue(comps)); //append, not overwrite
	}

	/** Submits the form with the specified ID.
	 */
	public static final void submitForm(String formId) {
		response(new AuSubmitForm(formId));
	}

	/** Submits the form with the specified form.
	 * It assumes the form component is a HTML form.
	 */
	public static final void submitForm(Component form) {
		submitForm(form.getUuid());
	}

	/** Asks the client to print the current desktop (a.k.a., browser window).
	 */
	public static void print() {
		response(new AuPrint());
	}

	/** Scrolls the ancestor elements to make the specified element visible.
	 * @since 3.6.1
	 */
	public static final void scrollIntoView(Component cmp) {
		response(new AuScrollIntoView(cmp));
	}

	/** Scrolls the current desktop (a.k.a., browser window) by the specified number of pixels.
	 * If the number passed is positive, the desktop is scrolled down.
	 * If negative, it is scrolled up.
	 * @see #scrollTo
	 */
	public static final void scrollBy(int x, int y) {
		response(new AuScrollBy(x, y));
	}

	/** Scrolls the current desktop (a.k.a., browser window) to the specified location (in pixels).
	 *
	 * @see #scrollBy
	 */
	public static final void scrollTo(int x, int y) {
		response(new AuScrollTo(x, y));
	}

	/** Resizes the current desktop (a.k.a., browser window) by the specified number of pixels.
	 * If the numbers passed are positive, the desktop size is increased.
	 * Negative numbers reduce the size of the desktop.
	 *
	 * @see #resizeTo
	 */
	public static final void resizeBy(int x, int y) {
		response(new AuResizeBy(x, y));
	}

	/** Resizes the current desktop (a.k.a., browser window) to the specified size (in pixels).
	 *
	 * @see #resizeBy
	 */
	public static final void resizeTo(int x, int y) {
		response(new AuResizeTo(x, y));
	}

	/** Moves the current desktop (a.k.a., browser window) by the specified number of pixels.
	 * If the number passed is positive, the desktop is moved down.
	 * If negative, it is moved up.
	 * @see #moveTo
	 */
	public static final void moveBy(int x, int y) {
		response(new AuMoveBy(x, y));
	}

	/** Moves the current desktop (a.k.a., browser window) to the specified location (in pixels).
	 *
	 * @see #moveBy
	 */
	public static final void moveTo(int x, int y) {
		response(new AuMoveTo(x, y));
	}

	/** Asks the browser to evaluate the specified JavaScript.
	 * <p>It has no effect if the client doesn't support JavaScript.
	 *
	 * @param javaScript the javaScript codes to run at the browser
	 */
	public static final void evalJavaScript(String javaScript) {
		response(new AuScript(null, javaScript));
	}

	/**
	 * Notification type: information
	 */
	public static final String NOTIFICATION_TYPE_INFO = "info";

	/**
	 * Notification type: warning
	 */
	public static final String NOTIFICATION_TYPE_WARNING = "warning";

	/**
	 * Notification type: error
	 */
	public static final String NOTIFICATION_TYPE_ERROR = "error";

	/**
	 * Shows a message at the center of the browser window.
	 * @param msg the message to show
	 * @since 6.0.1
	 */
	public static final void showNotification(String msg) {
		showNotification(msg, null, null, null, -1, false);
	}

	/**
	 * Shows a message at the center of the browser window.
	 * @param msg the message to show
	 * @param closable whether to close notification manually or not. If true there will be a
	 * close button on notification message and won't close until user click the button 
	 * or duration time up, default false.
	 * @since 6.5.0
	 */
	public static final void showNotification(String msg, boolean closable) {
		showNotification(msg, null, null, null, -1, closable);
	}

	/**
	 * Shows a message at the right side of the given component.
	 * @param msg the message to show
	 * @param ref the referenced component, null to be based on browser window
	 * @since 6.0.1
	 */
	public static final void showNotification(String msg, Component ref) {
		showNotification(msg, null, ref, null, -1, false);
	}

	/**
	 * Shows a message at the right side of the given component.
	 * @param msg the message to show
	 * @param ref the referenced component, null to be based on browser window
	 * @param closable whether to close notification manually or not. If true there will be a
	 * close button on notification message and won't close until user click the button 
	 * or duration time up, default false.
	 * @since 6.5.0
	 */
	public static final void showNotification(String msg, Component ref, boolean closable) {
		showNotification(msg, null, ref, null, -1, closable);
	}

	/**
	 * Displays a message.
	 * @param msg the message to show
	 * @param type available types are "info", "warning", "error"
	 * @param ref the referenced component, null to be based on browser window
	 * @param position predefined positions.
	 * <p> Available options are:
	 * <ul>
	 * 	<li><b>before_start</b><br/> the message appears above the anchor, aligned to the left.</li>
	 * 	<li><b>before_center</b><br/> the message appears above the anchor, aligned to the center.</li>
	 *  <li><b>before_end</b><br/> the message appears above the anchor, aligned to the right.</li>
	 *  <li><b>after_start</b><br/> the message appears below the anchor, aligned to the left.</li>
	 *  <li><b>after_center</b><br/> the message appears below the anchor, aligned to the center.</li>
	 *  <li><b>after_end</b><br/> the message appears below the anchor, aligned to the right.</li>
	 *  <li><b>start_before</b><br/> the message appears to the left of the anchor, aligned to the top.</li>
	 *  <li><b>start_center</b><br/> the message appears to the left of the anchor, aligned to the middle.</li>
	 *  <li><b>start_after</b><br/> the message appears to the left of the anchor, aligned to the bottom.</li>
	 *  <li><b>end_before</b><br/> the message appears to the right of the anchor, aligned to the top.</li>
	 *  <li><b>end_center</b><br/> the message appears to the right of the anchor, aligned to the middle.</li>
	 *  <li><b>end_after</b><br/> the message appears to the right of the anchor, aligned to the bottom.</li>
	 *  <li><b>overlap/top_left</b><br/> the message overlaps the anchor, with anchor and message aligned at top-left.</li>
	 *  <li><b>top_center</b><br/> the message overlaps the anchor, with anchor and message aligned at top-center.</li>
	 *  <li><b>overlap_end/top_right</b><br/> the message overlaps the anchor, with anchor and message aligned at top-right.</li>
	 *  <li><b>middle_left</b><br/> the message overlaps the anchor, with anchor and message aligned at middle-left.</li>
	 *  <li><b>middle_center</b><br/> the message overlaps the anchor, with anchor and message aligned at middle-center.</li>
	 *  <li><b>middle_right</b><br/> the message overlaps the anchor, with anchor and message aligned at middle-right.</li>
	 *  <li><b>overlap_before/bottom_left</b><br/> the message overlaps the anchor, with anchor and message aligned at bottom-left.</li>
	 *  <li><b>bottom_center</b><br/> the message overlaps the anchor, with anchor and message aligned at bottom-center.</li>
	 *  <li><b>overlap_after/bottom_right</b><br/> the message overlaps the anchor, with anchor and message aligned at bottom-right.</li>
	 *  <li><b>at_pointer</b><br/> the message appears with the upper-left aligned with the mouse cursor.</li>
	 *  <li><b>after_pointer</b><br/> the message appears with the top aligned with
	 *  	the bottom of the mouse cursor, with the left side of the message at the horizontal position of the mouse cursor.</li>
	 * </ul></p>
	 * @param duration the duration of notification in millisecond. If zero or 
	 * negative the notification does not dismiss until user left-clicks outside 
	 * of the notification box.
	 * @since 6.0.1
	 */
	public static final void showNotification(String msg, String type, Component ref, String position, int duration) {
		showNotification(msg, type, ref, position, duration, false);
	}

	/**
	 * Displays a message.
	 * 
	 * @param msg the message to show
	 * @param type available types are "info", "warning", "error"
	 * @param ref the referenced component, null to be based on browser window
	 * @param position predefined positions.
	 * <p> Available options are:
	 * <ul>
	 * 	<li><b>before_start</b><br/> the message appears above the anchor, aligned to the left.</li>
	 * 	<li><b>before_center</b><br/> the message appears above the anchor, aligned to the center.</li>
	 *  <li><b>before_end</b><br/> the message appears above the anchor, aligned to the right.</li>
	 *  <li><b>after_start</b><br/> the message appears below the anchor, aligned to the left.</li>
	 *  <li><b>after_center</b><br/> the message appears below the anchor, aligned to the center.</li>
	 *  <li><b>after_end</b><br/> the message appears below the anchor, aligned to the right.</li>
	 *  <li><b>start_before</b><br/> the message appears to the left of the anchor, aligned to the top.</li>
	 *  <li><b>start_center</b><br/> the message appears to the left of the anchor, aligned to the middle.</li>
	 *  <li><b>start_after</b><br/> the message appears to the left of the anchor, aligned to the bottom.</li>
	 *  <li><b>end_before</b><br/> the message appears to the right of the anchor, aligned to the top.</li>
	 *  <li><b>end_center</b><br/> the message appears to the right of the anchor, aligned to the middle.</li>
	 *  <li><b>end_after</b><br/> the message appears to the right of the anchor, aligned to the bottom.</li>
	 *  <li><b>overlap/top_left</b><br/> the message overlaps the anchor, with anchor and message aligned at top-left.</li>
	 *  <li><b>top_center</b><br/> the message overlaps the anchor, with anchor and message aligned at top-center.</li>
	 *  <li><b>overlap_end/top_right</b><br/> the message overlaps the anchor, with anchor and message aligned at top-right.</li>
	 *  <li><b>middle_left</b><br/> the message overlaps the anchor, with anchor and message aligned at middle-left.</li>
	 *  <li><b>middle_center</b><br/> the message overlaps the anchor, with anchor and message aligned at middle-center.</li>
	 *  <li><b>middle_right</b><br/> the message overlaps the anchor, with anchor and message aligned at middle-right.</li>
	 *  <li><b>overlap_before/bottom_left</b><br/> the message overlaps the anchor, with anchor and message aligned at bottom-left.</li>
	 *  <li><b>bottom_center</b><br/> the message overlaps the anchor, with anchor and message aligned at bottom-center.</li>
	 *  <li><b>overlap_after/bottom_right</b><br/> the message overlaps the anchor, with anchor and message aligned at bottom-right.</li>
	 *  <li><b>at_pointer</b><br/> the message appears with the upper-left aligned with the mouse cursor.</li>
	 *  <li><b>after_pointer</b><br/> the message appears with the top aligned with
	 *  	the bottom of the mouse cursor, with the left side of the message at the horizontal position of the mouse cursor.</li>
	 * </ul></p>
	 * @param duration the duration of notification in millisecond. If zero or 
	 * negative the notification does not dismiss until user left-clicks outside 
	 * of the notification box.
	 * @param closable whether to close notification manually or not. If true there will be a
	 * close button on notification message and won't close until user click the button 
	 * or duration time up, default false.
	 * @since 6.5.0
	 */
	public static final void showNotification(String msg, String type, Component ref, String position, int duration,
			boolean closable) {
		Execution exec = Executions.getCurrent();
		Page page = ref != null ? ref.getPage() : null;
		if (page == null && exec instanceof ExecutionCtrl)
			page = ((ExecutionCtrl) exec).getCurrentPage();
		if (type == null)
			type = NOTIFICATION_TYPE_INFO;
		response(new AuNotification(msg, type, page, ref, position, duration, closable));
	}

	/**
	 * Displays a message.
	 * @param msg the message to show
	 * @param type available types are "info", "warning", "error"
	 * @param ref the referenced component, null to be based on browser window
	 * @param x the horizontal position of the notification, aligned at top-left (in pixel)
	 * @param y the vertical position of the notification, aligned at top-left (in pixel)
	 * @param duration the duration of notification in millisecond. If zero or 
	 * negative the notification does not dismiss until user left-clicks outside 
	 * of the notification box.
	 * @since 6.0.1
	 */
	public static final void showNotification(String msg, String type, Component ref, int x, int y, int duration) {
		showNotification(msg, type, ref, x, y, duration, false);
	}

	/**
	 * Displays a message.
	 * @param msg the message to show
	 * @param type available types are "info", "warning", "error"
	 * @param ref the referenced component, null to be based on browser window
	 * @param x the horizontal position of the notification, aligned at top-left (in pixel)
	 * @param y the vertical position of the notification, aligned at top-left (in pixel)
	 * @param duration the duration of notification in millisecond. If zero or 
	 * negative the notification does not dismiss until user left-clicks outside 
	 * of the notification box.
	 * @param closable whether to close notification manually or not. If true there will be a
	 * close button on notification message and won't close until user click the button 
	 * or duration time up, default false.
	 * @since 6.5.0
	 */
	public static final void showNotification(String msg, String type, Component ref, int x, int y, int duration,
			boolean closable) {
		Execution exec = Executions.getCurrent();
		Page page = ref != null ? ref.getPage() : null;
		if (page == null && exec instanceof ExecutionCtrl)
			page = ((ExecutionCtrl) exec).getCurrentPage();
		if (type == null)
			type = NOTIFICATION_TYPE_INFO;
		response(new AuNotification(msg, type, page, ref, x, y, duration, closable));
	}

	/** Shows the busy message at the browser such that
	 * the user knows the system is busy.
	 *
	 * <p>It is usually used with {@link org.zkoss.zk.ui.event.Events#echoEvent}
	 * to prevent the user to click another buttons or components.
	 *
	 * <p>To cover only a particular component, use {@link #showBusy(Component, String)}.
	 * To close, use {@link #clearBusy()}.
	 *
	 * @param msg the message to show. If null, the default message (processing)
	 * is shown.
	 * @see #clearBusy()
	 * @since 5.0.0
	 */
	public static final void showBusy(String msg) {
		response(new AuShowBusy(msg));
	}

	/** Shows the busy message at the browser that covers only the specified
	 * component.
	 * It is used to denote a portion of the desktop is busy, and
	 * the user still can access the other part.
	 * In other words, it means there is a long operation taking place.
	 * <p>To execute a long operation asynchronously, the developer can use
	 * a working thread,
	 * or use {@link org.zkoss.zk.ui.event.EventQueue#subscribe(org.zkoss.zk.ui.event.EventListener,boolean)}.
	 * <p>See also <a href="http://books.zkoss.org/wiki/ZK_Developer%27s_Reference/UI_Patterns/Long_Operations">Long Operations</a>
	 *
	 * @param comp the component that the busy message to cover.
	 * Ignored if null. Notice that if the component is not found,
	 * the busy message won't be shown. In additions, the busy message
	 * is removed automatically if the component is detached later.
	 * To manually remove the busy message, use {@link #clearBusy(Component)}
	 * @param msg the message to show. If null, the default message (processing)
	 * is shown.
	 * @see #clearBusy(Component)
	 * @since 5.0.0
	 */
	public static final void showBusy(Component comp, String msg) {
		response(new AuShowBusy(comp, msg));
	}

	/** Cleans the busy message at the browser.
	 * @see #showBusy(String)
	 * @since 5.0.0
	 */
	public static final void clearBusy() {
		response(new AuClearBusy());
	}

	/** Clears the busy message at the browser that covers only the specified
	 * component.
	 * @param comp the component that the busy message to cover.
	 * @see #showBusy(Component, String)
	 * @since 5.0.0
	 */
	public static final void clearBusy(Component comp) {
		response(new AuClearBusy(comp));
	}

	/** Forces the client to re-calculate the size of the given component.
	 * For better performance, ZK Client Engine will cache the size of
	 * components with hflex=min or vflex=min, and recalculate it only
	 * necessary. However, sometimes it is hard (too costly) to know
	 * if the size of a component with flex=min is changed. For example,
	 * If another component is added to it and causes the size changed.
	 * In this case, you could use this method to enforce the re-calculation.
	 * @since 5.0.8
	 */
	public static final void resize(Component comp) {
		response(new AuResizeWidget(comp));
	}

	/** Reloads the client-side messages in the specified locale.
	 * It is used if you allow the user to change the locale dynamically.
	 *
	 * <p>Notice that this method only reloads the <i>standard</i> messages.
	 * The application has to update the component's content (such as labels)
	 * manually if necessary.
	 *
	 * <p>Limitation: it reloads only the messages of ZK Client Engine
	 * and ZUL components. It does not reload messages loaded by your
	 * own JavaScript codes.
	 *
	 * @param locale the locale. If null, {@link org.zkoss.util.Locales#getCurrent}
	 * is assumed.
	 * @exception UnsupportedOperationException if the device is not ajax.
	 * @since 3.6.3
	 */
	public static final void reloadMessages(Locale locale) throws java.io.IOException {
		Executions.getCurrent().getDesktop().getDevice().reloadMessages(locale);
	}

	/** Logs the message to the client.
	 *  <p>data[0]: the title
	 * @since 5.0.8
	 */
	public static final void log(String msg) {
		response(new AuLog(msg));
	}

	/** Logs the message to the client.
	 * @param msg message, cast to String
	 * @since 8.0.0
	 */
	public static final void log(Object msg) {
		response(new AuLog((String) msg));
	}

	/**
	 * Loads a JavaScript file to client and execute it.
	 * <p> By default, this method won't cache the JavaScript file, but you can
	 * enable it by invoking {@link #loadScript(String, String, boolean)}.
	 * @param url
	 *            the JavasScript file path, it will be encoded with
	 *            {@link Encodes#encodeURL(javax.servlet.ServletContext, javax.servlet.ServletRequest, javax.servlet.ServletResponse, String)}
	 *            if any.
	 * @since 8.0.0
	 * @see Encodes#encodeURL
	 * @see #loadScript(String, String, boolean)
	 */
	public static final void loadScript(String url) {
		response(new AuLoadScript(url, null, false));
	}

	/**
	 * Loads a JavaScript file to client and execute it.
	 * 
	 * @param url
	 *            the JavasScript file path, it will be encoded with
	 *            {@link Encodes#encodeURL(javax.servlet.ServletContext, javax.servlet.ServletRequest, javax.servlet.ServletResponse, String)}
	 *            if any.
	 * @param callback a callback script to execute when the JavaScript file has loaded.
	 * @param cache true means the url will be cached and executed only once.
	 * @since 8.0.0
	 * @see Encodes#encodeURL
	 */
	public static final void loadScript(String url, String callback, boolean cache) {
		response(new AuLoadScript(url, callback, cache));
	}

	/**
	 * Loads a CSS file.
	 * @param url the URL of the CSS file
	 * @since 8.0.0
	 */
	public static final void loadCSS(String url) {
		response(new AuLoadCSS(url));
	}

	/**
	 * Send a command to client (afterCommand)
	 * @param component target component
	 * @param commandName the command name which would be triggered in client
	 * @param data data which would be sent to client
	 * @since 8.0.0
	 */
	public static final void sendClientCommand(Component component, String commandName, Object data) {
		response(new AuInvoke(component, "$afterCommand", new Object[] { commandName, data }));
	}

	/**
	 * Send a command to sync all the errorboxes and its reference component position on the desktop.
	 * @since 8.5.2
	 */
	public static final void syncErrorbox () {
		response(new AuSyncErrorbox());
	}

	/**
	 * Send a command to sync a target component and its errorbox position.
	 * @param component target component
	 * @since 8.5.2
	 */
	public static final void syncErrorbox (Component component) {
		response(new AuSyncErrorbox(component));
	}
}
