package org.gudy.azureus2.ui.swt.shells;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Monitor;
import org.eclipse.swt.widgets.Shell;
import org.gudy.azureus2.core3.util.AERunnable;

public class ShellSlider
{

	private final boolean DEBUG = false;

	private int STEP = 8;

	private int PAUSE = 30;

	private Shell shell;

	private Rectangle shellBounds = null;

	private Rectangle endBounds;

	private final int direction;

	private final boolean slideIn;

	/**
	 * Slide In
	 * 
	 * @param shell
	 * @param direction 
	 * @param endBounds 
	 */
	public ShellSlider(final Shell shell, int direction, final Rectangle endBounds) {
		this.shell = shell;
		this.endBounds = endBounds;
		this.slideIn = true;
		this.direction = direction;

		if (shell == null || shell.isDisposed())
			return;

		Display display = shell.getDisplay();
		display.syncExec(new Runnable() {
			public void run() {
				if (shell.isDisposed())
					return;

				switch (ShellSlider.this.direction) {
					case SWT.UP:
					default:
						shell.setLocation(endBounds.x, endBounds.y);
						Rectangle displayBounds = null;
						try {
							boolean ok = false;
							Monitor[] monitors = shell.getDisplay().getMonitors();
							for (int i = 0; i < monitors.length; i++) {
								Monitor monitor = monitors[i];
								displayBounds = monitor.getBounds();
								if (displayBounds.contains(endBounds.x, endBounds.y)) {
									ok = true;
									break;
								}
							}
							if (!ok) {
								displayBounds = shell.getMonitor().getBounds();
							}
						} catch (Throwable t) {
							displayBounds = shell.getDisplay().getBounds();
						}

						shellBounds = new Rectangle(endBounds.x, displayBounds.y
								+ displayBounds.height, endBounds.width, 0);
						break;
				}
				shell.setBounds(shellBounds);
				shell.setVisible(true);

				if (DEBUG)
					System.out.println("Slide In: " + shell.getText());
			}
		});
	}

	/**
	 * Slide Out
	 * 
	 * @param shell
	 * @param direction
	 */
	public ShellSlider(final Shell shell, int direction) {
		this.shell = shell;
		this.slideIn = false;
		this.direction = direction;
		if (DEBUG && canContinue())
			shell.getDisplay().syncExec(new Runnable() {
				public void run() {
					System.out.println("Slide Out: " + shell.getText());
				}
			});
	}

	private boolean canContinue() {
		if (shell == null || shell.isDisposed())
			return false;

		if (shellBounds == null)
			return true;

		//System.out.println((slideIn ? "In" : "Out") + ";" + direction + ";S:" + shellBounds + ";" + endBounds);
		if (slideIn) {
			if (direction == SWT.UP) {
				return shellBounds.y > endBounds.y;
			}
			// TODO: Other directions
		} else {
			if (direction == SWT.RIGHT) {
				// stop early, because some OSes have trim, and won't allow the window
				// to go smaller than it.
				return shellBounds.width > 10;
			}
		}
		return false;
	}

	public void run() {

		while (canContinue()) {
			long lStartedAt = System.currentTimeMillis();

			shell.getDisplay().syncExec(new AERunnable() {
				public void runSupport() {
					if (shell == null || shell.isDisposed()) {
						return;
					}

					if (shellBounds == null) {
						shellBounds = shell.getBounds();
					}

					int delta;
					if (slideIn) {
						switch (direction) {
							case SWT.UP:
								delta = Math.min(endBounds.height - shellBounds.height, STEP);
								shellBounds.height += delta;
								delta = Math.min(shellBounds.y - endBounds.y, STEP);
								shellBounds.y -= delta;
								break;

							default:
								break;
						}
					} else {
						switch (direction) {
							case SWT.RIGHT:
								delta = Math.min(shellBounds.width, STEP);
								shellBounds.width -= delta;
								shellBounds.x += delta;

								if (shellBounds.width == 0) {
									shell.dispose();
									return;
								}
								break;

							default:
								break;
						}
					}

					shell.setBounds(shellBounds);
					shell.update();
				}
			});

			try {
				long lDrawTime = System.currentTimeMillis() - lStartedAt;
				long lSleepTime = PAUSE - lDrawTime;
				if (lSleepTime < 15) {
					double d = (lDrawTime + 15.0) / PAUSE;
					PAUSE *= d;
					STEP *= d;
					lSleepTime = 15;
				}
				Thread.sleep(lSleepTime);
			} catch (Exception e) {
			}
		}
	}
}