/*******************************************************************************
 * 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.SWT;
import org.eclipse.swt.events.ShellListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics.Region;

public class Shell extends Decorations {
	Region region;
	ToolTip [] toolTips;
	boolean moved, resized, opened;
	int oldX, oldY, oldWidth, oldHeight;
	
public Shell () {
	this ((Display) null);
}
public Shell (Display display) {
	this (display, /*OS.IsWinCE ? SWT.NONE : */SWT.SHELL_TRIM);
}

public Shell (int style) {
	this ((Display) null, style);
}

public Shell (Display display, int style) {
	this (display, null, style, null, false);
}

public Shell (Shell parent) {
	this (parent, /*OS.IsWinCE ? SWT.NONE : */SWT.DIALOG_TRIM);
}

public Shell (Shell parent, int style) {
	this (parent != null ? parent.display : null, parent, style, null, false);
}

Shell (Display display, Shell parent, int style, Object handle, boolean embedded) {
	super ();
	if (display == null) display = Display.getCurrent ();
	if (display == null) display = Display.getDefault ();
	if (!display.isValidThread ()) {
		error (SWT.ERROR_THREAD_INVALID_ACCESS);
	}
	if (parent != null && parent.isDisposed ()) {
		error (SWT.ERROR_INVALID_ARGUMENT);	
	}
	this.style = checkStyle (style);
	this.parent = parent;
	this.display = display;
	this.jsObject = handle;
	if (handle != null && !embedded) {
		state |= FOREIGN_HANDLE;
	}
	createWidget (display, parent, style, -1);
}

public static Shell dojo_new (Display display, Object handle) {
	return new Shell (display, null, SWT.NO_TRIM, handle, true);
}

public static Shell internal_new (Display display, int handle) {
	return null;
}

void addParent () {
	/* Do nothing */
}

public void addShellListener (ShellListener listener) {
	checkWidget ();
	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
	TypedListener typedListener = new TypedListener (listener);
	addListener (SWT.Close,typedListener);
	addListener (SWT.Iconify,typedListener);
	addListener (SWT.Deiconify,typedListener);
	addListener (SWT.Activate, typedListener);
	addListener (SWT.Deactivate, typedListener);
}

void checkOpened() {
	if (!opened) resized = false;
}

static int checkStyle (int style) {
	style = Decorations.checkStyle (style);
//TODO	
//	style &=~SWT.TRANSPARENT;
	int mask = SWT.SYSTEM_MODAL | SWT.APPLICATION_MODAL | SWT.PRIMARY_MODAL;
	int bits = style & ~mask;
	if ((style & SWT.SYSTEM_MODAL) != 0) return bits | SWT.SYSTEM_MODAL;
	if ((style & SWT.APPLICATION_MODAL) != 0) return bits | SWT.APPLICATION_MODAL;
	if ((style & SWT.PRIMARY_MODAL) != 0) return bits | SWT.PRIMARY_MODAL;
	return bits;
}

public void close () {
	checkWidget ();
	closeWidget ();
}

void closeWidget () {
	Event event = new Event ();
	sendEvent (SWT.Close, event);
	if (event.doit && !isDisposed ()) dispose ();
}

void createWidget (Display display, Widget parent, int style, int index) {
	state |= HIDDEN;
	super.createWidget (display, parent, style, index);
}

Control findBackgroundControl () {
	return background != null || backgroundImage != null ? this : null;
}

Composite findDeferredControl () {
	return layoutCount > 0 ? this : null;
}

public void forceActive () {
}

public int getImeInputMode () {
	return 0;
}

public Point getMinimumSize () {
	return null;
}

public Region getRegion () {
	checkWidget ();
	return region;
}

public Shell getShell () {
	checkWidget ();
	return this;
}

public Shell[] getShells () {
	checkWidget ();
	int count = 0;
	Shell [] shells = display.getShells ();
	for (int i=0; i<shells.length; i++) {
		Control shell = shells [i];
		do {
			shell = shell.parent;
		} while (shell != null && shell != this);
		if (shell == this) count++;
	}
	int index = 0;
	Shell [] result = new Shell [count];
	for (int i=0; i<shells.length; i++) {
		Control shell = shells [i];
		do {
			shell = shell.parent;
		} while (shell != null && shell != this);
		if (shell == this) {
			result [index++] = shells [i];
		}
	}
	return result;
}

void hookEvents() {
	super.hookEvents();
	_hookEnter("onmouseover");
}

public boolean isEnabled () {
	checkWidget ();
	return getEnabled ();
}

public boolean isVisible () {
	checkWidget ();
	return getVisible ();
}

public void open () {
	checkWidget ();
	setVisible (true);
}

//TODO TEMP HACK, have to really implement it
void register () {	
	display.addControl (0, this);
}

