// status = {title, isAppStatuses, blockApp, blockView, additional}
/* PAGE CONTROL ELEMENT
***************************************************************************************************/
var PageControlElement = Class.create(
{
	PageControlElement: 1,

	statuses:{
		enabled: "enabled",
		disabled: "disabled",
		pressed: "pressed",
		active: "active",
		wait: "wait",
		infocus: "infocus",
		hidden: "hidden"
	},


	initialize: function(element)
	{
		//{ Fields
		this._defaultStatus = this.statuses.enabled,
		this.eventParams = new Object();
		this.eventParams.owner = this;

		this.element = element;
		this.id = element.id;
		this.status = this.statuses.enabled;
		this.styleClass = "";
	//}
		if(this.clearStatus == undefined)
			throwNotDefinedError('clearStatus');
		if(this.setStatus == undefined)
			throwNotDefinedError('setStatus');

	//{ Clear statuses from element class properties
		for (var property in this.statuses){
			this.element.removeClassName(this.statuses[property]);
		}
	//}
		this.infocus = 0;
		this._observe();

		this.setStatus(this._defaultStatus);
	},


	clearStatus: function()
	{
		this.element.removeClassName(this.styleClass);
		this.status = this.statuses.enabled;
		if(this.infocus){
			this.styleClass = this.statuses.infocus;
			this.element.addClassName(this.styleClass);
		} else {
			this.styleClass = "";
		}
		this.element.fire("Control:statusCleared");
	},


	setStatus: function(status)
	{
		if(this.status != this.statuses.enabled && this.status == status){
			return;
		}
		switch(status){
			case this.statuses.enabled:
				this.clearStatus();
				break;
			case this.statuses.disabled:
				this.element.removeClassName(this.styleClass);
				this.styleClass = status;
				this.element.addClassName(status);
				this.status = status;
				this.element.fire("Control:setDisabled");
				break;
			case this.statuses.pressed:
				this.element.removeClassName(this.styleClass);
				this.styleClass = status;
				this.element.addClassName(status);
				this.element.fire("Control:setPressed");
				break;
			case this.statuses.hidden:
				this.element.removeClassName(this.styleClass);
				this.element.addClassName(status);
				this.status = status;
				this.styleClass = status;
				this.element.fire("Control:setHidden");
				break;
			case this.statuses.active:
				this.element.removeClassName(this.styleClass);
				if(this.infocus){
					this.styleClass = status + this.statuses.infocus;
				} else {
					this.styleClass = status;
				}
				this.element.addClassName(this.styleClass);
				this.status = status;
				this.element.fire("Control:setActive");
				break;
			case this.statuses.wait:
				this.element.removeClassName(this.styleClass);
				if(this.infocus){
					this.styleClass = status + this.statuses.infocus;
				} else {
					this.styleClass = status;
				}
				this.element.addClassName(this.styleClass);
				this.status = status;
				this.element.fire("Control:setWait");
				break;
			default:
				throw "Unknown status type";
		}
	},

//{ Delegates
	//{ Common actions
	_onFocusDelegate: function(event)
	{
		Event.stop(event);
		if( this.status == this.statuses.enabled ||
			this.status == this.statuses.active ||
			this.status == this.statuses.wait )
		{
			this.infocus = 1;
			this.setStatus(this.status);
			this.element.fire("Control:focus", this.eventParams);
		} else {
			Event.stop(event);
		}
	},


	_onBlurDelegate: function(event)
	{
		Event.stop(event);
		if( this.status == this.statuses.enabled ||
			this.status == this.statuses.active ||
			this.status == this.statuses.wait )
		{
			this.infocus = 0;
			this.setStatus(this.status);
			this.element.fire("Control:blur", this.eventParams);
		} else {
			Event.stop(event);
		}
	},


	_onClickDelegate: function(event)
	{
		if( this.status == this.statuses.disabled ||
			this.status == this.statuses.hidden ||
			this.status == this.statuses.wait ||
			this.status == this.statuses.active )
		{
			Event.stop(event);
		} else {
			this.element.fire("Control:click", this.eventParams);
		}
	},
	//}
	//{ Mouse actions
	_onMouseOverDelegate: function(event)
	{
		Event.stop(event);
		if(	this.status == this.statuses.enabled ||
			this.status == this.statuses.active ||
			this.status == this.statuses.infocus )
		{
			this.element.fire("Control:mouseover", this.eventParams);
		} else {
			Event.stop(event);
		}
	},


	_onMouseDownDelegate: function(event)
	{
		Event.stop(event);
		if(	this.status == this.statuses.enabled ||
			this.status == this.statuses.infocus )
		{
			this.setStatus(this.statuses.pressed);
			this.element.fire("Control:mousedown", this.eventParams);
		} else {
			Event.stop(event);
		}
	},


	_onMouseUpDelegate: function(event)
	{
		Event.stop(event);
		if(	this.status == this.statuses.hidden ){
			Event.stop(event);
		} else {
			this.setStatus(this.status);
			this.element.fire("Control:mouseup", this.eventParams);
		}
	},


	_onDbClickDelegate: function(event)
	{
		Event.stop(event);
		if( this.status == this.statuses.enabled ||
			this.status == this.statuses.infocus )
		{
			this.element.fire("Control:dbclick", this.eventParams);
		} else {
			Event.stop(event);
		}
	},


	_onMouseOutDelegate: function(event)
	{

		Event.stop(event);
		if(	this.status == this.statuses.enabled ||
			this.status == this.statuses.active ||
			this.status == this.statuses.infocus )
		{
			this.element.fire("Control:mouseout", this.eventParams);
		} else {
			Event.stop(event);
		}
	},
	//}
	//{ Keyboard actions
	_onKeyDownDelegate: function(event)
	{
		Event.stop(event);
		var params = this.eventParams;
		params.charCode = event.charCode;
		params.keyCode = event.keyCode;
		if(	this.status == this.statuses.enabled ||
			this.status == this.statuses.infocus )
		{
			this.setStatus(this.statuses.pressed);
			this.element.fire("Control:keydown", params);
		} else {
			Event.stop(event);
		}
	},


	_onKeyPressDelegate: function(event)
	{
		Event.stop(event);
		var params = this.eventParams;
		params.charCode = event.charCode;
		params.keyCode = event.keyCode;
		this.element.fire("Control:keypress", params);
	},


	_onKeyUpDelegate: function(event)
	{
		Event.stop(event);
		var params = this.eventParams;
		params.charCode = event.charCode;
		params.keyCode = event.keyCode;
		if(	this.status == this.statuses.enabled ||
			this.status == this.statuses.infocus )
		{
			Event.stop(event);
		} else {
			this.setStatus(this.status);
			this.element.fire("Control:keyup", params);
		}
	},
	//}
//}
//{ Observing
	_observe: function()
	{
	//{ Common events
		Event.observe(this.element, 'focus', this._onFocusDelegate.bind(this));
		Event.observe(this.element, 'blur', this._onBlurDelegate.bind(this));
		Event.observe(this.element, 'click', this._onClickDelegate.bind(this));
	//}
	//{ Mouse events
		Event.observe(this.element, 'mouseover', this._onMouseOverDelegate.bind(this));
		Event.observe(this.element, 'mousedown', this._onMouseDownDelegate.bind(this));
		Event.observe(this.element, 'mouseup', this._onMouseUpDelegate.bind(this));
		Event.observe(this.element, 'dbclick', this._onDbClickDelegate.bind(this));
		Event.observe(this.element, 'mouseout', this._onMouseOutDelegate.bind(this));
	//}
	//{Keyboard events
		Event.observe(this.element, 'keypress', this._onKeyPressDelegate.bind(this));
		Event.observe(this.element, 'keydown', this._onKeyDownDelegate.bind(this));
		Event.observe(this.element, 'keyup', this._onKeyUpDelegate.bind(this));
	//}
	},
//}

	/**
	 * Sets events params for the  control element
	 * @param params	Object 	Params for generated events
	**/
	setEventParams: function(params)
	{
		if(typeof(params) != "object"){
			throw "PageControlElement.setEventParams Incorect parameter passed";
		}

		for (var property in params){
			this.eventParams[property] = params[property];
		}
	}
});
CtPage.registerScript("PageControlElement");

