/*******************************************************************************
 * Copyright (c) 2000, 2008 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.swt.widgets;

import org.eclipse.swt.*;
import org.eclipse.swt.graphics.*;

import intrinsic.Function;
import intrinsic.flash.display.BitmapData;
import intrinsic.flash.display.DisplayObject;
import intrinsic.flash.display.DisplayObjectContainer;
import intrinsic.flash.display.InteractiveObject;
import intrinsic.flash.events.TimerEvent;
import intrinsic.flash.ui.Keyboard;
import intrinsic.flash.utils.Timer;
import intrinsic.mx.core.Container;
import intrinsic.mx.core.Application;
import intrinsic.mx.core.UIComponent;
import intrinsic.mx.managers.CursorManager;
import intrinsic.mx.managers.CursorManagerPriority;
import intrinsic.mx.managers.IFocusManager;
import intrinsic.mx.managers.IFocusManagerComponent;

public class Display extends Device {

	Container root;

	Thread thread;
	Event [] eventQueue;
	EventTable eventTable, filterTable;
	Synchronizer synchronizer;
	Runnable [] disposeList;
	
	/* Display Data */
	Object data;
	String [] keys;
	Object [] values;

	/* System Tray */
	Tray tray;

	/* Current caret */
	Caret currentCaret;
	Timer caretTimer;
	
	/* Timers */
	Timer [] timerIds;
	Runnable [] timerList;
	
	/* Grab control */
	Control grabControl = null;

	/* Mouse down count */
	Control lastMouseDownControl;
	int lastMouseDownX;
	int lastMouseDownY;
	int lastMouseDownTime;
	int clickCount;
	
	/* Cursors */
	Cursor currentCursor;
	Cursor [] cursors = new Cursor [SWT.CURSOR_HAND + 1];

	static final String SWT_OBJECT_STYLE = "SWT_OBJECT_STYLE";

	static final int [] [] KeyTable = {
		
		/* Keyboard and Mouse Masks */
//		{Keyboard.ALTERNATE,		SWT.ALT},
		{Keyboard.SHIFT,		SWT.SHIFT},
		{Keyboard.CONTROL,	SWT.CONTROL},
//		{Keyboard.COMMAND,		SWT.COMMAND},
		
		/* Non-Numeric Keypad Keys */
		{Keyboard.UP,						SWT.ARROW_UP},
		{Keyboard.DOWN,					SWT.ARROW_DOWN},
		{Keyboard.LEFT,						SWT.ARROW_LEFT},
		{Keyboard.RIGHT,					SWT.ARROW_RIGHT},
		{Keyboard.PAGE_UP,				SWT.PAGE_UP},
		{Keyboard.PAGE_DOWN,			SWT.PAGE_DOWN},
		{Keyboard.HOME,					SWT.HOME},
		{Keyboard.END,						SWT.END},
		{Keyboard.INSERT,					SWT.INSERT},
		
		/* Virtual and Ascii Keys */
		{Keyboard.BACKSPACE,		SWT.BS},
		{Keyboard.ENTER,				SWT.CR},
		{Keyboard.DELETE,				SWT.DEL},
		{Keyboard.ESCAPE,			SWT.ESC},
//		{Keyboard.GDK_Linefeed,			SWT.LF},
		{Keyboard.TAB,					SWT.TAB},
	
		/* Functions Keys */
		{Keyboard.F1,		SWT.F1},
		{Keyboard.F2,		SWT.F2},
		{Keyboard.F3,		SWT.F3},
		{Keyboard.F4,		SWT.F4},
		{Keyboard.F5,		SWT.F5},
		{Keyboard.F6,		SWT.F6},
		{Keyboard.F7,		SWT.F7},
		{Keyboard.F8,		SWT.F8},
		{Keyboard.F9,		SWT.F9},
		{Keyboard.F10,		SWT.F10},
		{Keyboard.F11,		SWT.F11},
		{Keyboard.F12,		SWT.F12},
		{Keyboard.F13,		SWT.F13},
		{Keyboard.F14,		SWT.F14},
		{Keyboard.F15,		SWT.F15},
		
		/* Numeric Keypad Keys */
		{Keyboard.NUMPAD_MULTIPLY,		SWT.KEYPAD_MULTIPLY},
		{Keyboard.NUMPAD_ADD,			SWT.KEYPAD_ADD},
		{Keyboard.NUMPAD_ENTER,			SWT.KEYPAD_CR},
		{Keyboard.NUMPAD_SUBTRACT,	SWT.KEYPAD_SUBTRACT},
		{Keyboard.NUMPAD_DECIMAL,	SWT.KEYPAD_DECIMAL},
		{Keyboard.NUMPAD_DIVIDE,		SWT.KEYPAD_DIVIDE},
		{Keyboard.NUMPAD_0,			SWT.KEYPAD_0},
		{Keyboard.NUMPAD_1,			SWT.KEYPAD_1},
		{Keyboard.NUMPAD_2,			SWT.KEYPAD_2},
		{Keyboard.NUMPAD_3,			SWT.KEYPAD_3},
		{Keyboard.NUMPAD_4,			SWT.KEYPAD_4},
		{Keyboard.NUMPAD_5,			SWT.KEYPAD_5},
		{Keyboard.NUMPAD_6,			SWT.KEYPAD_6},
		{Keyboard.NUMPAD_7,			SWT.KEYPAD_7},
		{Keyboard.NUMPAD_8,			SWT.KEYPAD_8},
		{Keyboard.NUMPAD_9,			SWT.KEYPAD_9},
//		{Keyboard.,	SWT.KEYPAD_EQUAL},

		/* Other keys */
		{Keyboard.CAPS_LOCK,		SWT.CAPS_LOCK},
//		{Keyboard.,		SWT.NUM_LOCK},
//		{Keyboard.,		SWT.SCROLL_LOCK},
//		{Keyboard.,				SWT.PAUSE},
//		{Keyboard.,				SWT.BREAK},
//		{Keyboard.,					SWT.PRINT_SCREEN},
//		{Keyboard.,					SWT.HELP},
		
	};

	static Display Default;
	static Display [] Displays = new Display [4];
	
	/* Package Name */
	static final String PACKAGE_PREFIX = "org.eclipse.swt.widgets."; //$NON-NLS-1$
	/*
	* This code is intentionally commented.  In order
	* to support CLDC, .class cannot be used because
	* it does not compile on some Java compilers when
	* they are targeted for CLDC.
	*/
