(function($, window, undefined) {

function _cssSelector(name) {
	if (name)
		return '.' + this.widgetName + '-' + name;
	else
		return '.' + this.widgetName;
}

function _classSelect(name, me) {
	if (name)
		var selector =  '.' + this.widgetName + '-' + name;
	else
		var selector = '.' +  this.widgetName;
	return $(selector, me ? me : this.me);
}

var slugRegex = /([^a-zA-z1-9]|\^|\\)+/g;

function slugSanitize(v) {
	var vv = v.replace(slugRegex, '-');
	if (vv.substr(0,1) == '-')
		vv = vv.substr(1);
	return vv;
}

function onSlugKeyup(e) {
	var s = $(this);
	var res = slugSanitize(s.val());
	s.val(res);
	$(this).data().autoSlug = res == '';
}

function onSlugChange(e) {
	var s = $(this);
	if(s.val() == '') {
		s.val(slugSanitize(s.parent(this.__('entry')).data().name.val()));
	}
}

function onNameKeyup(e) {
	var p = $(this).data().parent;
	var s = p.data().slug;
	var n = $(this);
	
	if (s.data().autoSlug) {
		var res = slugSanitize(n.val());
		s.val(res);
	}
}

function slugSanitize(v) {
	var vv = v.replace(slugRegex, '-');
	if (vv.substr(0,1) == '-')
		vv = vv.substr(1);
	return vv;
}
	
var tagManager = function() {
	var template = '\
		<div class="${widgetName} ui-widget">\
			<div class="${widgetName}-entry">\
				<span class="${widgetName}-icon"></span>\
				<input type="text" class="${widgetName}-name" disabled="disabled">\
				<input type="text" class="${widgetName}-slug" disabled="disabled">\
				<a class="${widgetName}-command ${widgetName}-update"></a>\
				<a class="${widgetName}-command ${widgetName}-edit"></a>\
				<a class="${widgetName}-command ${widgetName}-delete"></a>\
				<a class="${widgetName}-command ${widgetName}-cancel"></a>\
				<img class="${widgetName}-spinner" width="18" height="18" src="/NMGfiles/img/5.gif">\
			</div>\
			<div class="confirmation-dialog">\
				<span class="ui-icon ui-icon-alert" style="float:left; margin:0 7px 20px 0;"></span><span class="dialog-message"></span>\
			</div>\
			<div class="validation-dialog">\
				<span class="ui-icon ui-icon-alert" style="float:left; margin:0 7px 20px 0;"></span><span class="dialog-message"></span>\
			</div>\
			<ul class="${widgetName}-list">\
			</ul>\
			<div class="${widgetName}-entry ${widgetName}-new-entry">\
				<span class="${widgetName}-icon"></span>\
				<span style="display:inline-block;width:80px;">Name:</span>\
				<input type="text" class="${widgetName}-name" placeholder="tag name">\
				<br><span style="display:inline-block;width:80px;">Description:</span>\
				<input type="text" class="${widgetName}-desc" placeholder="tag description">\
				<input type="text" class="${widgetName}-slug" placeholder="tag slug" style="display:none;">\
				<a class="${widgetName}-command ${widgetName}-save-new"></a>\
				<a class="${widgetName}-command ${widgetName}-cancel-new"></a>\
				<img class="${widgetName}-spinner" width="18" height="18" src="/NMGfiles/img/5.gif">\
			</div>\
		</div>\
	';
	var widgetName = 'tag-manager';
	var templateData = {
		widgetName: widgetName
	};
	var element = $.tmpl(template, templateData);
	
	return {
		widgetName: widgetName,
		data: [],
		postUrl: '/admin/tag',
		baseAjaxData: function(obj) {
			if (this.mode !== undefined) {
				obj.mode = this.mode;
			}
			return obj;
		},
		_: function(selector, ctx) {
			return _classSelect.call(this, selector, ctx);
		},
		__: function(selector) {
			return _cssSelector.call(this, selector);
		},
		init: function() {
			var me = element.clone();
			this.me = me;
			
			// set up event listeners for tag name/slug autocomplete validation
			$(this.__('slug')).live('keyup', onSlugKeyup);
			$(this.__('slug')).live('change', onSlugChange);
			$(this.__('name')).live('keyup', onNameKeyup);
			
			// template for rendering data entries
			var tpl = this._('entry:first').detach();
			this.tpl = tpl;
			
			// new entry controls
			var newEntry = this._('new-entry');
			newEntry.data('entry', {tag:'', slug:''});
			newEntry.data().icon = this._('icon', newEntry);
			newEntry.data().name = this._('name', newEntry).data('parent', newEntry);
			newEntry.data().slug = this._('slug', newEntry).data('parent', newEntry);
			newEntry.data().desc = this._('desc', newEntry).data('parent', newEntry);
			newEntry.data().update = this._('save-new', newEntry).button({text:false, icons:{primary:'ui-icon-disk'}}).click($.proxy(function(){this.onCmdSaveOrUpdate(newEntry)}, this));
			newEntry.data().cancel = this._('cancel-new', newEntry).button({text:false, icons:{primary:'ui-icon-arrowreturnthick-1-w'}}).click($.proxy(this.onCmdCancelNew, this));
			newEntry.data().spinner = this._('spinner', newEntry).hide();
			this.newEntry = newEntry;
			
			// the actual html ul for listing tags
			var list = this._('list');
			this.list = list;
			
			// delete confirmation dialog
			this.deleteDialog = $('.confirmation-dialog', me).detach().dialog({
				resizable: false,
				autoOpen: false,
				modal: true,
				title: 'Delete this item?',
				buttons: {
					'Delete': function() {
						$(this).dialog('option', 'onDelete').call();
						$(this).dialog('close');
					},
					'Cancel': function() {
						$(this).dialog('close');
					}
				}	
			});
			
			// validation error dialog
			this.validationDialog = $('.validation-dialog', me).detach().dialog({
				resizable: false,
				autoOpen: false,
				modal: true,
				buttons: {
					'Cancel': function() {
						$(this).dialog('close');
					}
				},
				close: function() {
					var callback = $(this).dialog('option', 'onClose');
					callback && callback();
				}	
			});
			
			this.refresh();
			this.clearNewEntry();
		},
		
		refresh: function() {
			$.post(this.postUrl, this.baseAjaxData({action:'list'}), $.proxy(function(d){
				this.data = JSON.parse(d);
				this.list.empty();
				$.each(this.data, $.proxy(function(i,v) {
					var e = this.newTemplate(v);
					$('<li/>').append(e).appendTo(this.list);
				}, this));
			}, this));
		},
		
		sort: function(){
			var list = this.list;
			var arr = list.children().toArray().sort(function(a, b) {
				var na = a.data().entry.tagName, nb = b.data().entry.tagName;
				if(na < nb) return -1;
				if(na > nb) return 1;
				return 0;
			});
			$.each(arr, function(i,v){
				list.append(v);
			});
		},
		
		clearNewEntry: function() {
			this.newEntry.data().name.val('');
			this.newEntry.data().slug.val('').data('autoSlug', true);
			delete this.newEntry.data().trID;
		},
		
		newTemplate: function(d) {
			var e = this.tpl.clone();
			e.data().icon = this._('icon', e);
			e.data().name = this._('name', e).val(d.tagName).data('parent', e);
			e.data().slug = this._('slug', e).val(d.tagSlug).data('parent', e);
			e.data().update = this._('update', e).button({text:false, icons:{primary:'ui-icon-disk'}}).hide().click($.proxy(function(){this.onCmdUpdate(e);}, this));
			e.data().edit = this._('edit', e).button({text:false, icons:{primary:'ui-icon-pencil'}}).click($.proxy(function(){this.onCmdEdit(e);}, this));
			e.data().del = this._('delete', e).button({text:false, icons:{primary:'ui-icon-trash'}}).click($.proxy(function(){this.onCmdDelete(e);}, this));
			e.data().cancel = this._('cancel', e).button({text:false, icons:{primary:'ui-icon-arrowreturnthick-1-w'}}).hide().click($.proxy(function(){this.onCmdCancel(e);}, this));
			e.data().spinner = this._('spinner', e).hide();
			e.data('entry', d);
			return e;
		},
		
		onCmdEdit: function(p) {
			p.data().update.show().startPulsate();
			p.data().cancel.show().startPulsate();
			p.data().edit.hide();
			p.data().del.hide();
			p.data().name.attr('disabled', null);
			p.data().slug.attr('disabled', null);
			p.data().name.focus();
		},
		
		onCmdUpdate: function(p) {
			var tagID = p.data().entry.tagID;
			var tagName = p.data().name.val();
			var tagSlug = p.data().slug.val();
			var tagDesc = p.data().desc.val();
			
			if (!this.validate(p)) {
				return;
			}
			
			p.data().update.stopPulsate().hide();
			p.data().cancel.stopPulsate().hide();
			p.data().edit.show();
			p.data().del.show();
			p.data().name.attr('disabled', 'disabled');
			p.data().slug.attr('disabled', 'disabled');
			p.data().spinner.show();
			$.post(this.postUrl, this.baseAjaxData({action:'update', tagId:p.data().entry.tagID, tagName:tagName, tagSlug:tagSlug, tagDesc:tagDesc}), function(){
				p.data().spinner.hide();
				p.data().entry.tagName = p.data().name.val();
				p.data().entry.tagSlug = p.data().slug.val();
			});
		},
		
		onCmdSaveNew: function() {
			var p = this.newEntry;
			var tagName = p.data().name.val();
			var tagSlug = p.data().slug.val();
			
			if (!this.validate(p)) {
				return;
			}
			
			p.data().spinner.show();
			var self = this;
			
			$.post(this.postUrl, this.baseAjaxData({action:'create', tagName:p.data().name.val(), tagSlug:p.data().slug.val()}), function(d){
				var newTag = JSON.parse(d);
				p.data().spinner.hide();
				self.refresh();
				self.newEntry.trigger('tagcreated', {newTag:newTag});
			});
			this.clearNewEntry();
		},
		
		onCmdSaveOrUpdate: function(p) {
			var tagName = p.data().name.val();
			var tagSlug = p.data().slug.val();
			var tagDesc = p.data().desc.val();
			
			//console.log(tagName, tagSlug, tagDesc);
			
			/* no validation needed
			if (!this.validate(p)) {
				return;
			}
			*/
			
			p.data().spinner.show();
			var self = this;
			
			
			var dd = this.baseAjaxData({action:'saveOrUpdate', id:p.data().rid, tagName:p.data().name.val(), tagSlug:p.data().slug.val(), tagDesc:p.data().desc.val()});
			var trID = p.data().trID;
			console.log('trID', trID);
			if (trID !== undefined) {
				dd.trID = trID;
			}
			
			$.post(this.postUrl, dd, function(d){
				//var newTag = JSON.parse(d);
				p.data().spinner.hide();
				//self.refresh();
				self.newEntry.trigger('tagcreated');
				//console.log('tttagcreated');
			});
			this.clearNewEntry();
		},
		
		onCmdCancelNew: function() {
			this.clearNewEntry();
		},
		
		onCmdDelete: function(p) {
			var self = this;
			this.showDeleteDialog('Delete this item?', "Are you sure you want to delete '" + p.data().entry.tagName + "'?",
					function() {
						p.data().spinner.show();
						$.post(self.postUrl, self.baseAjaxData({action:'delete', tagId:p.data().entry.tagID}), function(){
							p.data().spinner.hide();
							self.refresh();
						});
			});
		},
		
		onCmdCancel: function(p) {
			p.data().update.stopPulsate().hide();
			p.data().cancel.stopPulsate().hide();
			p.data().edit.show();
			p.data().del.show();
			p.data().name.val(p.data().entry.tagName);
			p.data().name.attr('disabled', 'disabled');
			p.data().slug.val(p.data().entry.tagSlug);
			p.data().slug.attr('disabled', 'disabled');
		},
		
		showDeleteDialog: function(title, message, callback) {
			this.deleteDialog.dialog('option', 'title', title);
			this.deleteDialog.find('.dialog-message').text(message);
			this.deleteDialog.dialog('option', 'onDelete', callback);
			this.deleteDialog.dialog('open');
			
		},
		
		showValidationDialog: function(title, message, callback) {
			this.validationDialog.find('.dialog-message').text(message);
			this.validationDialog.dialog('option', 'title', title);
			this.validationDialog.dialog('option', 'onClose', callback);
			this.validationDialog.dialog('open');
		}, 
		
		validate: function(p) {
			var d = p.data();
			var tagName = d.name.val();
			var tagSlug = d.slug.val();
			var tagID; d.entry && (tagID = d.entry.tagID);
			if (d.name.val() == '' || d.slug.val() == '') {
				if (d.name.val()=='') {
					var callback = function(){d.name.focus()};
				} else {
					var callback = function(){d.slug.focus()};
				}
				this.showValidationDialog('Cannot save tag', 'The tag fields cannot be left empty', callback);
				return false;
			}
			
			var failed = false;
			for (var i = 0 ; i < this.data.length ; i++) {
				var v = this.data[i];
				if (v.tagName == tagName) {
					if (tagID != v.tagID) {
						failed = true;
						var callback = function(){d.name.focus();};
						break;
					}
				} else if (v.tagSlug == tagSlug) {
					if (tagID != v.tagID) {
						failed = true;
						var callback = function(){d.slug.focus();};
						break;
					}
				}
			}
			
			if (failed) {
				this.showValidationDialog('Cannot save tag', 'There is already a tag by that name or slug (' + tagName + ', ' + tagSlug + ')', callback);
				return false;
			}
			
			return true;
		}
	};
	
}();

var tagger = function() {
	var template = '\
		<div class="${widgetName} ui-widget">\
			<span style="display:block;" class="${widgetName}-tag ui-widget ui-state-default ui-corner-all ui-button-text-only"><a class="${widgetName}-tagname">tag name</a><a class="${widgetName}-tagedit"></a><a class="${widgetName}-tagremove"></a></span>\
			<input type="text" class="${widgetName}-newtag" placeholder="add tag">\
		</div>\
	';
	var widgetName = 'tagger';
	var templateData = {
		widgetName: widgetName
	};
	var element = $.tmpl(template, templateData);
	
	return {
		widgetName: widgetName,
		data: [],
		postUrl: '/admin/tag',
		baseAjaxData: function(obj) {
			obj.mode = this.mode;
			return obj;
		},
		_: function(selector, ctx) {
			return _classSelect.call(this, selector, ctx);
		},
		__: function(selector) {
			return _cssSelector.call(this, selector);
		},
		init: function(conf){
			var self = this;
			
			this.mode = conf.mode;
			this.idSelectName = conf.idSelectName;
			
			this.idSelect = $('form *[name=' + this.idSelectName + ']');

			var me = element.clone();
			this.me = me;
			
			var tpl = this._('tag').detach();
			this.tpl = tpl;
			
			this.tagManager = NMGTags.newTagManager();
			
			this.newTagDialog = this.tagManager.newEntry.dialog({
				autoOpen: false,
				resizable: false,
				modal: true,
				title: 'New tag',
				width: '375',
				buttons: {
					'Close': function() {
						$(this).dialog('close');
					}
				}
			});
			
			this.tagManager.newEntry.data().rid = this.idSelect.val();
			this.tagManager.mode = this.mode;
			//this.tagManager.refreshList = $.proxy(this.refresh, this);
			
			me.find('.tagger-tagname, .ui-icon').button();
			
			this.newtag = this._('newtag');
			
			this.newtag.nmgSearchbox({cssClass:'tagger-taglist', filterFunction:$.proxy(this, 'tagSuggestionsFilter'), dataUrl:'/admin/tag', minLength:false, requestData:{action:'list'}, menuRenderer:'_renderTagMenu', suggestionSelected:function(v){
				var selTag = v;
				if (selTag.tagID == 'new') {
					selTag.tagName = self.newtag.val();
					selTag.tagSlug = slugSanitize(self.newtag.val());
					selTag.tagDesc = '';
				}
				//if (selTag == 'new') {
					self.openNewTagDialog(selTag, function(d) {
						if (d) {
							/*
							self.newtag.nmgSearchbox('invalidateSuggestions');
							self.appendTag(d);
							self.data.push(d);
							self.newtag.val('');
							$.post(self.postUrl, self.baseAjaxData({action:'add', id:self.idSelect.val(), tagId:d.tagID}), function(d){
								// stuff that needs to run after tag relationship has been saved
							});
							*/
							self.refresh();
						}
						self.newtag.focus();
					});
				//} else {
				//	self.appendTag(v);
				//	self.data.push(v);
				//	self.newtag.focus();
				//	$.post(self.postUrl, self.baseAjaxData({action:'add', id:self.idSelect.val(), tagId:v.tagID}), function(d){
						// stuff that needs to run after tag relationship has been saved
				//	});
				//}
			}});
			
			$(this.__('tagremove')).live('click', function(){
				var tag = $(this).parent();
				var e = tag.data().entry;
				for (var i = 0 ; i < self.data.length ; i++) {
					if (self.data[i].tagID == e.tagID) {
						self.data.splice(i, 1);
						tag.detach();
						//$.post(self.postUrl, self.baseAjaxData({action:'remove', id:self.idSelect.val(), tagId:e.tagID}));
						$.post(self.postUrl, self.baseAjaxData({action:'removeRelationship', trID:e.trID}));
						break;
					}
				}
			});
			
			$(this.__('tagedit')).live('click', function(){
				var tag = $(this).parent();
				var e = tag.data().entry;
				self.openNewTagDialog(e, function(t) {
					self.refresh();
				});
			});
			
			this.refresh();
		},
		
		tagSuggestionsFilter: function(items) {
			var self = this;
			return $.grep(items, function(v) {
				/* always return all tags!
				for (var i = 0 ; i < self.data.length ; i++) {
					if (self.data[i].tagID == v.tagID) {
						return false;
					}
				}
				*/
				return true;
			});
		},
		
		appendTag: function(tag) {
			var t = this.newTemplate(tag);
			t.insertBefore(this.newtag);
		},
		
		refresh: function() {
			$.post(this.postUrl, this.baseAjaxData({action:'get', id:this.idSelect.val()}), $.proxy(function(d){
				this.data = JSON.parse(d);
				this._('tag').detach();
				$.each(this.data, $.proxy(function(i,v) {
					this.appendTag(v);
				}, this));
			}, this));
		},
		
		newTemplate: function(d) {
			var e = this.tpl.clone();
			this._('tagname', e).text(d.tagName + ' - ' + d.tagDesc);//.button();
			this._('tagremove', e).button({text:false, icons:{primary:'ui-icon-closethick'}});
			this._('tagedit', e).button({text:false, icons:{primary:'ui-icon-pencil'}});
			e.data('entry', d);
			return e;
		},
		
		openNewTagDialog: function(t, callback) {
			var self = this;
			this.newTagDialog.data().name.val(t.tagName);
			this.newTagDialog.data().slug.val(t.tagSlug);
			this.newTagDialog.data().desc.val(t.tagDesc);
			this.newTagDialog.data().trID = t.trID;
			
			var newTag = undefined;
			
			var tagCreatedListener = function(e, d) {
				newTag = true;
				//newTag = d.newTag;
				//self.refresh();
				self.newTagDialog.dialog('close');
			}
			
			this.newTagDialog.bind('tagcreated', tagCreatedListener);
			this.newTagDialog.dialog('option', 'close', function(){
				self.newTagDialog.unbind('tagcreated', tagCreatedListener);
				//console.log('callback');
				callback(newTag);
			});
			
			this.newTagDialog.dialog('open');
		}
	};
}();

NMGTags = {
	newTagManager: function() {
		var F = function(){};
		F.prototype = tagManager;
		var o = new F();
		o.init();
		return o;
	},
	newTagger: function(conf) {
		var F = function(){};
		F.prototype = tagger;
		var o = new F();
		o.init(conf);
		return o;
	}
};

})(jQuery, window);
