/**
 * @copyright WebKey
 */

Function.prototype.bind=function(_1)
{
	var _2=this;
	return function()
	{
		return _2.apply(_1,arguments);
	};
};


var EventDispatcher = function()
{
	this.__events = {};
};

EventDispatcher.prototype = {
	__event: function(Type)
	{
		var eventID = 1;

		var o = {
			id: eventID,
			name: Type,
			listeners: []
		};
		return o;
	},

	__dispatchQueue: function(data)
	{
		 var name = data.type;

		 var event = this.__events[name];
		 if(!event) return;

		 for(var i=0; i < event.listeners.length; i++)
		 {
		 	var proc = event.listeners[i][name];
		 	proc(data);

		 }
	},

	__removeListener: function(type, listener)
	{
		var event = this.__events[type];
		if(event)
		{
			for (var i in event.listeners)
			{
				if(event.listeners[i] == listener)
				{
					event.listeners.splice(i, 1);
				}
			}
		}
	},

	__addEventType: function(type)
	{
		if (!this.__events[type]) this.__events[type] = this.__event(type);
	},

	__checkListener: function(event, obj)
	{
		var listeners = this.__events[event].listeners;
		for(var i=0; i < listeners.length; i++)
		{
			if(listeners[i] == obj) return true;
		}
		return false;
	},

	dispatch: function(data)
	{
		this.__dispatchQueue(data);
	},

	addListener: function(event,obj)
	{
		this.__addEventType(event);
		if(!this.__checkListener(event, obj)) this.__events[event].listeners.push(obj);
	},

	removeListener: function(type, listener)
	{
		this.__removeListener(type, listener);
	}
};