//	static {
//		String name = Display.class.getName ();
//		int index = name.lastIndexOf ('.');
//		PACKAGE_PREFIX = name.substring (0, index + 1);
//	}
	
	/*
	* TEMPORARY CODE.  Install the runnable that
	* gets the current display. This code will
	* be removed in the future.
	*/
	static {
		DeviceFinder = new Runnable () {
			public void run () {
				Device device = getCurrent ();
				if (device == null) {
					device = getDefault ();
				}
				setDevice (device);
			}
		};
	}
	/* TEMPORARY CODE. */
	static void setDevice (Device device) {
		CurrentDevice = device;
	}
	
public Display () {
	this (null);
}

public Display (DeviceData data) {
	super (data);
	synchronizer = new Synchronizer (this);
}

static boolean isValidClass (Class clazz) {
	String name = clazz.getName ();
	int index = name.lastIndexOf ('.');
	return name.substring (0, index + 1).equals (PACKAGE_PREFIX);
}

static int translateKey (int key) {
	for (int i=0; i<KeyTable.length; i++) {
		if (KeyTable [i] [0] == key) return KeyTable [i] [1];
	}
	return 0;
}

static int untranslateKey (int key) {
	for (int i=0; i<KeyTable.length; i++) {
		if (KeyTable [i] [1] == key) return KeyTable [i] [0];
	}
	return 0;
}

public void addFilter (int eventType, Listener listener) {
	checkDevice ();
	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
	if (filterTable == null) filterTable = new EventTable ();
	filterTable.hook (eventType, listener);
}

public void addListener (int eventType, Listener listener) {
	checkDevice ();
	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
	if (eventTable == null) eventTable = new EventTable ();
	eventTable.hook (eventType, listener);
}

void addWidget (UIComponent component, Widget widget) {
	if (component == null) return;
	component.setStyle (SWT_OBJECT_STYLE, widget);
}

public void asyncExec (Runnable runnable) {
	synchronized (Device.class) {
		if (isDisposed ()) error (SWT.ERROR_DEVICE_DISPOSED);
		synchronizer.asyncExec (runnable);
		
		if (getMessageCount() == 1) callLater();
	}
}

public void beep () {
	checkDevice ();
}

void callLater () {
	root.callLater(dispathEventsFunction (), null);
}

void caretTimerHandler (intrinsic.flash.events.TimerEvent event) {
	if (currentCaret == null || currentCaret.isDisposed()) return;
	if (!(currentCaret.blinkCaret () && currentCaret.blinkRate != 0)) {
		currentCaret = null;
		caretTimer.stop();
		caretTimer = null;
	}
}

native Function caretTimerHandlerFunction ()/*{
	return caretTimerHandler__Lflash_events_TimerEvent_2;
}*/;

void changeCursor(Cursor cursor) {
	if (currentCursor != cursor){
		currentCursor = cursor;
		CursorManager.removeCursor(CursorManager.currentCursorID);
		if (cursor != null){
			if (cursor.wait) {
				CursorManager.setBusyCursor();
			} else {
				if (cursor.object != null) {
					CursorManager.setCursor(cursor.object, CursorManagerPriority.MEDIUM, -cursor.hotspotX, -cursor.hotspotY);
				} else {
					CursorManager.setCursor(SWTBitmapAsset.getClazz(), CursorManagerPriority.MEDIUM, -cursor.hotspotX, -cursor.hotspotY);
				}
			}
		}
	}
}

