//= require <mootools-core-1.3-full-nocompat>
//= require <uiframework/UIComponent>
//= require <uiframework/Tween>
var Panel = function(id /* String */){

	/**
     * @ignore
     */
	this.Extends = UIComponent;

	/**
     * The close button, if displayed.
     * @type UIComponent
     */
	this.closeButton = null;

	/**
     * Wrapper for the main content.
     * @type UIComponent
     */
	this.contentWrapper = null;

	/**
     * Background div for opaque backgrouns
     * @type UIComponent
     */
	this.background = null;

	/**
     * Hash for the elements that make up the border.
     * @type Object
     */
	this.borderElements = null;
	
	/**
	 * The tween used to animate the panel.
	 * @type Tween
	 */
	this.expandTween = null;

	/**
	 * Flag that shows whether or
	 * not the panel is expanded.
	 * @type Boolean
	 */
	this._expanded = false;

	/**
	 * Flag indicating whether or not
	 * the expanded state
	 */
	this._expandedChanged = false;

	/**
	 * Determines if the panel is expandable.
	 * @type Boolean
	 */
	this._expandable = false;

	/**
	 * The rect that defines the
	 * expanded panel size.
	 * @type Object
	 */
	this._expandedRect = null;

	/**
	 * The rect that defines the
	 * original panel size.
	 */
	this._originalRect = null;

	/**
	 * The current rect of the panel.
	 */
	this._currentRect = null;

	/**
     * Hash containing details of the borders and corners.
     * of the panel object.
     * @type Object
     */
	this._borderData = null;

	/**
     * Regular expression used to validate against
     * the key value pairs of the borderData object.
     * @type Object
     */
	this._validBorders = /^(topLeft|top|topRight|right|bottomRight|bottom|bottomLeft|left)$/;
	
	/**
     * Indicates the border data has changed.
     * @type Boolean
     */
	this._borderDataChanged = false;

	/**
     * Object containing the styles used on the background.
     * @type Object
     */
	this._backgroundStyle = null;

	/**
     * @ignore
     */
	this.initialize = function(id /* String */)
	{
		this.parent('div', {
			id:id
		});

		this.borderElements = {};
		this.sizeHash = {};
		this._backgroundStyle = {};
		this._currentRect = {};

		this.tweenUpdate = this.tweenUpdate.bind(this);
		this.tweenComplete = this.tweenComplete.bind(this);
	};

	//-----------------------------------------------
	// UIComponent overrides

	this.commitProperties = function()
	{
		this.parent();
		if (this._borderDataChanged)
		{
			this.borderDataChangedHandler();
			this._borderDataChanged = false;
		}
		if (this._expandedChanged)
		{
			this.expandedChangedHandler();
			this._expandedChanged = false;
		}
	};

	this.createChildren = function()
	{
		this.parent();
		if (this.background) return;
		this.background = new UIComponent('div', {
			styles:this._backgroundStyle
		});
		this.addChild(this.background);
	};

	this.updateDisplayList = function(unscaledWidth, unscaledHeight)
	{
		this.parent(unscaledWidth, unscaledHeight);
		this.layoutPanel(unscaledWidth, unscaledHeight);
		if (this.expanded() && this._originalRect)
			return;
		this._originalRect = {
			x:this._x,
			y:this._y,
			width:unscaledWidth,
			height:unscaledHeight
		};
	};

	this.layoutPanel = function(unscaledWidth, unscaledHeight)
	{
		var top = this.borderElements['top'];
		var topLeft = this.borderElements['topLeft'];
		var topRight = this.borderElements['topRight'];
		var left = this.borderElements['left'];
		var bottomLeft = this.borderElements['bottomLeft'];
		var bottom = this.borderElements['bottom'];
		var bottomRight = this.borderElements['bottomRight'];
		var right = this.borderElements['right'];
		var delta = 0;

		if (topLeft)
			topLeft.move(0, 0);

		if (topRight)
			topRight.move(unscaledWidth - topRight.width(), 0);

		if (bottomLeft)
			bottomLeft.move(0, unscaledHeight - bottomLeft.height());

		if (bottomRight)
			bottomRight.move(unscaledWidth - bottomRight.width(), unscaledHeight - bottomRight.height());

		if (top)
		{
			delta = topLeft ? topLeft.width() : 0;
			delta += topRight ? topRight.width() : 0;
			top.width(unscaledWidth - delta);
			top.move(topLeft ? topLeft.width() : 0, 0);
		}

		if (right)
		{
			delta = topRight ? topRight.height() : 0;
			delta += bottomRight ? bottomRight.height() : 0;
			right.height(unscaledHeight - delta);
			right.move(unscaledWidth - right.width(), topRight ? topRight.height() : 0);
		}

		if (bottom)
		{
			delta = bottomLeft ? bottomLeft.width() : 0;
			delta += bottomRight ? bottomRight.width() : 0;
			bottom.width(unscaledWidth - delta);
			bottom.move(bottomLeft ? bottomLeft.width() : 0, unscaledHeight - bottom.height());
		}

		if (left)
		{
			delta = topLeft ? topLeft.height() : 0;
			delta += bottomLeft ? bottomLeft.height() : 0;
			left.height(unscaledHeight - delta);
			left.move(0, topLeft ? topLeft.height() : 0);
		}
		if (this.background)
		{
			var _h = bottom ? unscaledHeight - bottom.height() : unscaledHeight;
			_h -= top ? top.height() : 0;
			var _w = left ? unscaledWidth - left.width() : unscaledWidth;
			_w -= right ? right.width() : 0;
			this.background.setActualSize(_w, _h);
			this.background.move(left ? left.width() : 0, top ? top.height() : 0);
		}
	};
	//-----------------------------------------------
	// Helper functions

	this.borderDataChangedHandler = function()
	{
		// Remove all border elements
		var key;
		for (key in this._borderElements)
			this.removeChild(this._borderElements[key])
		this.borderElements = {};
		// Preload the images.
		for (key in this._borderData)
		{
			var styles;
			var imgUrl = this._borderData[key];
			if (this._validBorders.test(key))
			{
				switch(key)
				{
					case "topLeft":
						styles = {
							"background":"url('"+imgUrl+"') no-repeat top left"
						};
						break;

					case "top":
						styles = {
							"background":"url('"+imgUrl+"') repeat-x top"
						};
						break;

					case "topRight":
						styles = {
							"background":"url('"+imgUrl+"') no-repeat top right"
						};
						break;

					case "right":
						styles = {
							"background":"url('"+imgUrl+"') repeat-y right"
						};
						break;

					case "bottomRight":
						styles = {
							"background":"url('"+imgUrl+"') no-repeat bottom right"
						};
						break;

					case "bottom":
						styles = {
							"background":"url('"+imgUrl+"') repeat-x bottom"
						};
						break;

					case "bottomLeft":
						styles = {
							"background":"url('"+imgUrl+"') no-repeat bottom left"
						};
						break;

					case "left":
						styles = {
							"background":"url('"+imgUrl+"') repeat-y left"
						};
						break;
				}
				//---------
				var div = new UIComponent('div', {
					styles:styles,
					"class":"panelImage"
				});
				div.visible(this._visible);
				this.addChild(div, "top");
				this.borderElements[key] = div;
				// Preload
				var img = new Element('img');
				img.addEvent('load', this.preloadCompleteHandler);
				img.store("data", {
					div:div,
					parent:this
				});
				$(document.body).grab(img);
				img.set('src', imgUrl);
			}
		}
	};

	this.preloadCompleteHandler = function()
	{
		var o = this;
		var data = o.retrieve("data");
		var worker = function()
		{
			var size = o.getSize();
			data.div.setActualSize(size.x, size.y);
			o.removeEvent('load', data.parent.preloadCompleteHandler);
			o.eliminate("data");
			o.dispose();
		}
		data.parent.callLater(worker);
	};

	this.expandedChangedHandler = function()
	{
		var tween = this.getExpandTween();
		tween.cancel();
		this._currentRect = {x:this._x, y:this._y, width:this._width, height:this._height};
		if (this._expanded)
			tween.start(0, 1);
		else
			tween.start(1, 0);
	};

	this.getExpandTween = function()
	{
		if (this.expandTween)
			return this.expandTween;
		this.expandTween = new Tween(null, this.tweenUpdate, this.tweenComplete);
		return this.expandTween;
	};

	this.tweenUpdate = function(value)
	{
		this._currentRect.x =  this._originalRect.x + (this._expandedRect.x - this._originalRect.x) * value;
		this._currentRect.y =  this._originalRect.y + (this._expandedRect.y - this._originalRect.y) * value;
		this._currentRect.width = this._originalRect.width + ((this._expandedRect.width - this._originalRect.width) * value);
		this._currentRect.height = this._originalRect.height + ((this._expandedRect.height - this._originalRect.height) * value);
		this.setActualSize(this._currentRect.width, this._currentRect.height);
		this.move(this._currentRect.x, this._currentRect.y);
		this.validateNow();
	};

	this.tweenComplete = function(value)
	{
		
	};

	//-----------------------------------------------
	// Getters / Setters

	/**
	 * The expanded state of the
	 * panel.
	 * 
	 * @param value {Boolean}
	 * @return Boolean
	 */
	this.expanded = function(value /* Boolean */)
	{
		if (this.expandable() && arguments.length && this._expanded != value)
		{
			this._expanded = value;
			this._expandedChanged = true;

			this.invalidateProperties();
		}
		return this._expanded;
	};

	/**
	 * Object containing x, y, with and height properties
	 * that define the expanded rectangle of this panel.
	 *
	 * @param value {Object} The new expanded rectangle.
	 */
	this.expandedRect = function(value /* Object */)
	{
		if (arguments.length && this._expandedRect != value)
			this._expandedRect = value;
		return this._expandedRect;
	};

	/**
     * Object used to describe the border elements
     * of this panel.  Contains a key value pair
     * with the key specifying the border side
     * and the value specifying the background image
     * location.
     *
     * @param value {Object} containing the key value pairs.
     */
	this.borderData = function(value /* Object */)
	{
		if (arguments.length)
		{
			this._borderData = value;
			this._borderDataChanged = true;

			this.invalidateSize();
			this.invalidateDisplayList();
		}
	};

	/**
     * Gets / Sets the style used for the background
     * @param styles {Object} The object containing style key value pairs
     * @return Object
     */
	this.backgroundStyle = function(styles /* Object */)
	{
		if (arguments.length)
		{
			this._backgroundStyle = styles;
			if (this.background)
				this.background.set('styles', this._backgroundStyle);
		}
		return this._backgroundStyle;
	};

	/**
	 * Flag indicating whether or not this panel
	 * is expandable.
	 *
	 * @param value {Boolean}
	 * @return Boolean
	 * @default false
	 */
	this.expandable = function(value /* Boolean */)
	{
		if (arguments.length && this._expandable != value)
			this._expandable = value;
		return this._expandable;
	};

	/**
	 * @ignore
	 */
	this.visible = function(value)
	{
		if (this.borderElements && arguments.length)
		{
			if (this.background)
				this.background.visible(value);
			for (var key in this.borderElements)
			{
				this.borderElements[key].visible(value);
			}
		}
		return this.parent(value);
	}
}
Panel = new Class(new Panel());
