﻿
(function($) { // create closure

	//setup a namespace
	var nsp = 'filterClauseEditor';

	//***************************************************
	// Main plugin method
	//***************************************************
	$.fn.filterClauseEditor = function(options) {

		if (typeof options == 'string') {
			// we are triggering a method on an existing editor
			var fn = $[nsp][options];
			if (!fn) {
				throw ("filterClauseEditor - No such method: " + options);
			}
			var args = $.makeArray(arguments).slice(1);
			return fn.apply(this[0], args);
		}

		return this.filter('table').each(function() {
			if (this.filterClauseEditor) {
				return; // Get out early - this target has already been initialized.
			}

			// Merge the supplied options with the defaults.
			var opts = $.extend({}, $.filterClauseEditor.defaults, options || {});
			this.opts = opts;
			this.filterClauseEditor = "Initialized";

			_.initializeTable.call(this, opts);

			if (opts.clauses && opts.clauses.length > 0) {

				$[nsp].addClauses.call(this, opts.clauses);
			}
			else {
				$[nsp].addClause.call(this, opts.newClause);
			}
		});
	};
	//***************************************************
	// End of Main plugin method
	//***************************************************

	// Public Members
	// callable as: $.filterClauseEditor.Foo()
	$[nsp] = {
		defaults: {
			colNames: ['Field', 'Operator', 'Value'],
			filterName: "",
			clauses: {},
			newClause: { Field: "", Operator: "EQ", Value: "" },
			fields: [{ Value: "", Text: "None available"}]
		},

		editors: {

			dateBox: function() {
				return function(id) { return _.buildEditorDatePicker(id); };
			},

			dropDown: function(choices) {
				return function(id) { return _.buildEditorSelect(id, choices); };
			},

			textBox: function() {
				return function(id) { return _.buildEditorTextBox(id); };
			},

			trueFalse: function() {
				return function(id) { return _.buildEditorSelect(id, ["True", "False"]); };
			}
		},

		addClauses: function(clauses) {
			for (var i = 0; i < clauses.length; i++) {
				$[nsp].addClause.call(this, clauses[i]);
			};
		},

		addClause: function(clause) {
			var id = _.getUniqueId();

			var row = $('<tr>');

			$('<td>').append(_.buildFieldSelector(id, this.opts, clause)).appendTo(row);
			$('<td>').append(_.buildOperatorSelector(id, this.opts, null)).appendTo(row);
			$('<td>').append(_.buildValueEditor(id, this.opts, null)).appendTo(row);
			$('<input type="button" value="Remove" />')
                .bind('click', function() {
                	var table = $(this).closest('table');
                	$(this).closest('tr').remove();

                	if ($('tbody >tr', table).length == 0) {
                		$[nsp].addClause.call(table[0], table[0].opts.newClause);
                	}
                })
                .appendTo('<td></td>').parent()
                .appendTo(row);

			$('tbody:last', this).append(row);
			$('#Field_' + id).val(clause.Field);
			$('#Field_' + id).trigger('change');
		},

		clearClauses: function() {
			$('tbody >tr', this).remove();
		},

		getRowData: function() {
			var items = new Array();
			$('tbody >tr', this).each(function(id) {
				var item = {
					"Field": $("[name^='Field_']", this).val(),
					"Operator": $("[name^='Operator_']", this).val(),
					"Value": $("[name^='Value_']", this).val()
				};
				items.push(item);
			});
			return items;
		},

		reset: function() {
			$[nsp].clearClauses.call(this);
			$[nsp].addClause.call(this, this.opts.newClause);
		},

		getFilter: function() {
			return {
				"Name": this.opts.filterName,
				"Clauses": $[nsp].getRowData.call(this)
			}
		},

		setFilter: function(filter) {
			this.opts.filterName = filter.Name;
			$[nsp].clearClauses.call(this);
			$[nsp].addClauses.call(this, filter.Clauses);

			if ($('tbody >tr', this).length == 0) {
				$[nsp].addClause.call(this, this.opts.newClause);
			}
		}
	};

	// Private Members (in the "_" object)
	// NB: "this" will refer to "_" unless you use the call or apply methods
	var _ = {
		nextId: 0,

		getUniqueId: function() {
			return _.nextId++;
		},

		initializeTable: function(opts) {
			var header = '<thead><tr>'
                + '<th>' + opts.colNames[0] + '</th>'
                + '<th>' + opts.colNames[1] + '</th>'
                + '<th>' + opts.colNames[2] + '</th>'
                + '<th></th>'
                + '</tr></thead>'
                + '<tbody></tbody>';
			$(this).html(header);

			var button = $('<input type="button" value="Add" />')
                .bind('click', function() {
                	$[nsp].addClause.call($(this).closest('table')[0], opts.newClause);
                })

			var buttonCell = $('<td></td>').append(button);
			var footerRow = $('<tr><td colspan="3"></td></tr>').append(buttonCell);
			var foot = $('<tfoot></tfoot>').append(footerRow);

			$(this).append(foot);
		},

		buildFieldSelector: function(idx, opts, clause) {
			var id = 'Field_' + idx;

			var select = $('<select id="' + id + '" name="' + id + '">');
			for (var i = 0; i < opts.fields.length; i++) {
				var option = $('<option>').attr('value', opts.fields[i].Value).text(opts.fields[i].Text);
				select.append(option);
			};

			select.bind('change', function() {
				var selectedIndex = $(this).attr("selectedIndex");

				var newOptionsSelector = _.buildOperatorSelector(idx, opts, opts.fields[selectedIndex]);
				$('#Operator_' + idx).replaceWith(newOptionsSelector);
				$('#Operator_' + idx).val(clause.Operator);

				var newValueEditor = _.buildValueEditor(idx, opts, opts.fields[selectedIndex]);
				$('#Value_' + idx).replaceWith(newValueEditor);
				$('#Value_' + idx).val(clause.Value);

				$('#Operator_' + idx).trigger('change');

			});
			return select;
		},

		buildOperatorSelector: function(idx, opts, field) {
			var id = 'Operator_' + idx;

			if (!field || !field.Value) {
				return $('<div id="' + id + '" name="' + id + '"></div');
			}

			var availableOps = {};
			availableOps['EQ'] = '<option value="EQ">Equals</option>';
			availableOps['NE'] = '<option value="NE">Not Equal</option>';
			availableOps['GT'] = '<option value="GT">Greater Than</option>';
			availableOps['GTE'] = '<option value="GTE">Greater or Equal</option>';
			availableOps['LT'] = '<option value="LT">Less Than</option>';
			availableOps['LTE'] = '<option value="LTE">Less or Equal</option>';
			availableOps['LK'] = '<option value="LK">Like</option>';
			availableOps['NLK'] = '<option value="NLK">Not Like</option>';
			availableOps['NUL'] = '<option value="NUL">Is Missing</option>';
			availableOps['NNUL'] = '<option value="NNUL">Is Not Missing</option>';

			var selector = '<select id="' + id + '" name="' + id + '">';

			if (field.Operators) {
				$.each(field.Operators, function(id, op) {
					if (typeof (op) === "string")
					{ selector += availableOps[op] }
					else {
						selector += '<option value="' + op.value + '">' + op.text + '</option>';
					}
				});
			}
			else {
				$.each(availableOps, function(id, op) {
					selector += op;
				});
			}

			selector += '</select>';

			var selectorElement = $(selector);

			selectorElement.bind('change', function() {
				if ($(this).val() == 'NUL' || $(this).val() == 'NNUL') {
					$('#Value_' + idx).hide();
				}
				else {
					$('#Value_' + idx).show();
				}
			});
			return selectorElement;
		},

		buildValueEditor: function(idx, opts, field) {
			var id = 'Value_' + idx;

			if (!field || !field.Value) {
				return $('<div id="' + id + '" name="' + id + '"></div');
			}

			if (field && field.Editor && $.isFunction(field.Editor)) {
				return field.Editor(id);
			}
			else {
				return _.buildEditorTextBox(id);
			}
		},

		buildEditorTextBox: function(id) {
			return $('<input type="text" id="' + id + '" name="' + id + '"/>');
		},

		buildEditorDatePicker: function(id) {
			var textBox = _.buildEditorTextBox(id);
			textBox.datepicker();
			return textBox;
		},

		buildEditorSelect: function(id, choices) {
			var selector = '<select id="' + id + '" name="' + id + '">';

			$.each(choices, function(index, op) {
				if (typeof (op) === "string")
				{ selector += '<option>' + op + '</option>'; }
				else {
					selector += '<option value="' + op.value + '">' + op.text + '</option>';
				}
			});

			selector += '</select>';
			return $(selector);
		}


	};

})(jQuery);                                                                         // end of closure
