//= require <mootools-core-1.3-full-nocompat>
//= require <uiframework/UICEvent>
//= require <uiframework/UIComponent>

var UIButton = function(){
	this.Extends = UIComponent;
	this._labelStyles = null;

	this.initialize = function(element, options)
	{
		this.parent(element, options);
		this._labelStyles = {};
		this._labelText = '';
		this._selected = false;
		this.selectedChanged = false;
		this.href = '';
		this.upSkin = '';
		this.overSkin = '';
		this.downSkin = '';
		this.disabledSkin = '';
		this.labelPosition = 'center';
		this.labelTextChanged = false;
		this.skinChanged = false;
		this.label = null;
		this.data = null; // Misc storage of data

		this.tabStates = null; // Hash of UIComponents, 1 for each button state
		this.mouseDown = false;

		this.initIncumbantLink();

		this.mouseEvent_documentMouseUp = this.mouseEvent_documentMouseUp.bind(this);
	};

	/**
     * initializes the label and the href properties
     * if an existing link with text is found on the DOM
     * node passed to the constructor.
     */
	this.initIncumbantLink = function()
	{
		// Store the href if we have one and get rid
		// of the default link.
		var link = this.getFirst('a');
		if (link)
		{
			this.href = link.href;
			this._labelText = link.get('text');
			this.dispose(link);
		}
		else
		{
			var pTag = this.getFirst('p');
			if (pTag)
			{
				this._labelText = pTag.get('text');
				this.dispose(pTag);
			}
		}
	};

	//----------------------------------------------------------------------
	// UIComponent overrides
	this.commitProperties = function()
	{
		this.parent();
	
		if (this.labelTextChanged)
		{
			var label = this.getLabelNode();
			label.set('text', this._labelText);

			this.labelTextChanged = false;
		}

		if (this.skinChanged)
		{
			this.createChildren();
	    
			this.tabStates['up'].setStyle("background-image", this.upSkin);
			this.tabStates['over'].setStyle("background-image", this.overSkin);
			this.tabStates['down'].setStyle("background-image", this.downSkin);
			this.tabStates['disabled'].setStyle("background-image", this.disabledSkin);

			this.skinChanged = false;
		}

		if (this.selectedChanged)
		{
			this.mouseEvent_documentMouseUp();
			this.selectedChanged = false;
		}
	};

	this.createChildren = function()
	{
		this.parent();
	
		if (!this.tabStates)
		{
			this.tabStates = {};
			this.tabStates['up'] = new UIComponent('div');
			this.tabStates['over'] = new UIComponent('div');
			this.tabStates['down'] = new UIComponent('div');
			this.tabStates['disabled'] = new UIComponent('div');

			this.addChild(this.tabStates['up']);
			this.addChild(this.tabStates['over']);
			this.addChild(this.tabStates['down']);
			this.addChild(this.tabStates['disabled']);
		}
	};

	this.childrenCreated = function()
	{
		this.parent();
	
		this.addEventListener('mouseenter', this.mouseEvent_mouseEnter, false, 0, this);
		this.addEventListener('mouseleave', this.mouseEvent_mouseLeave, false, 0, this);
		this.addEventListener('mousedown', this.mouseEvent_mouseDown, false, 0, this);
		this.addEventListener('mouseup', this.mouseEvent_mouseUp, false, 0, this);

		this.tabStates['over'].fade("hide");
		this.tabStates['down'].fade("hide");
		this.tabStates['disabled'].fade("hide");
	
		var options = {
			duration:500,
			transition:Fx.Transitions.Quint.easeOut
		};
		this.tabStates['over'].set('tween', options);
		this.tabStates['down'].set('tween', options);
		this.tabStates['disabled'].set('tween', options);
	};

	this.updateDisplayList = function(width, height)
	{
		this.parent(width, height);
		for (var key in this.tabStates)
		{
			this.tabStates[key].setActualSize(width, height);
			this.tabStates[key].move(0, 0);
		}
		if (!this._enabled)
		{
			this.tabStates['up'].fade('hide');
			this.tabStates['over'].fade('hide');
			this.tabStates['down'].fade('hide');
			this.tabStates['disabled'].fade('in');
			this.morphLabel('disabled');
		}
		var label = this.getLabelNode();
		label.move((width - label.width()) / 2, (height - label.height()) / 2);
	};

	//----------------------------------------------------------------------
	// Helper functions

	/**
     * On-Demand access and creation of the
     * label component
     * @return UIComponent
     */
	this.getLabelNode = function()
	{
		if (!this.label)
		{
			var o = this;
			this.label = new UIComponent('p', {
				"class":"buttonLabel"
			});
			this.label.set('morph', {
				duration: 'short',
				transition:Fx.Transitions.Quint.easeOut
			});
			this.label.addEventListener('mousedown', function(event){
				o.mouseEvent_mouseDown(event);
				event.preventDefault()
			});
			this.addChild(this.label);
			this.label.setStyles(
			{
				margin:0,
				padding:0,
				overflow:"hidden"
			});
		}
		return this.label;
	};

	/**
     * Handles the mouseOver state of the button
     */
	this.mouseEvent_mouseEnter = function(event)
	{
		if (!this._enabled)
			return;
		if (this.mouseDown)
		{
			this.tabStates['down'].fade("in");
			this.tabStates['over'].fade("out");
			this.morphLabel('down');
		}
		else
		{
			this.tabStates['down'].fade("out");
			this.tabStates['over'].fade("in");
			this.morphLabel('over');
		}
	};

	/**
     * Dispatches a "buttonDown" event, changes the
     * button's state and adds a listener to the document
     * for mouseUp events that may occur outside of the
     * button's bounds.
     */
	this.mouseEvent_mouseDown = function(event)
	{
		if (!this._enabled)
			return;
		// Dispatch both a buttonDown and a mouseDown event.
		// Button down is for auto-repeat and mouseDown is
		// simply a conversion from a DOM event to a UICEvent.
		if (this.hasEventListener("buttonDown"))
		{
			var e = new UICEvent();
			Object.append(e, event);
			e.type = "buttonDown";
			e.data = this.href;
			e.bubbles = false;
			this.dispatchEvent(e);
		}
		
		this.mouseDown = true;
		this.tabStates['down'].fade("in");
		this.morphLabel('down');
	};

	this.mouseEvent_mouseUp = function()
	{
		if (!this._enabled)
			return;
		this.mouseDown = false;
		if (!this._selected)
		{
			this.tabStates['down'].fade("out");
			this.morphLabel('up');
		}
	};

	this.mouseEvent_documentMouseUp = function()
	{
		if (!this._enabled)
			return;
		document.removeEvent("mouseup", this.mouseEvent_documentMouseUp);
		this.mouseDown = false;
		if (!this._selected)
		{
			this.tabStates['down'].fade("out");
			this.morphLabel('up');
		}
		else
		{
			this.tabStates['down'].fade("in");
			this.morphLabel('down');
		}
		this.tabStates['over'].fade("out");
	};

	this.mouseEvent_mouseLeave = function(event)
	{
		if (!this._enabled)
			return;
		if (!this.mouseDown)
		{
			this.mouseEvent_documentMouseUp();
		}
		else
			document.addEvent("mouseup", this.mouseEvent_documentMouseUp);
	};

	this.morphLabel = function(state /* String */)
	{
		if (this._labelStyles && this._labelStyles[state])
		{
			this.getLabelNode().morph(this._labelStyles[state]);
		}
	};

	//-----------------------------------------------------------------
	// Getters / setters
	/**
     * Gets/Sets the button skin for each state.
     *
     * @param {Object} value containing the key=>value pairs of the skins to set.
     * @return {Object} containing the values for each skin state.
     */
	this.buttonSkins = function(value)
	{
		var skinChanged  = false;
		for (var key in value)
		{
			if (/(upSkin|downSkin|overSkin|disabledSkin)/.test(key) && this[key] != value[key])
			{
				this[key] = value[key];
				skinChanged = true;
			}
		}
		if (skinChanged)
		{
			this.skinChanged = true;
			this.invalidateDisplayList();
			this.invalidateProperties();
		}
		return {
			upSkin:this.upSkin,
			downSkin:this.downSkin,
			overSkin:this.overSkin,
			disabledSkin:this.disabledSkin
		};
	};

	/**
     * Sets the styles used for the up, over and down
     * states of the button.  The button morphs to each style
     * as the state changes
     *
     * @param {Object} value the object containing the CSS selector names
     * used for each state.
     */
	this.labelStyles = function(value /* Object */)
	{
		if (arguments.length)
			this._labelStyles = value;
		return this._labelStyles;
	};

	/**
     * Gets / sets the label text of the button programatically.
     * @param value String The new label
     * @return String The current label or the String provided in the argument.
     */
	this.labelText = function(value)
	{
		if (arguments.length && this._labelText != value)
		{
			this._labelText = value;
			this.labelTextChanged = true;
	    
			this.invalidateDisplayList();
			this.invalidateProperties();
		}
		return this._labelText;
	};

	/**
     * Gets / Sets the selected state of the button.
     * @param value Boolean indicating whether or not the button is in a selected state.
     */
	this.selected = function(value)
	{
		if (arguments.length && this._selected != value)
		{
			this._selected = value;
			this.selectedChanged = true;

			this.invalidateDisplayList();
			this.invalidateProperties();
		}
	};
};
UIButton = new Class(new UIButton());