protected void checkDevice () {
	if (thread == null) error (SWT.ERROR_WIDGET_DISPOSED);
	if (thread != Thread.currentThread ()) error (SWT.ERROR_THREAD_INVALID_ACCESS);
	if (isDisposed ()) error (SWT.ERROR_DEVICE_DISPOSED);
}

static void checkDisplay (Thread thread, boolean multiple) {
	synchronized (Device.class) {
		for (int i=0; i<Displays.length; i++) {
			if (Displays [i] != null) {
				if (!multiple) SWT.error (SWT.ERROR_NOT_IMPLEMENTED, null, " [multiple displays]");
				if (Displays [i].thread == thread) SWT.error (SWT.ERROR_THREAD_INVALID_ACCESS);
			}
		}
	}
}

protected void checkSubclass () {
	if (!isValidClass (getClass ())) error (SWT.ERROR_INVALID_SUBCLASS);
}

public void close () {
	checkDevice ();
	Event event = new Event ();
	sendEvent (SWT.Close, event);
	if (event.doit) dispose ();
}

protected void create (DeviceData data) {
	checkSubclass ();
	checkDisplay (thread = Thread.currentThread (), false);
	createDisplay (data);
	register (this);
	if (Default == null) Default = this;
}

void createDisplay (DeviceData data) {
	Application app = (Application)Application.application;
	root = new intrinsic.mx.containers.Canvas();
	root.percentHeight = root.percentWidth = 100;
	app.setStyle("paddingLeft", 0);
	app.setStyle("paddingTop", 0);
	app.setStyle("paddingRight", 0);
	app.setStyle("paddingBottom", 0);
	root.addEventListener(intrinsic.flash.events.Event.ADDED_TO_STAGE, handleAddedToStageFunction ());
	app.addChild(root);
}

static void deregister (Display display) {
	synchronized (Device.class) {
		for (int i=0; i<Displays.length; i++) {
			if (display == Displays [i]) Displays [i] = null;
		}
	}
}

protected void destroy () {
	if (this == Default) Default = null;
	deregister (this);
	destroyDisplay ();
}

void destroyDisplay () {
	Application app = (Application)Application.application;
	app.removeChild(root);
	root = null;
}

void dispatchEvents() {
	if (runEvents()) callLater();
}

native Function dispathEventsFunction ()/*{
	return dispatchEvents__;
}*/;

public void disposeExec (Runnable runnable) {
	checkDevice ();
	if (disposeList == null) disposeList = new Runnable [4];
	for (int i=0; i<disposeList.length; i++) {
		if (disposeList [i] == null) {
			disposeList [i] = runnable;
			return;
		}
	}
	Runnable [] newDisposeList = new Runnable [disposeList.length + 4];
	System.arraycopy (disposeList, 0, newDisposeList, 0, disposeList.length);
	newDisposeList [disposeList.length] = runnable;
	disposeList = newDisposeList;
}

void error (int code) {
	SWT.error (code);
}

boolean filterEvent (Event event) {
	if (filterTable != null) filterTable.sendEvent (event);
	return false;
}

boolean filters (int eventType) {
	if (filterTable == null) return false;
	return filterTable.hooks (eventType);
}

Control findControl (DisplayObject target, boolean check) {
	if (target == null) return null;
	while (target != null) {
		if (target instanceof UIComponent) {
			Widget widget = getWidget((UIComponent)target);
			if (widget != null) {
				if (check && !widget.checkComponent(target)) return null;
				if (widget instanceof Control) {
					Control control = (Control)widget;
					if (control.isEnabled()) return control;
				} 
			}
		}
		target = target.parent;
	}
	return null;
}

public Widget findWidget (int handle) {
	checkDevice ();
	return null;
}

public Widget findWidget (int handle, int id) {
	//TODO - 
	return null;
}

public Widget findWidget (Widget widget, int id) {
	//TODO - 
	return null;
}

public static Display findDisplay (Thread thread) {
	synchronized (Device.class) {
		for (int i=0; i<Displays.length; i++) {
			Display display = Displays [i];
			if (display != null && display.thread == thread) {
				return display;
			}
		}
		return null;
	}
}

public Shell getActiveShell () {
	//TODO - 
	return null;
}

int getCaretBlinkTime () {
//	checkDevice ();
	//TODO blink caret time
	return 400;
}

int getClickCount (int type, Control control, int x, int y, int time) {
	switch (type) {
		case SWT.MouseDown:
			int timeDelta = 500;
			int xInset = 2;
			int yInset = 2;
			Rectangle clickRect = new Rectangle (lastMouseDownX - xInset, lastMouseDownY - yInset, xInset + xInset, yInset + yInset);
			if (control == lastMouseDownControl && (time - lastMouseDownTime) <= timeDelta && clickRect.contains (x, y)) {
				clickCount++;
			} else {
				clickCount = 1;
			}
			lastMouseDownControl = control;
			lastMouseDownX = x;
			lastMouseDownY = y;
			lastMouseDownTime = time;
			//FALL THROUGH
		case SWT.MouseUp:
			return clickCount;
	}
	return 0;
}

