(function($) {
	$.fn.tagger = function(tagsContainerSelector, o) {
		var options = $.extend(true, {}, $.fn.tagger.defaults, o);
		for(var key in options.classes) {
			if(key != 'prefix') {
				options.classes[key] = options.classes.prefix+options.classes[key];
			}
		}
		
		var Tagger = Class.create({
			initialize: function(inputField, tagsContainerSelector) {
				this.count = 0;
				this.idPrefix = 'tagger-'+inputField.attr('name')+'-';
				
				this.tagsContainer = $(tagsContainerSelector);
				this.inputField = inputField;
				
				this.createHtml();
			},
			
			parseInitialValues: function() {
				var value = this.inputField.val();
				if(value.length > 0) {
					var t = this;
					value.split(',').each(function(tag) {
						new Tag(tag, t, t.idPrefix+t.count)
						t.count++;
					});
					this.inputField.val('');
				}
			},
			
			createHtml: function() {
				var name = this.inputField.attr('name'), value = this.inputField.val();
				this.inputField.attr('name', 'tagger-'+name);
				
				//name = name.gsub('[', '').gsub(']', '');
				this.inputField.after("<input type='hidden' name='"+name+"' value='"+value+"' />");
				this.hiddenField = $('body').find('input[name="'+name+'"]');
				
				this.bindEvents();
			},
			
			bindEvents: function() {
				var t = this;
				this.inputField.keyup(function(e) {t.keyup(e);});
				this.inputField.keydown(function(e) {t.keydown(e);});
				this.inputField.blur(function(e) {t.blur(e);});
			},
			
			blur: function(event) {
				var data = this.inputField.data('autocomplete-select');
				if(!data || !data.visible()) {
					value = this.inputField.val();
					value = String.trimCharacters(value, [' ', ',']);
					if(value.length > 0) {
						(new Tag(value, this, this.idPrefix+this.count)).save();
						this.count++;
						this.inputField.val('');
					}
				}
			},
			
			keyup: function(event) {
				var key = event.keyCode;
				var value = '';
				if(key == 13 || key == 9)
					value = this.inputField.val();
					value = String.trimCharacters(value, [' ', ',']);
				if(key == 188) {
					value = this.inputField.val();
					value = String.trimCharacters(value, [' ', ',']);
				}
				
				if(value.length > 0) {
					(new Tag(value, this, this.idPrefix+this.count)).save();
					this.count++;
					this.inputField.val('');
					event.preventDefault();
				}
			},
			
			keydown: function(event) {
				var key = event.keyCode;
				if(key == 13 || key == 9) {
					event.preventDefault();
					event.stopPropagation();
				}
			},
			
			save: function() {
				var nodes = this.tagsContainer.children();
				var text = [];
				nodes.each(function() {
					text.push($(this).data('tagger').text);
				});
				this.hiddenField.val(text.join(','));
				
				if(options.changeCallback)
					options.changeCallback(this.hiddenField.val());
			},
			
			cancel: function() {
				this.tagsContainer.children().each(function() { $(this).data('tagger').cancel(); });
			}
		});
		
		var Tag = Class.create({
			initialize: function(text, tagger, id) {
				this.text = text;
				this.originalText = text;
				this.tagger = tagger;
				this.id = id;
				
				this.container = this.createContainer();
				this.container.data('tagger', this);
				
				this.createHtml();
			},
			
			createContainer: function() {
				var clas = options.classes.tag;
				var html = "<div id='"+this.id+"' class='"+clas+"'></div>";
				this.tagger.tagsContainer.append(html);
				return $('#'+this.id);
			},
			
			createHtml: function() {
				var html = options.templates.tag.gsub('%tagTextClass', options.classes.tagText)
					.gsub('%destroyButtonClass', options.classes.destroyButton)
					.gsub('%text', this.text);
					
				this.container.removeClass(options.classes.editableTag);
				this.container.html(html);
				this.bindEvents();
			},
			
			createEditHtml: function() {
				var html = options.templates.editableTag.gsub('%editFieldClass', options.classes.editField)
					.gsub('%saveButtonClass', options.classes.saveButton)
					.gsub('%destroyButtonClass', options.classes.destroyButton)
					.gsub('%size', this.text.length)
					.gsub('%text', this.text);
					
				this.container.html(html);
				this.field = this.container.find('.'+options.classes.editField);
				
				this.setSize();
				this.container.addClass(options.classes.editableTag);
				this.bindEvents();
			},
			
			bindEvents: function() {
				var t = this;
				if(this.container.hasClass(options.classes.editableTag)) {
					this.container.unbind('click');
					
					this.container.blur(function(event) { event.stopPropagation(); t.containerBlur(); });
					this.container.focus(function(event) { event.stopPropagation(); t.containerFocus(); });
					this.container.children().each(function() { 
						$(this).blur(function(event) { event.stopPropagation(); t.containerBlur(); });
						$(this).focus(function(event) { event.stopPropagation(); t.containerFocus(); });
					});
					
					this.container.find('.'+options.classes.destroyButton).click(function(event) { event.stopPropagation(); t.cancel(); });
					this.container.find('.'+options.classes.saveButton).click(function(event) { event.stopPropagation(); t.update(); });
					this.field.keyup(function() { t.keyup(); });
				} else {
					this.container.unbind('blur').unbind('focus');
					this.container.find('.'+options.classes.destroyButton).click(function() { t.destroy(); });
					this.container.click(function() { t.tagger.cancel(); t.createEditHtml(); });
				}
			},
			
			containerBlur: function() {
				var t = this;
				this.blurTimeout = setTimeout(function() { t.update(); }, 100);
			},
			
			containerFocus: function() {
				clearTimeout(this.blurTimeout);
			},
			
			keyup: function() {
				this.text = this.field.val();
				this.setSize();
			},
			
			setSize: function() {
				var size = this.text.length;
				if(size > options.maxFieldSize)
					size = options.maxFieldSize;
				if(size < options.minFieldSize)
					size = options.minFieldSize;
					
				this.field.attr('size', size);
			},
			
			cancel: function() {
				if(this.container.hasClass(options.classes.editableTag)) {
					this.text = this.originalText;
					this.createHtml();
				}
			},
			
			save: function() {
				if(options.ajax.use)
					this.makeAjaxRequest('saveAction');
				else
					this.tagger.save();
			},
			
			update: function() {
				this.text = this.field.val();
				
				if(options.ajax.use)
					this.makeAjaxRequest('updateAction');
				else
					this.tagger.save();
					
				this.createHtml();
				this.originalText = this.text;
			},
			
			destroy: function() {
				this.container.remove();
				
				if(options.ajax.use)
					this.makeAjaxRequest('destroyAction');
				else
					this.tagger.save();
			},
			
			makeAjaxRequest: function(action) {				
				var url = options.ajax.urlBase;
				url += options.ajax[action];
				url += options.ajax.extraParams;
				
				url = url.gsub('%oldText', this.originalText).gsub('%text', this.text);
				
				$.get(url);
			}
		});
		
		return $(this).each(function() {
			var t = new Tagger($(this), tagsContainerSelector);
			t.parseInitialValues();
			return t;
		});
	}
	
	$.fn.tagger.defaults = {
		classes: {
			prefix: 'tagger-',
			tag: 'tag',
			tagText: 'text',
			editableTag: 'editable',
			editField: 'editField',
			saveButton: 'save',
			destroyButton: 'destroy'
		}, templates: {
			tag: "<span class='%tagTextClass'>%text</span><div class='%destroyButtonClass'></div>",
			editableTag: "<input type='text' name='%editFieldClass' value='%text' size='%size' class='%editFieldClass' /><a href='#' class='%saveButtonClass'>Save</a><div class='%destroyButtonClass'></div>"
		}, ajax: {
			use: true,
			urlBase: '/controller/',
			saveAction: 'add_tag?text=%text',
			updateAction: 'edit_tag?oldText=%oldText&text=%text',
			destroyAction: 'remove_tag?text=%text',
			extraParams: '&photo_id=1'
		}, maxFieldSize: 20,
		minFieldSize: 4,
		changeCallback: null
	};
	
})(jQuery);

Object.extend(String, {
	trimCharacters: function(s, characters) {
		var startTrim = 0, endTrim = s.length, foundTrim = false;
		for(var c = 0; c < s.length; c++) {
			foundTrim = false;
			for(var d = 0; d < characters.length; d++) {
				if(s.charAt(c) == characters[d]) {
					startTrim++;
					foundTrim = true;
					break;
				}
			}
			if(!foundTrim)
				break;
		}
		
		for(var c = s.length-1; c >= startTrim; c--) {
			foundTrim = false;
			for(var d = 0; d < characters.length; d++) {
				if(s.charAt(c) == characters[d]) {
					endTrim--;
					foundTrim = true;
					break;
				}
			}
			if(!foundTrim)
				break;
		}
		
		return s.substring(startTrim, endTrim);
	}
});