(function ($) {
	/**
	 * Plugin for replacing multi selects with more intuitive selection methods
	 * @author Patrik Å
	 */
	$.fn.multiselect = function () {
		return this.filter('select[multiple]').each (
			function () {
				var $this = $(this);
				$.fn.multiselect.replaceMultiselect($this);
			}
		);
	};
// Private plugin methods ----------------------------------------	
	function replace (string, placeholders) {
		var manipulatedString = string;
		$.each(placeholders,
			function (key) {
				manipulatedString = manipulatedString.replace("#{"+key+"}", this);
			}
		);
		return manipulatedString;
	}
	
	/**
	 * Clones the options of the multiselect into the select
	 */
	function addOptions ($multiselect, $select) {
		var $options = $multiselect.children().clone();
		$select.append($options);
	}

	/**
	 * Creates a select box with the same options as the multiselect
	 */
	function createSelect ($multiselect) {
		// #{x} is a placeholder, and is replaced with a proper value before insertion
		// into the DOM.
		var selectHtml = '<select id="#{id}"></select>';
		var $select = 
			$(replace(
				selectHtml,
				{
					'id': $multiselect.attr('id') + '-jsMultiselect'
				}
			));
		return $select;
	}
	
	/**
	 * Hides the multiselect from the client. It is still submitted together
	 * with the form
	 */
	function hideMultiselect ($multiselect) {
		$multiselect.css(
			{
				'position': 'absolute',
				'left': '-9999px'
			}
		);
		$multiselect.hide();
	}
	
	/**
	 * Removes an option from the created select box
	 */
	function removeSelectOption ($option, $selectBox) {
		var $parent = $option.parent('optgroup');
		// Remove the selected option
		$option.attr('disabled', 'disabled').hide();
		//Remove optgroup if it doesn't have any more children
		if(!$parent.children(':visible').size() > 0) {
			$parent.hide();
		}
	}
	/**
	 * Adds an option into the created select box in the correct position
	 */
	function addSelectOption ($multiselectOption, $multiselect, $select) {
		var $selectOption = $select.find('[label='+$multiselectOption.attr('label')+']');
		$selectOption.removeAttr('disabled').show();
		$selectOption.parent('optgroup').show();
	}
	
	function createTag ($option, $tags) {
		var id = $option.attr('value')+'_'+ $option.closest('select').attr('id');
		if($('#' + id).size() > 0) { 
			return; 
		}
		var tagText = $option.attr('label');
		var $tag = $('<li class="tag" id="'+ id +'"><span class="remove"></span>'+ tagText +'</li>');
		$tag.find('.remove').click($.fn.multiselect.onDelete);
		$tags.append($tag);
	}
	
	function createTags ($multiselect, $select) {
		var $tags = $('<ul class="tags"></ul>');
		$multiselect.parent().append($tags);
		$multiselect
			.find(':selected')
			.each(function (i) {
				var $option = $interactiveSelect.find('[value='+$(this).attr('value')+']');
				createTag($option, $tags);
				// Hide preselected options
				removeSelectOption($option, $interactiveSelect);
			});
			
	}
	
	function removePrompt ($tags) {
		$tags.siblings('.selectPrompt').remove();
	}
	
	function createPrompt ($tags) {
		if($tags.siblings('.selectPrompt').length > 0) {
			return;
		}
		
		$prompt = $('<p class="selectPrompt">Du kan välja fler</p>');
		$prompt.insertBefore($tags);
	}
	
	function checkPrompt ($multiselect) {
		var $tags = $multiselect.siblings('.tags');
		if($tags.children().length > 0) {
			createPrompt($tags);
		} else {
			removePrompt($tags);
		}
	}

// Public plugin methods ------------------------------------------

	/**
	 * Called upon a select event in the created select box
	 */
	$.fn.multiselect.onSelect = function () {
		var $interactiveSelect = $(this);
		var $multiselect = $interactiveSelect.siblings('select([multiple])');
		
		// Select corresponding option in the multiselect
		var $selectedOption = $interactiveSelect.find(':selected'),
			value = $selectedOption.attr('value');
		if(value === '') {
			return;
		}
		var $multiselectOption = $multiselect.find('[value='+value+']');
		$multiselectOption.attr('selected', 'selected');
		
		// Create the tag
		var $tagContainer = $interactiveSelect.siblings('.tags');
		var $tag = createTag($selectedOption, $tagContainer);
		
		removeSelectOption($selectedOption, $interactiveSelect);
		$interactiveSelect.val('');
		checkPrompt($multiselect);
		$multiselect.trigger('jsMultiselect:select.'+$multiselect.attr('id'));
	};
	
	/**
	 * Called when a selected option is deleted
	 */
	$.fn.multiselect.onDelete = function () {
		var $tag = $(this).closest('.tag');
		var $tags = $tag.closest('.tags');
		var id = $tag.attr('id');
		// Remove the tag
		$tag.remove();
		
		// Extract the option value from the tag id
		var value = id.substr(0, id.indexOf('_'));
		// Find the multiselect for the tag
		var $multiselect = $tags.siblings('select[multiple]');
		// Find the select box for the tag
		var $select = $tags.siblings('select:not([multiple])');
		// Find the option corresponding to the value of the tag
		var $multiselectOption = $multiselect.find('[value='+value+']');
		// Deselect the option from the multiselect
		$multiselectOption.removeAttr('selected');
		
		addSelectOption($multiselectOption, $multiselect, $select);
		checkPrompt($multiselect);
		$multiselect.trigger('jsMultiselect:delete.'+$multiselect.attr('id'));
	};	
	
	/**
	 * Replaces the multiselect with a simple select box. Also adds listeners
	 * to the select box which ties it to the multiselect. Upon a selection
	 * the corresponding option is selected in the multiselect-box.
	 * @see $.fn.multiselect.onSelect
	 */
	$.fn.multiselect.replaceMultiselect = function ($multiselect) {
		$interactiveSelect = createSelect ($multiselect);
		addOptions($multiselect, $interactiveSelect);
		createTags($multiselect, $interactiveSelect);
		// Insert the new select into the DOM
		$interactiveSelect.insertAfter($multiselect);
		// Hide the multiselect from view
		hideMultiselect($multiselect);
		$interactiveSelect.val('');
		
		checkPrompt($multiselect);
		// Add event handler
		$interactiveSelect.change($.fn.multiselect.onSelect);
		// Setup dependencies on other multiselects
		//setupDependsOn($multiselect, $interactiveSelect);
		
	};
	
	$(document).ready(function () {
		$('select[multiple]').multiselect();}
	);		
})(jQuery);