/*******************************************************************************
 * 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.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics2.Surface;

public class Composite extends Scrollable {
	Layout layout;
	Control [] tabList, children;
	int layoutCount, backgroundMode;
	Surface surface;
	
Composite () {
}

public Composite (Composite parent, int style) {
	super (parent, style);
}

void addControl (Control control) {
	if (children == null) children = new Control [4];
	int length = children.length;
	for (int i=0; i<length; i++) {
		if (children [i] == control) return;
	}
	int index = 0;
	while (index < length) {
		if (children [index] == null) break;
		index++;
	}
	if (index == length) {
		Control [] temp = new Control [length + 4];
		System.arraycopy (children, 0, temp, 0, length);
		children = temp;
	}
	children [index] = control;
}

Control[] _getChildren () {
	if (children == null) return new Control[0];
	int count = 0;
	int length = children.length;
	while (count < length) {
		if (children [count] == null) break;
		count++;
	}
	Control [] result = new Control [count];
	System.arraycopy (children, 0, result, 0, count);
	return result;
}

Control [] _getTabList () {
	if (tabList == null) return tabList;
	int count = 0;
	for (int i=0; i<tabList.length; i++) {
		if (!tabList [i].isDisposed ()) count++;
	}
	if (count == tabList.length) return tabList;
	Control [] newList = new Control [count];
	int index = 0;
	for (int i=0; i<tabList.length; i++) {
		if (!tabList [i].isDisposed ()) {
			newList [index++] = tabList [i];
		}
	}
	tabList = newList;
	return tabList;
}

public void changed (Control[] changed) {
	checkWidget ();
	if (changed == null) error (SWT.ERROR_INVALID_ARGUMENT);
	for (int i=0; i<changed.length; i++) {
		Control control = changed [i];
		if (control == null) error (SWT.ERROR_INVALID_ARGUMENT);
		if (control.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
		boolean ancestor = false;
		Composite composite = control.parent;
		while (composite != null) {
			ancestor = composite == this;
			if (ancestor) break;
			composite = composite.parent;
		}
		if (!ancestor) error (SWT.ERROR_INVALID_PARENT);
	}
	for (int i=0; i<changed.length; i++) {
		Control child = changed [i];
		Composite composite = child.parent;
		while (child != this) {
			if (composite.layout == null || !composite.layout.flushCache (child)) {
				composite.state |= LAYOUT_CHANGED;
			}
			child = composite;
			composite = child.parent;
		}
	}
}

protected void checkSubclass () {
	/* Do nothing - Subclassing is allowed */
}

public Point computeSize (int wHint, int hHint, boolean changed) {
	checkWidget ();
	Point size = new Point(0, 0) ;
	if (layout != null) {
		if (wHint == SWT.DEFAULT || hHint == SWT.DEFAULT) {
			changed |= (state & LAYOUT_CHANGED) != 0;
			state &= ~LAYOUT_CHANGED;
			size = layout.computeSize (this, wHint, hHint, changed);
		} else {
			size = new Point (wHint, hHint);
		}
	} else {
		size = minimumSize (wHint, hHint, changed);
	}
	if (size.x == 0) size.x = DEFAULT_WIDTH;
	if (size.y == 0) size.y = DEFAULT_HEIGHT;
	if (wHint != SWT.DEFAULT) size.x = wHint;
	if (hHint != SWT.DEFAULT) size.y = hHint;
	Rectangle trim = computeTrim (0, 0, size.x, size.y);
	return new Point (trim.width, trim.height);
}

Control [] computeTabList () {
	Control result [] = super.computeTabList ();
	if (result.length == 0) return result;
	Control [] list = tabList != null ? _getTabList () : _getChildren ();
	for (int i=0; i<list.length; i++) {
		Control child = list [i];
		Control [] childList = child.computeTabList ();
		if (childList.length != 0) {
			Control [] newResult = new Control [result.length + childList.length];
			System.arraycopy (result, 0, newResult, 0, result.length);
			System.arraycopy (childList, 0, newResult, result.length, childList.length);
			result = newResult;
		}
	}
	return result;
}

void createWidget (Display display, Widget parent, int style, int index) {
	super.createWidget (display, parent, style, index);
	//TODO - subclasses , different place than other ports
	state |= CANVAS;
}

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