var TagSystem = function()
{
	var TagList = function()
	{
		var __Items = [];

		var __TagList = document.getElementById('TagList');

		var __TagObj = function()
		{
			var _Name = null;
			var _Value = null;
			var _DomReference = null;
			var _Selected = false;

			this.SetDomReference = function(Value)
			{
				Value.onclick = function()
				{
					this.dispatch(
					{
						type: 'onclick',
						Name: _Name,
						Value: _Value
					});
				}.bind(this);

				Value.onmouseover = function()
				{
					this.ToggleSelected();
				}.bind(this);

				Value.onmouseout = function()
				{
					this.ToggleSelected();
				}.bind(this);

				Value.className = 'tag';

				_DomReference = Value;

				Value = null;
			}

			this.GetDomReference = function()
			{
				return _DomReference;
			}

			this.SetName = function(Value)
			{
				_DomReference.name = Value;
				_Name = Value;
			}

			this.GetName = function()
			{
				return _Name;
			}

			this.SetValue = function(Value)
			{
				_DomReference.innerHTML = Value;
				_Value = Value;
			}

			this.GetValue = function()
			{
				return _Value;
			}

			this.SetSelected = function(Value)
			{
				_Selected = Value;
			}

			this.GetSelected = function()
			{
				return _Selected;
			}

			this.ToggleSelected = function()
			{
				if(_Selected == true) this.SetSelected(false);
				else this.SetSelected(true);
			}
		}
		__TagObj.prototype = new EventDispatcher;

		this.AddTag = function(Name, Value)
		{
			var SPAN = document.createElement('span');

			var TagObj = new __TagObj;
			TagObj.SetDomReference(SPAN);
			TagObj.SetName(Name);
			TagObj.SetValue(Value);

			var e = {};
			e.onclick = function(e)
			{
				if(e.Name === TagObj.GetName())
				{
					this.dispatch({
						type: 'item_onclick',
						Item: TagObj
					});
				}
			}.bind(this);

			SPAN = null;

			TagObj.__events = {};
			TagObj.addListener('onclick', e);
			TagObj.addListener('onmouseover', e);

			__Items.push(TagObj);
		}

		this.Render = function()
		{
			for(var i = 0; i < __Items.length; i++)
			{
				var COMMA = document.createElement('span');
				COMMA.innerHTML = ', ';

				var Item = __Items[i];
				__TagList.appendChild(Item.GetDomReference());
				if(i != __Items.length - 1) __TagList.appendChild(COMMA);

				COMMA = null;
			}
		}

		this.Clear = function()
		{
			this.DESTROY();
			__Items = [];
			__TagList.innerHTML = '';
		}

		this.DESTROY = function()
		{
			for(var i=0; i < __Items.length; i++)
			{
				__Items[i].DomReference = null;
			}
			__Items = null;
		}
	}
	TagList.prototype = new EventDispatcher;

	var TagBox = function()
	{
		var TagBox = document.getElementById('tag_string');

		var Clicked = false;

		TagBox.onclick = function()
		{
			if(Clicked === false)
			{
				this.Clear();
				Clicked = true;
			}
		}.bind(this);

		this.__Value;
		var __OldValue;

		this.ResetValue = function()
		{
			__OldValue = TagBox.value;
			this.__Value = TagBox.value.replace(/([^a-zA-Z0-9-_ ])/g, '\$0');
		}

		this.DomReference = function()
		{
			return TagBox;
		}

		this.SetValue = function(Value)
		{
			this.__Value = Value;
			TagBox.value = Value;
		}

		this.SetOldValue = function()
		{
			TagBox.value = __OldValue;
			this.__Value = __OldValue;
		}

		this.AutoSetValue = function()
		{
			__OldValue = TagBox.value;
			this.__Value = TagBox.value;
			this.dispatch({type: 'keyup', value: this.__Value});
		}

		this.Clear = function()
		{
			TagBox.value = '';
			this.__Value = '';
		}

		this.GetValue = function()
		{
			return TagBox.value;
		}

		this.Focus = function()
		{
			TagBox.focus();
		}
	}
	TagBox.prototype = new EventDispatcher;


	var AutoCompleteBox = function()
	{
		var __Items = [];
		var DomReference = document.getElementById('TagAutoCompleteBox');

		DomReference.onmouseover = function()
		{
			this.dispatch({
				type: 'mouseover'
			});
		}.bind(this);

		var __ItemObj = function()
		{
			var _DomReference = null;
			var _Name = null;
			var _Value = null;
			var _Selected = false;

			this.SetDomReference = function(Value)
			{
				Value.onclick = function()
				{
					this.dispatch({
						type: 'onclick',
						Name: this.GetName(),
						Value: this.GetValue(),
						Selected: this.GetSelected()
					});
				}.bind(this);

				Value.onmouseover = function()
				{
					this.dispatch({
						type: 'onmouseover',
						Item: this
					});

				}.bind(this);

				Value.onmouseout = function()
				{
					this.dispatch({
						type: 'onmouseout',
						Item: this
					});

				}.bind(this);

				_DomReference = Value;

				Value = null;
			}

			this.GetDomReference = function()
			{
				return _DomReference;
			}

			this.SetName = function(Value)
			{
				_DomReference.name = Value;
				_Name = Value;
			}

			this.GetName = function()
			{
				return _Name;
			}

			this.SetValue = function(Value)
			{
				_Value = Value;
				_DomReference.innerHTML = Value;
			}

			this.GetValue = function()
			{
				return _Value;
			}

			this.SetSelected = function(Value)
			{
				_Selected = Value;

				switch(_Selected)
				{
					case true:
						_DomReference.className = 'selected';
					break;

					case false:
						_DomReference.className = 'unselected';
					break;
				}
			}

			this.GetSelected = function()
			{
				return _Selected;
			}

			this.ToggleSelected = function()
			{
				if(_Selected == true) this.SetSelected(false);
				else this.SetSelected(true);
			}
		}
		__ItemObj.prototype = new EventDispatcher;

		this.SelectItemByName = function(Name)
		{
			var Item;

			this.DeSelectItemByName(Name);

			for(var i=0; i < __Items.length; i++)
			{
				Item = __Items[i];
				if(Item.GetName() == Name) Item.SetSelected(true);
			}
		}

		this.DeSelectItemByName = function(Name)
		{
			var Item;

			for(var i=0; i < __Items.length; i++)
			{
				Item = __Items[i];
				if(Item.GetName() == Name) Item.SetSelected(false);
			}
		}

		this.IsItemSelected = function()
		{
			var Item;

			for(var i=0; i < __Items.length; i++)
			{
				Item = __Items[i];
				if(Item.GetSelected() === true) return true;
			}
			return false;
		}

		this.GetValueByName = function(Name)
		{
			var Item;

			for(var i=0; i < __Items.length; i++)
			{
				Item = __Items[i];
				if(Item.GetName() == Name) return Item.GetValue();
			}
		}

		this.GetItemByName = function(Name)
		{
			for(var i=0; i < __Items.length; i++)
			{
				Item = __Items[i];
				if(Item.GetName() == Name) return __Items[i];
			}
		}

		this.GetSelectedItem = function()
		{
			for(var i=0; i < __Items.length; i++)
			{
				if(__Items[i].GetSelected() === true) return __Items[i];
			}
		}

		this.GetSelectedIndex = function()
		{
			for(var i=0; i < __Items.length; i++)
			{
				if(__Items[i].GetSelected() == true) return i;
			}
			return false;
		}

		this.SelectPreviousItem = function()
		{
			var Index = this.GetSelectedIndex();

			var Item = __Items[Index];
			var PreviousItem = __Items[Index-1];

			if(!PreviousItem)
			{
				Item.SetSelected(false);
				this.dispatch({type: 'unselect'});
				return;
			}

			this.dispatch({type: 'select', item: PreviousItem});
			Item.SetSelected(false);
			PreviousItem.SetSelected(true);

			return true;
		}

		this.SelectNextItem = function()
		{
			var Index = this.GetSelectedIndex();

			if(Index === false)
			{
				Index = 0;
				NextItemIndex = 0;
			}
			else
			{
				if(__Items.length != 1) NextItemIndex = Index + 1;
				else NewItemIndex = 0;
			}

			if(Index >= __Items.length - 1 && Index != 0) return false;

			var Item = __Items[Index];
			var NextItem = __Items[NextItemIndex];

			if(!NextItem) return;

			Item.SetSelected(false);
			NextItem.SetSelected(true);

			this.dispatch({type: 'select', item: NextItem});

			return true;
		}

		this.AddItem = function(Name, Value)
		{
			var LI = document.createElement('li');

			var Item = new __ItemObj;
			Item.SetDomReference(LI);
			Item.SetName(Name);
			Item.SetValue(Value);

			var e = {};
			e.onclick = function()
			{
				if(Item.GetSelected() === true)
				{
					this.dispatch({
						type: 'item_onclick',
						Item: Item
					});
				}
			}.bind(this);

			e.onmouseover = function(e)
			{
				if(this.IsItemSelected())
				{
					var Item = this.GetSelectedItem();
					Item.SetSelected(false);
				}
				e.Item.ToggleSelected();
			}.bind(this);

			e.onmouseout = function(e)
			{
				if(this.IsItemSelected())
				{
					var Item = this.GetSelectedItem();
					Item.SetSelected(false);
				}
				e.Item.ToggleSelected();
			}.bind(this);

			Item.__events = {};
			Item.addListener('onclick', e);
			Item.addListener('onmouseover', e);
			Item.addListener('onmouseout', e);

			LI = null;


			__Items.push(Item);
		}

		this.Clear = function()
		{
			this.DESTROY();
			DomReference.innerHTML = '';
			__Items = [];
		}

		this.Render = function()
		{
			var UL = document.createElement('ul');

			for(var i=0; i < __Items.length; i++)
			{
				UL.appendChild(__Items[i].GetDomReference());
			}

			DomReference.appendChild(UL);
		}

		this.DESTROY = function()
		{
			for(var i=0; i < __Items.length; i++)
			{
				__Items[i].DomReference = null;
			}
			__Items = null;
		}
	}
	AutoCompleteBox.prototype = new EventDispatcher;

	var XHRController = function()
	{
		this.Time = null;

		this.__GetTags_Done = function(Data)
		{
			this.dispatch({type: 'GetTags_Done', transferData: Data});
		}

		this.__SearchTags_Done = function(Data)
		{
			this.dispatch({type: 'SearchTags_Done', transferData: Data});
		}

		this.__AddTag_Done = function(Data)
		{
			this.dispatch({type: 'AddTag_Done', transferData: Data});
		}

		this.__RemoveTag_Done = function(Data)
		{
			this.dispatch({type: 'RemoveTag_Done', transferData: Data});
		}

		this.GetTags = function()
		{
			var Rand = Math.floor(Math.random()*100000);
			$.getJSON("http://" + window.location.hostname + "/tag.php?type=tag_get&key=null&" + location.search.substring(1) + "&_r=" + Rand, this.__GetTags_Done.bind(this));
		}

		this.SearchTags = function(Value)
		{
			var Rand = Math.floor(Math.random()*100000);
			$.getJSON("http://" + window.location.hostname + "/tag.php?type=tag_search&key=" + Value + "&" + location.search.substring(1) + "&_r=" + Rand, this.__SearchTags_Done.bind(this));
		}

		this.RemoveTag = function(TagName)
		{
			var Rand = Math.floor(Math.random()*100000);
			$.getJSON("http://" + window.location.hostname + "/tag.php?type=tag_remove&key=" + TagName + "&"+ location.search.substring(1) + "&_r=" + Rand, this.__RemoveTag_Done.bind(this));
		}

		this.AddTag = function(TagName)
		{
			if(!TagName) return;
			var Rand = Math.floor(Math.random()*100000);
			$.getJSON("http://" + window.location.hostname + "/tag.php?type=tag_add&key=" + TagName + "&" + location.search.substring(1) + "&_r=" + Rand, this.__AddTag_Done.bind(this));
		}
	}
	XHRController.prototype = new EventDispatcher;

	var Timeout;

	this.KeyFactory = function(event)
	{
		var Key = (window.event) ? window.event.keyCode : event.which;

		switch(Key)
		{
			// Enter
			case 13:
				if(Timeout) clearTimeout(Timeout);
				__XHRController.AddTag(__TagBox.GetValue());
				__TagBox.Clear();
				__AutoCompleteBox.Clear();
			break;

			// Down Arrow
			case 40:
				__AutoCompleteBox.SelectNextItem();
			break;

			// Up Arrow
			case 38:
				__AutoCompleteBox.SelectPreviousItem();
			break;

			// Alphanumeric, delete, hyphen, underscore, and space
			case (((Key >= 65 && Key <= 90) || (Key >= 48 && Key <= 57) || Key === 8 || Key === 32 || Key === 109) ? Key : !Key):
				__TagBox.AutoSetValue();
			break;

			// Escape Key
			case 27:
				__TagBox.SetOldValue();
				__AutoCompleteBox.Clear();
			break;

			default:

			break;
		}
	}

	this.__OnKeyUp = function(eData)
	{
		var Value = eData.value;

		if(Value == '')
		{
			clearTimeout(Timeout);
			__AutoCompleteBox.Clear();
			return;
		}

		if(Timeout) clearTimeout(Timeout);

		Timeout = setTimeout(
		function()
		{
			__XHRController.SearchTags(Value)
		}
		, 300);
	}

	this.__SearchTagsDone = function(Data)
	{
		var Tags = Data.transferData.Tags;

		if(!Tags) return;

		var searchText = Data.transferData.SearchText;

		__AutoCompleteBox.Clear();

		var RegEx = new RegExp(""+ searchText + "", "i");


		for(var i = 0; i < Tags.length; i++)
		{
			var TagName = Tags[i].toLowerCase();
			var NewRegEx = RegEx.exec(TagName);

			if(NewRegEx !== null)
			{
				var Str = NewRegEx[0];
				var ReplacedString = TagName.replace(Str, "<b>" + searchText.toLowerCase() + "</b>");
				__AutoCompleteBox.AddItem(TagName, ReplacedString);
			}
		}

		__AutoCompleteBox.Render();
	}

	this.__unselect_autocompletebox = function(eData)
	{
		__TagBox.SetOldValue();
	}

	this.__select_autocompletebox = function(eData)
	{
		var Item = eData.item;

		var Name = Item.GetName();
		var Value = Item.GetValue();

		__TagBox.SetValue(Name);

		Item.DomReference = null;
	}

	this.__item_onclick_autocompletebox = function(eData)
	{
		var Value = eData.Item.GetValue();
		var Name = eData.Item.GetName();

		__XHRController.AddTag(Name);
		__TagBox.Clear();
		__AutoCompleteBox.Clear();

		eData.Item.DomReference = null;
	}

	this.__GetTagsDone = function(eData)
	{
		var transferData = eData.transferData;
		if(!transferData) return;

		__TagList.Clear();
		for(var i=0; i < transferData.length; i++)
		{
			var Data = transferData[i];

			__TagList.AddTag(Data.ID, Data.Value);
		}
		__TagList.Render();
	}

	this.__AddTagDone = function(eData)
	{
		__XHRController.GetTags();
	}

	this.__item_onclick_taglist = function(eData)
	{
		var Name = eData.Item.GetName();

		__XHRController.RemoveTag(Name);
		eData.Item.DomReference = null;
	}

	this.__RemoveTagDone = function()
	{
		__TagList.Clear();
		__XHRController.GetTags();
	}

	this.__mouseover_autocompletebox = function()
	{
		__TagBox.Focus();
	}


	var __TagBox = new TagBox;
	var __TagList = new TagList;
	var __AutoCompleteBox = new AutoCompleteBox;
	var __XHRController = new XHRController;


	var e = {};
	e.keyup = this.__OnKeyUp.bind(this);
	__TagBox.addListener('keyup', e);


	var e = {};
	e.SearchTags_Done = this.__SearchTagsDone.bind(this);
	e.GetTags_Done = this.__GetTagsDone.bind(this);
	e.AddTag_Done = this.__AddTagDone.bind(this);
	e.RemoveTag_Done = this.__RemoveTagDone.bind(this);

	__XHRController.addListener('SearchTags_Done', e);
	__XHRController.addListener('GetTags_Done', e);
	__XHRController.addListener('AddTag_Done', e);
	__XHRController.addListener('RemoveTag_Done', e);

	var e = {};
	e.unselect = this.__unselect_autocompletebox.bind(this);
	e.select = this.__select_autocompletebox.bind(this);
	e.item_onclick = this.__item_onclick_autocompletebox.bind(this);
	e.mouseover = this.__mouseover_autocompletebox.bind(this);
	__AutoCompleteBox.addListener('unselect', e);
	__AutoCompleteBox.addListener('select', e);
	__AutoCompleteBox.addListener('item_onclick', e);
	__AutoCompleteBox.addListener('mouseover', e);

	var e = {};
	e.item_onclick = this.__item_onclick_taglist.bind(this);
	__TagList.addListener('item_onclick', e);

	__XHRController.GetTags();

	var TagBoxDOMNode = __TagBox.DomReference();
	TagBoxDOMNode.onkeyup = this.KeyFactory.bind(this);

	window.unload = function()
	{
		__AutoCompleteBox.DESTROY();
		__TagList.DESTROY();
	}
}