void releaseChildren (boolean destroy) {
	Shell [] shells = getShells ();
	for (int i=0; i<shells.length; i++) {
		Shell shell = shells [i];
		if (shell != null && !shell.isDisposed ()) {
			shell.release (false);
		}
	}
	if (toolTips != null) {
		for (int i=0; i<toolTips.length; i++) {
			ToolTip toolTip = toolTips [i];
			if (toolTip != null && !toolTip.isDisposed ()) {
				toolTip.release (false);
			}
		}
	}
	toolTips = null;
	super.releaseChildren (destroy);
}

void releaseParent () {
	/* Do nothing */
}

void releaseWidget () {
	super.releaseWidget ();
	region = null;
}

public void removeShellListener (ShellListener listener) {
	checkWidget ();
	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
	if (eventTable == null) return;
	eventTable.unhook (SWT.Close, listener);
	eventTable.unhook (SWT.Iconify,listener);
	eventTable.unhook (SWT.Deiconify,listener);
	eventTable.unhook (SWT.Activate, listener);
	eventTable.unhook (SWT.Deactivate, listener);
}

void resizeBounds (int width, int height, boolean notify) {
	//_setSize (width, height);
	if (notify) {
		resized = true;
		sendEvent (SWT.Resize);
		if (isDisposed ()) return;
		if (layout != null) {
			markLayout (false, false);
			updateLayout (false);
		}
	}
}

void sendClose () {
	closeWidget ();
}

public void setActive () {
}

int setBounds (int x, int y, int width, int height, int flags) {
	int result = 0;
	//TODO: split this into 2 separate functions - one that
	//sets only the size and one that sets only the location
	_setBounds(x, y, width, height);
	if ((flags & MOVED) != 0) {
		Point currentPosition = new Point(0,0);
		_getSize (currentPosition);
		//_setLocation(x, y);
		if (currentPosition.x!= x || currentPosition.y != y) {
			moved = true;
			oldX = x;
			oldY = y;
			sendEvent (SWT.Move);
			if (isDisposed ()) return 0;
			result |= MOVED;
		}
	}
	if ((flags & RESIZED) != 0) {
		width = Math.max (1, width);
		height = Math.max (1, height);
		
		boolean changed = width != oldWidth || height != oldHeight;
		if (changed) {
			oldWidth = width;
			oldHeight = height;
			result |= RESIZED;
		}
		//Point currentSize = new Point(0,0);
		//_getLocation(currentSize);
		//_setSize(width, height);
		resizeBounds (width, height, changed);
	}
	return result;
}


protected void setInitialBounds (){
	int x, y, width, height;
	if (this.parent==null) {
//TODO uncomment and compute x and y also		
		x = y = 0;
//		width = display.getClientArea().width*5/8;
//		height = display.getClientArea().height*5/8;
		width = display.getClientArea().width;
		height = display.getClientArea().height;
	} else {
		Rectangle rec = this.parent.getBounds();
		x = rec.x;
		y = rec.y;
//		width = rec.width*5/8;
//		height = rec.height*5/8;
		width = rec.width;
		height = rec.height;
	}
	setBounds(x, y, width, height);
}

public void setImeInputMode (int mode) {
}


public void setMinimumSize (int width, int height) {
	checkWidget ();
	_setMinimumSize (width, height);
}

public void setMinimumSize (Point size) {
	checkWidget ();
	if (size == null) error (SWT.ERROR_NULL_ARGUMENT);
	setMinimumSize (size.x, size.y);
}

public void setRegion (Region region) {
	checkWidget ();
	if ((style & SWT.NO_TRIM) == 0) return;
	if (region != null && region.isDisposed()) error (SWT.ERROR_INVALID_ARGUMENT);
	this.region = region;
	_setRegion (region);
}

public void setVisible(boolean visible) {
	checkWidget();
	_setVisible(visible);
	if (visible) {
		sendEvent (SWT.Show);
		if (isDisposed ()) return;
	
		opened = true;
		if (!moved) {
			moved = true;
			Point location = getLocation();
			oldX = location.x;
			oldY = location.y;
			sendEvent (SWT.Move);
			if (isDisposed ()) return;
		}
		if (!resized) {
			resized = true;
			Point size = getSize ();
			oldWidth = size.x; //- trimWidth ();
			oldHeight = size.y; // - trimHeight ();
			sendEvent (SWT.Resize);
			if (isDisposed ()) return;
			if (layout != null) {
				markLayout (false, false);
				updateLayout (false);
			}
		}
	} else {
		sendEvent (SWT.Hide);
	}
}
/*---------------- NATIVE INTERFACE ----------------*/