public Rectangle getBounds () {
	checkDevice();
	Application application = (Application)Application.application;
	return new Rectangle(0, 0, (int)application.width, (int)application.height);
}

public static Display getCurrent () {
	return findDisplay (Thread.currentThread ());
}

public Rectangle getClientArea () {
	checkDevice();
	return getBounds();
}

BitmapData getCursorBitmap() {
	return currentCursor != null ? currentCursor.bitmap : null;
}

public Control getCursorControl () {
	checkDevice ();
	//TODO - 
	return null;
}

public Point getCursorLocation () {
	checkDevice ();
	return new Point((int)root.mouseX, (int)root.mouseY);
}

public Point[] getCursorSizes () {
	//TODO - 
	return null;
}

public static Display getDefault () {
	synchronized (Device.class) {
		if (Default == null) Default = new Display ();
		return Default;
	}
}

public Object getData () {
	checkDevice ();
	return data;
}

public Object getData (String key) {
	checkDevice ();
	if (key == null) error (SWT.ERROR_NULL_ARGUMENT);
	if (keys == null) return null;
	for (int i=0; i<keys.length; i++) {
		if (keys [i].equals (key)) return values [i];
	}
	return null;
}

public int getDismissalAlignment () {
	checkDevice ();
	return SWT.RIGHT;
}

public int getDoubleClickTime () {
	//TODO - 
	return 400;
}

public Control getFocusControl () {
	Application app = (Application)Application.application;
	IFocusManager focusManager = app.focusManager;
	if (focusManager == null) return null;
	IFocusManagerComponent focusComponent = focusManager.getFocus();
	if (focusComponent != null && focusComponent instanceof UIComponent) {
		return (Control)getWidget((UIComponent)focusComponent);
	}
	return null;
}

public boolean getHighContrast () {
	//TODO - 
	return false;
}

public int getIconDepth () {
	//TODO - 
	return 0;
}

public Point[] getIconSizes () {
	//TODO - 
	return null;
}

int getLastEventTime () {
	return 0;
}

int getMessageCount () {
	return synchronizer.getMessageCount ();
}

public Monitor[] getMonitors () {
	//TODO - 
	return null;
}

public Monitor getPrimaryMonitor () {
	//TODO - 
	return null;
}

public Shell[] getShells () {
	int count = 0;
	Shell[] result = new Shell[root.numChildren];
	for (int i = 0; i < result.length; i++) {
		DisplayObject object = root.getChildAt(i);
		if (object instanceof UIComponent) {
			Widget widget = getWidget((UIComponent)object);
			if (widget instanceof Shell) {
				result[count++] = (Shell)widget;
			}
		}
	}
	if (count != result.length) {
		Shell[] temp = new Shell[count];
		System.arraycopy(result, 0, temp, 0, count);
		result = temp;
	}
	return result;
}

public Synchronizer getSynchronizer () {
	checkDevice ();
	return synchronizer;
}

public Thread getSyncThread () {
	synchronized (Device.class) {
		if (isDisposed ()) error (SWT.ERROR_DEVICE_DISPOSED);
		return synchronizer.syncThread;
	}
}

public Color getSystemColor (int id) {
	checkDevice ();
	int rgb = 0x000000;
	switch (id) {
		//TODO
		case SWT.COLOR_INFO_FOREGROUND: rgb = 0x000000;  break;
		case SWT.COLOR_INFO_BACKGROUND: rgb = 0xFFFFE1;  break;
		case SWT.COLOR_TITLE_FOREGROUND: rgb = 0xFFFFFF;  break;
		case SWT.COLOR_TITLE_BACKGROUND: rgb = 0x0054e3;  break;
		case SWT.COLOR_TITLE_BACKGROUND_GRADIENT: rgb = 0x3D95FF;  break;
		case SWT.COLOR_TITLE_INACTIVE_FOREGROUND: rgb = 0xD8E4F8;  break;
		case SWT.COLOR_TITLE_INACTIVE_BACKGROUND: rgb = 0x7A96DF;  break;
		case SWT.COLOR_TITLE_INACTIVE_BACKGROUND_GRADIENT: rgb = 0x9DB9EB;  break;
		case SWT.COLOR_WIDGET_DARK_SHADOW:		rgb = 0x000000;  break;
		case SWT.COLOR_WIDGET_NORMAL_SHADOW:	rgb = 0x909090;  break;
		case SWT.COLOR_WIDGET_LIGHT_SHADOW:		rgb = 0xC0C0C0;  break;
		case SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW:	rgb = 0xFFFFFF;  break;
		case SWT.COLOR_WIDGET_BACKGROUND:		rgb = 0xFFFFFF;  break;
		case SWT.COLOR_WIDGET_FOREGROUND:		rgb = 0x000000;  break;
		case SWT.COLOR_WIDGET_BORDER:
		case SWT.COLOR_LIST_FOREGROUND:			rgb = 0x000000;  break;
		case SWT.COLOR_LIST_BACKGROUND:			rgb = 0xFFFFFF;  break;
		case SWT.COLOR_LIST_SELECTION_TEXT:		rgb = 0xFFFFFF;  break;
		case SWT.COLOR_LIST_SELECTION:			rgb = 0x000000;  break;
		default:
			return super.getSystemColor (id);	
	}
	return Color.flex_new (this, rgb);
}