void fixTabList (Control control) {
	if (tabList == null) return;
	int count = 0;
	for (int i=0; i<tabList.length; i++) {
		if (tabList [i] == control) count++;
	}
	if (count == 0) return;
	Control [] newList = null;
	int length = tabList.length - count;
	if (length != 0) {
		newList = new Control [length];
		int index = 0;
		for (int i=0; i<tabList.length; i++) {
			if (tabList [i] != control) {
				newList [index++] = tabList [i];
			}
		}
	}
	tabList = newList;
}

public int getBackgroundMode () {
	checkWidget ();
	return backgroundMode;
}

public Control[] getChildren () {
	checkWidget ();
	return _getChildren ();
}

public Layout getLayout () {
	checkWidget ();
	return layout;
}

Surface getSurface () {
	if (surface == null) {
		surface = new Surface();
		surface.createSurface(this, 400, 400);
	}
	return surface;
}

public Control[] getTabList () {
	checkWidget ();
	Control [] tabList = _getTabList ();
	if (tabList == null) {
		int count = 0;
		Control [] list =_getChildren ();
		for (int i=0; i<list.length; i++) {
			if (list [i].isTabGroup ()) count++;
		}
		tabList = new Control [count];
		int index = 0;
		for (int i=0; i<list.length; i++) {
			if (list [i].isTabGroup ()) {
				tabList [index++] = list [i];
			}
		}
	}
	return tabList;
}

public boolean getLayoutDeferred () {
	checkWidget ();
	return layoutCount > 0 ;
}

boolean hooksKeys () {
	return hooks (SWT.KeyDown) || hooks (SWT.KeyUp);
}

public boolean isLayoutDeferred () {
	checkWidget ();
	return findDeferredControl () != null;
}

public void layout () {
	checkWidget ();
	layout (true);
}

public void layout (boolean changed) {
	checkWidget ();
	if (layout == null) return;
	layout (changed, false);
}

public void layout (boolean changed, boolean all) {
	checkWidget ();
	if (layout == null && !all) return;
	markLayout (changed, all);
	updateLayout (all);
}