/* JS BUTTON
**************************************************************************************************/
var JsButton = Class.create(PageControlElement,
{
	initialize: function($super, element)
	{
		this._defaultStatus = this.statuses.wait;
		$super(element);
	}
});
CtPage.registerScript("JsButton");

/* <A> JS BUTTON
**************************************************************************************************/
var AJsButton = Class.create(JsButton,
{
	initialize: function($super, element)
	{
		$super(element);
		this.requestParams = eval(element.rel);
	},


	_onClickDelegate: function(event)
	{
		Event.stop(event);
		if( !(this.status == this.statuses.disabled ||
			this.status == this.statuses.wait ||
			this.status == this.statuses.hidden ||
			this.status == this.statuses.active) )
		{
			this.element.fire("Control:click", this.eventParams);
		}
	}
});
CtPage.registerScript("AJsButton");

/* PAGE CONTROL TYPE
***************************************************************************************************/
/**
 * Used for control element identification
**/
var PageControlType = Class.create({
	initialize: function(tagName, cssIdentifier, appClassName)
	{
		if(tagName != undefined){
			this.tagName = tagName;
		}

		if(cssIdentifier != undefined){
			this.cssIdentifier = cssIdentifier;
		}

		if(appClassName != undefined){
			this.appClassName = appClassName;
		}

		var throwNotDefinedError = function(caption){
			throw '"' + caption + '" : is not defined';
		};

		if(typeof(this.tagName) == "undefined")
			throwNotDefinedError('tagName');
		if(typeof(this.cssIdentifier) == "undefined")
			throwNotDefinedError('cssIdentifier');
		if(typeof(this.appClassName) == "undefined")
			throwNotDefinedError('appClassName');
	}
});
CtPage.registerScript("PageControlType");
/* EXAMPLE FOR CREATING NEW TYPE
var someType = new (Class.create(PageControlType, {cssIdentifier: "some class name", appClassName: "some control class name"}));
*/