public Cursor getSystemCursor (int id) {
	checkDevice ();
	if (!(0 <= id && id < cursors.length)) return null;
	if (cursors [id] == null) {
		cursors [id] = new Cursor (this, id);
	}
	return cursors [id];
}

public Image getSystemImage (int id) {
	//TODO-
	return null;
}

public Tray getSystemTray () {
	checkDevice ();
	return null;
}

public Thread getThread () {
	synchronized (Device.class) {
		if (isDisposed ()) error (SWT.ERROR_DEVICE_DISPOSED);
		return thread;
	}
}

Widget getWidget (UIComponent component) {
	if (component == null) return null;
	return (Widget)component.getStyle (SWT_OBJECT_STYLE);
}

void handleAddedToStage (intrinsic.flash.events.Event event) {
	InteractiveObject stage = root.stage;
	stage.addEventListener(intrinsic.flash.events.MouseEvent.MOUSE_DOWN, handleMouseDownFunction ());
	stage.addEventListener(intrinsic.flash.events.MouseEvent.MOUSE_UP, handleMouseUpFunction ());
	stage.addEventListener(intrinsic.flash.events.MouseEvent.MOUSE_MOVE, handleMouseMoveFunction ());
	stage.addEventListener(intrinsic.flash.events.MouseEvent.MOUSE_OVER, handleMouseOverFunction ());
	stage.addEventListener(intrinsic.flash.events.MouseEvent.MOUSE_OUT, handleMouseOutFunction ());
	stage.addEventListener(intrinsic.flash.events.KeyboardEvent.KEY_DOWN, handleKeyDownFunction (), true);
	stage.addEventListener(intrinsic.flash.events.KeyboardEvent.KEY_UP, handleKeyUpFunction (), true);
	stage.addEventListener(intrinsic.flash.events.FocusEvent.FOCUS_IN, handleFocusInFunction (), true);
	stage.addEventListener(intrinsic.flash.events.FocusEvent.FOCUS_OUT, handleFocusOutFunction (), true);
}

native Function handleAddedToStageFunction ()/*{
	return handleAddedToStage__Lflash_events_Event_2;
}*/;

void handleFocusIn (intrinsic.flash.events.FocusEvent event) {
	Control control = findControl((DisplayObject)event.target, false);
	if (control == null) return;
	control.sendFocusEvent(SWT.FocusIn);
}

native Function handleFocusInFunction ()/*{
	return handleFocusIn__Lflash_events_FocusEvent_2;
}*/;

void handleFocusOut (intrinsic.flash.events.FocusEvent event) {
	Control control = findControl((DisplayObject)event.target, false);
	if (control == null) return;
	control.sendFocusEvent(SWT.FocusOut);
}

native Function handleFocusOutFunction ()/*{
	return handleFocusOut__Lflash_events_FocusEvent_2;
}*/;

void handleKeyDown (intrinsic.flash.events.KeyboardEvent event) {
	Control control = findControl((DisplayObject)event.target, false);
	if (control == null) return;
	if (!control.sendKeyEvent(SWT.KeyDown, event)) {
		event.stopPropagation();
	}
}

native Function handleKeyDownFunction ()/*{
	return handleKeyDown__Lflash_events_KeyboardEvent_2;
}*/;

void handleKeyUp (intrinsic.flash.events.KeyboardEvent event) {
	Control control = findControl((DisplayObject)event.target, false);
	if (control == null) return;
	if (!control.sendKeyEvent(SWT.KeyUp, event)) {
		event.stopPropagation();
	}
}

native Function handleKeyUpFunction ()/*{
	return handleKeyUp__Lflash_events_KeyboardEvent_2;
}*/;

void handleMouseDown (intrinsic.flash.events.MouseEvent event) {
	Control control = grabControl = findControl((DisplayObject)event.target, true);
	if (control == null) return;
	control.sendMouseEvent (SWT.MouseDown, event, false);
}

native Function handleMouseDownFunction ()/*{
	return handleMouseDown__Lflash_events_MouseEvent_2;
}*/;

void handleMouseMove (intrinsic.flash.events.MouseEvent event) {
	Control control = grabControl != null ? grabControl : findControl((DisplayObject)event.target, true);
	if (control == null) return;
	control.sendMouseEvent (SWT.MouseMove, event, false);
}

native Function handleMouseMoveFunction ()/*{
	return handleMouseMove__Lflash_events_MouseEvent_2;
}*/;