public void layout (Control[] changed) {
	checkWidget ();
	if (changed == null) error (SWT.ERROR_INVALID_ARGUMENT);
	for (int i=0; i<changed.length; i++) {
		Control control = changed [i];
		if (control == null) error (SWT.ERROR_INVALID_ARGUMENT);
		if (control.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
		boolean ancestor = false;
		Composite composite = control.parent;
		while (composite != null) {
			ancestor = composite == this;
			if (ancestor) break;
			composite = composite.parent;
		}
		if (!ancestor) error (SWT.ERROR_INVALID_PARENT);
	}
	int updateCount = 0;
	Composite [] update = new Composite [16];
	for (int i=0; i<changed.length; i++) {
		Control child = changed [i];
		Composite composite = child.parent;
		while (child != this) {
			if (composite.layout != null) {
				composite.state |= LAYOUT_NEEDED;
				if (!composite.layout.flushCache (child)) {
					composite.state |= LAYOUT_CHANGED;
				}
			}
			if (updateCount == update.length) {
				Composite [] newUpdate = new Composite [update.length + 16];
				System.arraycopy (update, 0, newUpdate, 0, update.length);
				update = newUpdate;
			}
			child = update [updateCount++] = composite;
			composite = child.parent;
		}
	}
	for (int i=updateCount-1; i>=0; i--) {
		update [i].updateLayout (false);
	}
}

void markLayout (boolean changed, boolean all) {
	if (layout != null) {
		state |= LAYOUT_NEEDED;
		if (changed) state |= LAYOUT_CHANGED;
	}
	if (all) {
		Control [] children = _getChildren ();
		for (int i=0; i<children.length; i++) {
			children [i].markLayout (changed, all);
		}
	}
}

Point minimumSize (int wHint, int hHint, boolean changed) {
	Control [] children = _getChildren ();
	int width = 0, height = 0;
	for (int i=0; i<children.length; i++) {
		Rectangle rect = children [i].getBounds ();
		width = Math.max (width, rect.x + rect.width);
		height = Math.max (height, rect.y + rect.height);
	}
	return new Point (width, height);
}

void releaseChildren (boolean destroy) {
	Control [] children = _getChildren ();
	for (int i=0; i<children.length; i++) {
		Control child = children [i];
		if (child != null && !child.isDisposed ()) {
			child.release (false);
		}
	}
	super.releaseChildren (destroy);
}

void releaseWidget () {
	super.releaseWidget ();
	layout = null;
	tabList = null;
	children = null;
}

void removeControl (Control control) {
	fixTabList (control);
	int index = 0;
	while (index < children.length) {
		if (children [index] == control) break;
		index++;
	}
	System.arraycopy (children, index + 1, children, index, children.length - index);
	children [children.length - 1] = null;
}

public void setBackgroundMode (int mode) {
}

int setBounds (int x, int y, int width, int height, int flags) {
	int result = super.setBounds (x, y, width, height, flags);
	if ((result & RESIZED) != 0 && layout != null) {
		markLayout (false, false);
		updateLayout (false);
	}
	return result;
}

public boolean setFocus () {
	return false;
}


public void setLayout (Layout layout) {
	checkWidget ();
	this.layout = layout;
}

public void setLayoutDeferred (boolean defer) {
	if (!defer) {
		if (--layoutCount == 0) {
			if ((state & LAYOUT_CHILD) != 0 || (state & LAYOUT_NEEDED) != 0) {
				updateLayout (true);
			}
		}
	} else {
		layoutCount++;
	}
}

boolean setTabGroupFocus () {
	if (isTabItem ()) return setTabItemFocus ();
	boolean takeFocus = (style & SWT.NO_FOCUS) == 0;
	if ((state & CANVAS) != 0) {
		takeFocus = hooksKeys ();
		if ((style & SWT.EMBEDDED) != 0) takeFocus = true;
	}
	if (takeFocus && setTabItemFocus ()) return true;
	Control [] children = _getChildren ();
//	for (int i=0; i<children.length; i++) {
//		Control child = children [i];
//		if (child.isTabItem () && child.setRadioFocus ()) return true;
//	}
	for (int i=0; i<children.length; i++) {
		Control child = children [i];
		if (child.isTabItem () && child.setTabItemFocus ()) return true;
	}
	return false;
}

public void setTabList (Control[] tabList) {
	checkWidget ();
	if (tabList != null) {
		for (int i=0; i<tabList.length; i++) {
			Control control = tabList [i];
			if (control == null) error (SWT.ERROR_INVALID_ARGUMENT);
			if (control.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
			if (control.parent != this) error (SWT.ERROR_INVALID_PARENT);
		}
		Control [] newList = new Control [tabList.length];
		System.arraycopy (tabList, 0, newList, 0, tabList.length);
		tabList = newList;
	} 
	this.tabList = tabList;
}

void updateLayout (boolean all) {
	Composite parent = findDeferredControl ();
	if (parent != null) {
		parent.state |= LAYOUT_CHILD;
		return;
	}
	if ((state & LAYOUT_NEEDED) != 0) {
		boolean changed = (state & LAYOUT_CHANGED) != 0;
		state &= ~(LAYOUT_NEEDED | LAYOUT_CHANGED);
		layout.layout (this, changed);
	}
	if (all) {
		state &= ~LAYOUT_CHILD;
		Control [] children = _getChildren ();
		for (int i=0; i<children.length; i++) {
			children [i].updateLayout (all);
		}
	}
}

/*--------------------- NATIVE INTERFACE ------------------*/

protected native void _createHandle (Widget parent, int style, int index) /*-{
	if(!$wnd.dojo._hasResource["org.eclipse.swt.Composite"]){
		$wnd.dojo._hasResource["org.eclipse.swt.Composite"] = true;
		$wnd.dojo.provide("org.eclipse.swt.Composite");
		
		$wnd.dojo.require("dijit._Widget");
		$wnd.dojo.require("dijit._Container");
		
		$wnd.dojo.declare(
			"org.eclipse.swt.Composite", $wnd.eval("[dijit._Widget, dijit._Container, dijit._Contained]"),{
				baseClass: "swtComposite",
				postCreate: function(){
					this.inherited("postCreate",arguments); 
					$wnd.dojo.addClass(this.domNode, this.baseClass);
				}
			}
		);
	}
	var jsParent;
	if(parent==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 = {};
	var self = new $wnd.org.eclipse.swt.Composite(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);
	}
}-*/;


public void sendResize() {
	super.sendResize();
	if ((style & CANVAS) != 0) {
		Event event = new Event ();
		GC gc = new GC (this);
		event.gc = gc;
		sendEvent (SWT.Paint, event);
		gc.dispose ();
	}
}

}