protected native void _createHandle (Widget parent, int style, int index) /*-{
	if(!$wnd.dojo._hasResource["org.eclipse.swt.Shell"]){
		$wnd.dojo._hasResource["org.eclipse.swt.Shell"] = true;
		$wnd.dojo.provide("org.eclipse.swt.Shell");
		
		$wnd.dojo.require("dijit._Widget");
		$wnd.dojo.require("dijit._Container");
		$wnd.dojo.require("dijit._Templated");
		
		$wnd.dojo.declare(
			"org.eclipse.swt.Shell", $wnd.eval("[dijit._Widget, dijit._Container, dijit._Contained, dijit._Templated]"),{
				baseClass: "swtShell",
				barTitle: "",
				titleBarHeight: 0,
				templateString: 
					'<div class="${baseClass}" baseClass="${baseClass}" id="${id}">' +
						'<div dojoAttachPoint="titleBarNode" class="${baseClass}TitleBar dijitDialogTitleBar">' +
							'<span dojoAttachPoint="titleNode" class="${baseClass}Title">${barTitle}</span>' +
							'<span dojoAttachPoint="closeButtonNode" class="${baseClass}CloseIcon dijitDialogCloseIcon" dojoAttachEvent="onclick: onCancel"></span>' +
						'</div>' +
						'<div dojoAttachPoint="containerNode"></div>' +
					'</div>',
				postCreate: function(){
					this.inherited("postCreate", arguments); 
					$wnd.dojo.addClass(this.domNode, this.baseClass);
					if(!((style & (@org.eclipse.swt.SWT::SHELL_TRIM | @org.eclipse.swt.SWT::BORDER)) == @org.eclipse.swt.SWT::NONE || (style & (@org.eclipse.swt.SWT::NO_TRIM | @org.eclipse.swt.SWT::ON_TOP)) != 0)){
						this._moveable = new $wnd.dojo.dnd.TimedMoveable(this.domNode, {
							handle: this.titleBarNode, 
							timeout: 0 
						});
						$wnd.swt.setCssStyle(this.containerNode, {position: "relative"});
						$wnd.swt.setCssStyle(this.titleBarNode, {visibility: "hidden"});
						this.titleBarHeight = $wnd.swt.getNativeSize(this.titleBarNode).h;
						this.closeButtonNode.setAttribute("title", "Close");
					}					
					if((style & (@org.eclipse.swt.SWT::NO_TRIM)) != 0) {
						$wnd.swt.setCssStyle(this.domNode, {border: "0px"});
					} 
				},
				onCancel: function(){
					this.jObject.@org.eclipse.swt.widgets.Shell::close()();
				},
				setTitle: function(string){
					this.titleNode.innerHTML = this.barTitle = string;
				},
				setVisible: function(visible){
					var cssVisible = visible ? "visible" : "hidden";
					$wnd.swt.setCssStyle(this.domNode, {visibility: cssVisible});
					if (this._moveable) {
						$wnd.swt.setCssStyle(this.titleBarNode, {visibility: cssVisible});
					}
				},
				setWidgetBounds: function(dim){
					var dnDim = $wnd.swt.setBounds(this.domNode, dim);
					$wnd.swt.setBounds(this.containerNode, {l:0, t:0, w: dnDim.w, h:Math.max(dnDim.h-this.titleBarHeight)});
				}
			}
		);
	}
	var jsParent;
	if(parent==null){
		jsParent = this.@org.eclipse.swt.widgets.Widget::jsObject;
		if (jsParent == null)  {
		  jsParent = this.@org.eclipse.swt.widgets.Widget::display.@org.eclipse.swt.widgets.Display::jsDisplay;
		}
	} else {		
		jsParent = parent.@org.eclipse.swt.widgets.Widget::jsObject;
	}
	var params = {};
	params.jObject = this;
	var self = new $wnd.org.eclipse.swt.Shell(params);
	try {
		this.@org.eclipse.swt.widgets.Widget::jsObject = self;
		jsParent.addChild(self);
	} catch (e) {
//TODO Have to throw real exception for Java side also	
		$wnd.console.log(e);
	}
}-*/;

native void _getClientArea (Rectangle rectangle) /*-{
	var cnDim = $wnd.swt.getNativeBounds(this.@org.eclipse.swt.widgets.Widget::jsObject.containerNode, true);	
	rectangle.@org.eclipse.swt.graphics.Rectangle::x = cnDim.l;
	rectangle.@org.eclipse.swt.graphics.Rectangle::y = cnDim.t;
	rectangle.@org.eclipse.swt.graphics.Rectangle::width = cnDim.w;
	rectangle.@org.eclipse.swt.graphics.Rectangle::height = cnDim.h;
}-*/;


native void _getSize (Point point)  /*-{
//TODO implement this
	point.@org.eclipse.swt.graphics.Point::x = 0;
	point.@org.eclipse.swt.graphics.Point::y = 0;
}-*/;

void _setMinimumSize (int width, int height) {
}

native void _setLocation (int x, int y) /*-{
	$wnd.swt.setBounds(this.@org.eclipse.swt.widgets.Widget::jsObject.domNode, {l:x, t:y});
}-*/;

native void _setSize (int width, int height) /*-{
	$wnd.swt.setBounds(this.@org.eclipse.swt.widgets.Widget::jsObject.domNode, {w:width, h:height});
}-*/;

native void _setText (String string) /*-{
	this.@org.eclipse.swt.widgets.Widget::jsObject.setTitle(string);
}-*/;

native void _setVisible (boolean visible) /*-{
	this.@org.eclipse.swt.widgets.Widget::jsObject.setVisible(visible);
}-*/;


void _setRegion (Region region) {	
}

}