void handleMouseOver (intrinsic.flash.events.MouseEvent event) {
	if (grabControl != null) {
		/*
		* Bug in Flex. If a cursor is set and the pointer exists the stage
		* while dragging, the cursor disappears. The fix to clear the 
		* cursor in mouse out and set it back on mouse over.
		*/
		if (event.relatedObject == null) {
			changeCursor(grabControl.findCursor());
		}
		return;
	}
	DisplayObject target = (DisplayObject)event.target;
	if (target instanceof DisplayObjectContainer) {
		DisplayObjectContainer container = (DisplayObjectContainer)target;
		InteractiveObject relatedObject = event.relatedObject;
		if (relatedObject != null && relatedObject != container && container.contains(relatedObject)) {
			/* Ignore mouse enter event when moving from a predecessor */
			return;
		}
	}
	Control control = findControl(target, true);
	if (control == null) return;
	control.sendMouseEvent (SWT.MouseEnter, event, true);
	if (control.isDisposed()) return;
	changeCursor(control.findCursor());
}

native Function handleMouseOverFunction ()/*{
	return handleMouseOver__Lflash_events_MouseEvent_2;
}*/;

void handleMouseOut (intrinsic.flash.events.MouseEvent event) {
	if (grabControl != null) {
		/*
		* Bug in Flex. If a cursor is set and the pointer exists the stage
		* while dragging, the cursor disappears. The fix to clear the 
		* cursor.
		*/
		if (event.relatedObject == null) {
			changeCursor(null);
		}
		return;
	}
	DisplayObject target = (DisplayObject)event.target;
	if (target instanceof DisplayObjectContainer) {
		DisplayObjectContainer container = (DisplayObjectContainer)target;
		InteractiveObject relatedObject = event.relatedObject;
		if (relatedObject != null && relatedObject != container && container.contains(relatedObject)) {
			/* Ignore mouse exit event when moving to a predecessor */
			return;
		}
	}
	Control control = findControl(target, true);
	changeCursor(null);
	if (control == null) return;
	control.sendMouseEvent (SWT.MouseExit, event, true);
}

native Function handleMouseOutFunction ()/*{
	return handleMouseOut__Lflash_events_MouseEvent_2;
}*/;

void handleMouseUp (intrinsic.flash.events.MouseEvent event) {
	Control control = grabControl != null ? grabControl : findControl((DisplayObject)event.target, true);
	grabControl = null;
	if (control == null) return;
	control.sendMouseEvent (SWT.MouseUp, event, false);
}

native Function handleMouseUpFunction ()/*{
	return handleMouseUp__Lflash_events_MouseEvent_2;
}*/;

protected void init () {
}

public int internal_new_GC (GCData data) {
	return 0;
}

public void internal_dispose_GC (int hDC, GCData data) {
}

boolean isValidThread () {
	return thread == Thread.currentThread ();
}

public Point map (Control from, Control to, Point point) {
	checkDevice ();
	if (point == null) error (SWT.ERROR_NULL_ARGUMENT);	
	return map (from, to, point.x, point.y);
}

public Point map (Control from, Control to, int x, int y) {
	checkDevice ();
	if (from != null && from.isDisposed()) error (SWT.ERROR_INVALID_ARGUMENT);
	if (to != null && to.isDisposed()) error (SWT.ERROR_INVALID_ARGUMENT);
	if (from == to) return new Point (x, y);
	intrinsic.flash.geom.Point point = new intrinsic.flash.geom.Point(x, y);
	if (from != null) {
		point = from.object.contentToGlobal(point);
	}
	if (to != null) {
		point = to.object.globalToContent(point);
	}
	return new Point((int)point.x, (int)point.y);
}

public Rectangle map (Control from, Control to, Rectangle rectangle) {
	checkDevice ();
	if (rectangle == null) error (SWT.ERROR_NULL_ARGUMENT);	
	return map (from, to, rectangle.x, rectangle.y, rectangle.width, rectangle.height);
}

public Rectangle map (Control from, Control to, int x, int y, int width, int height) {
	checkDevice ();
	if (from != null && from.isDisposed()) error (SWT.ERROR_INVALID_ARGUMENT);
	if (to != null && to.isDisposed()) error (SWT.ERROR_INVALID_ARGUMENT);
	if (from == to) return new Rectangle (x, y, width, height);
	intrinsic.flash.geom.Point point = new intrinsic.flash.geom.Point(x, y);
	if (from != null) {
		point = from.object.contentToGlobal(point);
	}
	if (to != null) {
		point = to.object.globalToContent(point);
	}
	return new Rectangle((int)point.x, (int)point.y, width, height);
}

public boolean post (Event event) {
	return false;
}