/* CONTROLS MANAGER
***************************************************************************************************/
var PageControlsManager = Class.create(
{
	initialize: function(page)
	{
		this.types = new Array(
			new (Class.create(PageControlType, {tagName: "a", cssIdentifier: "button", appClassName: "AJsButton"})),
			new (Class.create(PageControlType, {tagName: "a", cssIdentifier: "linkbutton", appClassName: "PageControlElement"})),
			new (Class.create(PageControlType, {tagName: "button", cssIdentifier: "button", appClassName: "JsButton"})));
		this.controls = new Hash();
	},


	/**
	 * Register all typified page control elements
	 * @param array additionalTypes
	**/
	registerAllPageControls: function()
	{
		var elements, controls = new Array(), control, obj;
		for(var i=0; i<this.types.length; i++){
			elements = $$(this.types[i].cssIdentifier);
			obj = eval(this.types[i].appClassName);
			for(var j=0; j<elements.length; j++){
				control = this.controls.get(elements[i]);
				if(control == undefined){
					control = new obj(elements[j]);
					this.controls.set(elements[i].id, control);
				}
				controls.push(control);
			}
		}

		return controls;
	},


	/**
	 * Sets status for the control
	 * @param control 	PageControlElement object | array of PageControlElement objects
	 * @param status		Control status
	**/
	setStatus: function(control, status)
	{
		var setControlStatus = function(control){
			if(typeof control.PageControlElement == "undefined"){
				throw "PCM[sS]: Control is undefined" + Object.inspect(control);
			} else {
				control.setStatus(status);
			}
		}.bind(this);

		if(Object.isArray(control)){
			for(var i=0; i<control.length; i++){
				setControlStatus(control[i]);
			}
		} else {
			setControlStatus(control);
		}
	},


	/**
	 * Gets control by element identifier or element
	 * @param ident		Element OR element id
	 * @return PageControlElement object
	**/
	getControl: function(ident)
	{
		var control;
		if(Object.isString(ident)){
			control = this.controls.get(ident);
			if(control == undefined){
				var element = $(ident);
				if(element == undefined){
					throw "PCM[gC]: Element is undefined " + ident;
				} else {
					var obj = eval(this._getControlClassName(element));
					var control = new obj(element);
					this.controls.set(element.id, control);
				}
			}
		} else if(Object.isElement(ident)){
			control = this.controls.get(ident.id);
			if(control == undefined){
				var obj = eval(this._getControlClassName(ident));
				var control = new obj(ident);
				this.controls.set(ident.id, control);
			}
		}
		return control;
	},


	/**
	 * Gets controls by elements identifiers or elements
	 * @param elements		Array of elements OR array of elements' ids
	 * @return Array	Array of PageControlElement objects
	**/
	getControlsByElements: function(elements)
	{
		// if(!Object.isArray(elements)){
			// throw "PCM[gCBE]: Invalid parameters passed";
		// }
		var controls = new Array(), obj;
		for(var key in elements){
			if(Object.isString(elements[key])){
				control = this.controls.get(elements[key]);
				if(control == undefined){
					var element = $(elements[key]);
					if(element == undefined){
						throw "PCM[gCBE]: Element is undefined " + ident;
					} else {
					obj = eval(this._getControlClassName(element));
					var control = new obj(element);
					controls.push(control);
					this.controls.set(element.id, control);
					}
				}
			} else if(Object.isElement(elements[key])){
				control = this.controls.get(elements[key].id);
				if(control == undefined){
					obj = eval(this._getControlClassName(elements[key]));
					var control = new obj(elements[key]);
					controls.push(control);
					this.controls.set(elements[key].id, control);
				}
			}
		// else {
				// throw "PCM[gCBE]: Invalid parameters passed";
			// }
		}
		return controls;
	},


	/**
	 * Gets all PageControlElement objects which will be founded in container
	 * @param container		Element of controls container
	 * @return Array 		Array of PageControlElement objects
	**/
	getControlsByContainer: function(container)
	{
		if(!Object.isElement(container)){
			throw "PCM[gCBC]: Invalid parameters passed";
		}

		var elements, controls = new Array(), control, obj;
		var containerCssIdent = container.tagName +"#"+ container.id;
		for(var i=0; i<this.types.length; i++){
			elements = $$(containerCssIdent +" "+ this.types[i].tagName +"."+ this.types[i].cssIdentifier);
			for(var j=0; j<elements.length; j++){
				obj = eval(this.types[i].appClassName);
				control = new obj(elements[j]);
				controls.push(control);
				this.controls.set(elements[i].id, control);
			}
		}

		return controls;
	},


	/**
	 * Destroys page controls
	 * @param controls 	PageControlElement objects
	**/
	destroyControls: function(controls)
	{
		var destroyControl = function(control){
			var id;
			if (Object.isString(control)) {
				id = control;
			} else {
				id = control.element.id;
			}
			var control = this.controls.get(id);
			control = null;
			this.controls.unset(id);
		}.bind(this);

		if(Object.isArray(controls)){
			for(var i=0; i<controls.length; i++){
				destroyControl(controls[i]);
			}
		} else {
			destroyControl(controls);
		}

	// Run garbarge collector on IE
		if (window.CollectGarbage){
			window.CollectGarbage();
		}
	},


	/**
	 * Get control class name
	 * @element	Element 	Control element
	**/
	_getControlClassName: function(element)
	{
		for(var i=0; i<this.types.length; i++){
			if(element.tagName.toLowerCase() == this.types[i].tagName && element.hasClassName(this.types[i].cssIdentifier)){
				return this.types[i].appClassName;
			}
		}
		throw "Control type for the element '"+ element +"' not found";
	}
});

var pageControlsManager = new PageControlsManager(document.body);
CtPage.registerScript("PageControlsManager");