void postEvent (Event event) {
	/*
	* Place the event at the end of the event queue.
	* This code is always called in the Display's
	* thread so it must be re-enterant but does not
	* need to be synchronized.
	*/
	if (eventQueue == null) eventQueue = new Event [4];
	int index = 0;
	int length = eventQueue.length;
	while (index < length) {
		if (eventQueue [index] == null) break;
		index++;
	}
	if (index == length) {
		Event [] newQueue = new Event [length + 4];
		System.arraycopy (eventQueue, 0, newQueue, 0, length);
		eventQueue = newQueue;
	}
	eventQueue [index] = event;
	
	//TODO
	if (index == 0) callLater();
}

public boolean readAndDispatch () {
	checkDevice ();
	//TODO
	return false;
//	boolean events = true;
//	if (events) {
//		runDeferredEvents();
//		return true;
//	}
//	return runAsyncMessages (false);
}

static void register (Display display) {
	synchronized (Device.class) {
		for (int i=0; i<Displays.length; i++) {
			if (Displays [i] == null) {
				Displays [i] = display;
				return;
			}
		}
		Display [] newDisplays = new Display [Displays.length + 4];
		System.arraycopy (Displays, 0, newDisplays, 0, Displays.length);
		newDisplays [Displays.length] = display;
		Displays = newDisplays;
	}
}

protected void release () {
	sendEvent (SWT.Dispose, new Event ());
	Shell [] shells = getShells ();
	for (int i=0; i<shells.length; i++) {
		Shell shell = shells [i];
		if (!shell.isDisposed ()) shell.dispose ();
	}
	if (tray != null) tray.dispose ();
	tray = null;
//	while (readAndDispatch ()) {}
	if (disposeList != null) {
		for (int i=0; i<disposeList.length; i++) {
			if (disposeList [i] != null) disposeList [i].run ();
		}
	}
	disposeList = null;
	synchronizer.releaseSynchronizer ();
	synchronizer = null;
	releaseDisplay ();
	super.release ();
}

void releaseDisplay() {
	currentCaret = null;
	if (caretTimer != null) caretTimer.stop();
	caretTimer = null;
	for (int i = 0; i < cursors.length; i++) {
		if (cursors [i] != null) cursors [i].dispose ();
	}
	cursors = null;
	if (timerIds != null) {
		for (int i = 0; i < timerIds.length; i++) {
			timerIds[i].stop();
		}
	}
	timerIds = null;
	timerList = null;
	lastMouseDownControl = null;
	grabControl = null;
}

public void removeFilter (int eventType, Listener listener) {
	checkDevice ();
	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
	if (filterTable == null) return;
	filterTable.unhook (eventType, listener);
	if (filterTable.size () == 0) filterTable = null;
}

public void removeListener (int eventType, Listener listener) {
	checkDevice ();
	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
	if (eventTable == null) return;
	eventTable.unhook (eventType, listener);
}

Widget removeWidget (UIComponent component) {
	if (component == null) return null;
	Object result = component.getStyle (SWT_OBJECT_STYLE);
	component.clearStyle (SWT_OBJECT_STYLE);
	return (Widget)result;
}

boolean runAsyncMessages (boolean all) {
	return synchronizer.runAsyncMessages (all);
}

boolean runDeferredEvents () {
	if (eventQueue == null) return false;
	/*
	* Run deferred events.  This code is always
	* called  in the Display's thread so it must
	* be re-enterant need not be synchronized.
	*/
	while (eventQueue != null) {
		
		/* Take an event off the queue */
		Event event = eventQueue [0];
		if (event == null) break;
		int length = eventQueue.length;
		System.arraycopy (eventQueue, 1, eventQueue, 0, --length);
		eventQueue [length] = null;

		/* Run the event */
		Widget widget = event.widget;
		if (widget != null && !widget.isDisposed ()) {
			Widget item = event.item;
			if (item == null || !item.isDisposed ()) {
				widget.notifyListeners (event.type, event);
			}
		}

		/*
		* At this point, the event queue could
		* be null due to a recursive invokation
		* when running the event.
		*/
	}

	/* Clear the queue */
	eventQueue = null;
	return true;
}

boolean runEvents () {
	if (runDeferredEvents()) {
		return true;
	}
	return runAsyncMessages (false);
}

void sendEvent (int eventType, Event event) {
	if (eventTable == null && filterTable == null) {
		return;
	}
	if (event == null) event = new Event ();
	event.display = this;
	event.type = eventType;
	if (event.time == 0) event.time = getLastEventTime ();
	if (!filterEvent (event)) {
		if (eventTable != null) eventTable.sendEvent (event);
	}
}

void setCurrentCaret (Caret caret) {
	currentCaret = caret;
	if (caretTimer != null) caretTimer.stop();
	caretTimer = null;
	if (currentCaret != null) {
		int blinkRate = currentCaret.blinkRate;
		if (blinkRate != 0) {
			caretTimer = new Timer(blinkRate, 0);
			caretTimer.start();
			caretTimer.addEventListener(TimerEvent.TIMER, caretTimerHandlerFunction());
		}
	}
}

public void setCursorLocation (int x, int y) {
	//TODO-
}

public void setCursorLocation (Point point) {
	checkDevice ();
	if (point == null) error (SWT.ERROR_NULL_ARGUMENT);
	setCursorLocation (point.x, point.y);
}

public void setData (Object data) {
	checkDevice ();
	this.data = data;
}

public void setData (String key, Object value) {
	checkDevice ();
	if (key == null) error (SWT.ERROR_NULL_ARGUMENT);
	
	/* Remove the key/value pair */
	if (value == null) {
		if (keys == null) return;
		int index = 0;
		while (index < keys.length && !keys [index].equals (key)) index++;
		if (index == keys.length) return;
		if (keys.length == 1) {
			keys = null;
			values = null;
		} else {
			String [] newKeys = new String [keys.length - 1];
			Object [] newValues = new Object [values.length - 1];
			System.arraycopy (keys, 0, newKeys, 0, index);
			System.arraycopy (keys, index + 1, newKeys, index, newKeys.length - index);
			System.arraycopy (values, 0, newValues, 0, index);
			System.arraycopy (values, index + 1, newValues, index, newValues.length - index);
			keys = newKeys;
			values = newValues;
		}
		return;
	}
	
	/* Add the key/value pair */
	if (keys == null) {
		keys = new String [] {key};
		values = new Object [] {value};
		return;
	}
	for (int i=0; i<keys.length; i++) {
		if (keys [i].equals (key)) {
			values [i] = value;
			return;
		}
	}
	String [] newKeys = new String [keys.length + 1];
	Object [] newValues = new Object [values.length + 1];
	System.arraycopy (keys, 0, newKeys, 0, keys.length);
	System.arraycopy (values, 0, newValues, 0, values.length);
	newKeys [keys.length] = key;
	newValues [values.length] = value;
	keys = newKeys;
	values = newValues;
}

public static void setAppName (String name) {
}

public void setSynchronizer (Synchronizer synchronizer) {
	checkDevice ();
	if (synchronizer == null) error (SWT.ERROR_NULL_ARGUMENT);
	if (synchronizer == this.synchronizer) return;
	Synchronizer oldSynchronizer;
	synchronized (Device.class) {
		oldSynchronizer = this.synchronizer;
		this.synchronizer = synchronizer;
	}
	if (oldSynchronizer != null) {
		oldSynchronizer.runAsyncMessages(true);
	}
}

public boolean sleep () {
	checkDevice ();
	if (getMessageCount () != 0) return true;
	//TODO
	return true;
}

public void syncExec (Runnable runnable) {
	Synchronizer synchronizer;
	synchronized (Device.class) {
		if (isDisposed ()) error (SWT.ERROR_DEVICE_DISPOSED);
		synchronizer = this.synchronizer;
	}
	synchronizer.syncExec (runnable);
}

public void timerExec (int milliseconds, Runnable runnable) {
	checkDevice ();
	if (runnable == null) error (SWT.ERROR_NULL_ARGUMENT);
	if (timerList == null) timerList = new Runnable [4];
	if (timerIds == null) timerIds = new Timer [4];
	int index = 0;
	while (index < timerList.length) {
		if (timerList [index] == runnable) break;
		index++;
	}
	Timer timerId = null;
	if (index != timerList.length) {
		timerId = timerIds [index];
		if (milliseconds < 0) {			
			timerId.stop();
			timerList [index] = null;
			timerIds [index] = null;
			return;
		}
	} else {
		if (milliseconds < 0) return;
		index = 0;
		while (index < timerList.length) {
			if (timerList [index] == null) break;
			index++;
		}
		if (index == timerList.length) {
			Runnable [] newTimerList = new Runnable [timerList.length + 4];
			System.arraycopy (timerList, 0, newTimerList, 0, timerList.length);
			timerList = newTimerList;
			Timer [] newTimerIds = new Timer [timerIds.length + 4];
			System.arraycopy (timerIds, 0, newTimerIds, 0, timerIds.length);
			timerIds = newTimerIds;
		}
	}
	Timer newTimerID = new Timer(milliseconds, 1);
	if (newTimerID != null) {
		newTimerID.start();
		newTimerID.addEventListener(TimerEvent.TIMER, timerHandlerFunction());
		timerList [index] = runnable;
		timerIds [index] = newTimerID;
	}
}

void timerHandler (intrinsic.flash.events.TimerEvent event) {
	if (timerList != null && timerIds != null) {
		int index = 0;
		while (index <timerIds.length) {
			if (timerIds [index] == event.target) {
				timerIds [index] = null;
				Runnable runnable = timerList [index];
				timerList [index] = null;
				if (runnable != null) runnable.run ();
				return;
			}
			index++;
		}
	}
}

native Function timerHandlerFunction ()/*{
	return timerHandler__Lflash_events_TimerEvent_2;
}*/;


public void update () {
}

public void wake () {
	synchronized (Device.class) {
		if (isDisposed ()) error (SWT.ERROR_DEVICE_DISPOSED);
		if (thread == Thread.currentThread ()) return;
		wakeThread ();
	}
}

void wakeThread () {
}

}
