var NUT = {

	// UPS table DOM ids
	listID: "#ups_list",
	listBodyID: "#ups_list_body",

	// Field names
	fields: [
		"manufacturer",
		"device-type",
		"support-level",
		"model",
		"comment",
		"driver"
	],

	// Actual HTML table columns
	columns: [
		[ "manufacturer" ],
		[ "model", "comment" ],
		[ "driver" ],
		[ "support-level" ],
	],

	// driver => connection type mappings
	driverMap: function(driver) {

		// Try not to catch "usb" across a word boundary:
		if (driver.match(/_usb|usbserial|usbhid-ups/))
			return "USB";

		// TODO: what about IPMI/powerman-pdu?
		if (driver.match(/snmp-ups|netxml-ups/))
			return "Network";

		return "Serial";

	},

	// Support level => CSS class mappings
	supportLevelClasses: {
		0: "",
		1: "red",
		2: "orange",
		3: "yellow",
		4: "blue",
		5: "green"
	},

	tableCache: false,

	// Parse GET parameters from window url and return them as a hash
	// The call format is:
	//  stable-hcl.html?<filter name>=<filter value>
	// Refer to docs/stable-hcl.txt for examples
	parseGetParameters: function() {

		var url = window.location.href;
		url = url.replace(/#$/, "");

		var fieldPos = url.indexOf("?");

		var get = {};

		if (fieldPos > -1) {

			var fileName = url.substring(0, fieldPos);
			var getList = url.substring(fieldPos + 1).split("&");

			for (var i = 0; i < getList.length; i++) {

				var getField = getList[i].split("=");

				key = unescape(getField[0]).toLowerCase();

				value = unescape(getField[1]);

				// just in case there are any old links out there.
				if (key === "manufacturer" && value === "Tripp-Lite") {
					value = "Tripp Lite";
				}

				get[key] = value;

			}

		}

		return get;

	},

	// UPS filter renderers by data column index
	filterRenderers: {

		"support-level": function(value) {

			var result = [];

			for (var i = 0; i < value; i++)
				result.push("*");

			return result.join("");

		},

		"driver": function(value) {

			return this.driverMap(value);

		},

		"device-type": function(value) {

			var map = {
				"pdu": "Power Distribution Unit",
				"ups": "Uninterruptible Power Supply",
				"scd": "Solar Controller Device"
			}

			return map[value];

		}

	},

	// Specific filter handlers
	filterHandlers:	{

		// Filter functions:
		//  @param {string} value value to filter
		//  @param {array} row raw data fields
		//  @return {boolean} true if value passes the filter, false otherwise

		"driver": function(value, row) {

			var driver = row[this.fields.indexOf("driver")];

			if (this.driverMap(driver) == value)
				return true;

			return false;

		},

		"support-level": function(value, row) {

			var level = row[this.fields.indexOf("support-level")];

			if (level >= value)
				return true;

			return false;

		}

	},

	// Returns rendered UPS data according to column index
	//  @param {integer} index
	//  @param {string} value
	renderFilter: function(index, value) {

		var renderer = this.filterRenderers[this.fields[index]];

		if (typeof renderer == "function")
			return renderer.call(this, value);

		return value;

	},

	// Initialization method
	init: function() {

		this.initFilters();
		this.sortUPSData(UPSData);
		this.buildUPSList(UPSData);
		this.buildFilters(UPSData);

		var get = this.parseGetParameters();

		for (var param in get) {

			var filter = $("#"+param);

			if (filter) {

				filter.val(get[param]);

				this.doFilter();

			}

		}

	},

	// Initialize filter filters references
	initFilters: function() {

		// Display filters fieldset hidden by default for user-agents not using javascript
		$("#filters-set").show();

		this.filters = {

			"support-level": {
				index: this.fields.indexOf("support-level"),
				field: $("#support-level")
			},

			"device-type": {
				index: this.fields.indexOf("device-type"),
				field: $("#device-type")
			},

			"manufacturer": {
				index: this.fields.indexOf("manufacturer"),
				field: $("#manufacturer")
			},

			"model": {
				index: this.fields.indexOf("model"),
				field: $("#model")
			},

			"driver": {
				index: this.fields.indexOf("driver"),
				field: $("#connection")
			}

		}

	},

	// Sorts table data by manufacturer and driver
	//  @param {Object} data
	sortUPSData: function(data) {

		var	mI = this.fields.indexOf("manufacturer"),
			dI = this.fields.indexOf("driver");

		data.sort(function(a, b) {

			var toLower = function(ar) {

				var res = ar.slice();

				res.forEach(function(i, index) {

					if (typeof i == "string")
						res[index] = i.toLowerCase()

				});

				return res;

			}

			var	c = toLower(a),
				d = toLower(b);

			return c[mI] == d[mI] ? c[dI] > d[dI] : c[mI] > d[mI];

		});

	},

	// Builds UPS list from provided data
	//  @param {array} data
	buildUPSList: function(data) {

		var list = $(this.listBodyID);

		// Initialize table cache
		if (!this.tableCache)
			this.tableCache = list.html();

		// If we're rebuilding the original table, just use the one in cache
		if (data == UPSData && this.tableCache) {
			list.html(this.tableCache);
			return;
		}

		list.empty();

		// Bailout if no data
		if (!data || data.length == 0)
			return;

		// Build rows
		var cellHistory = [], rows = [];

		var rowHistory = data[0][0];

		var	classes = [ "even", "odd" ],
			manufIndex = this.fields.indexOf("manufacturer"),
			currentClass = 0;

		data.forEach(function(upsRow, rowIndex) {

			if (upsRow[manufIndex] != rowHistory) {
				currentClass = Number(!currentClass);
				rowHistory = upsRow[manufIndex];
			}

			var cells = [];

			// Build cells
			this.columns.forEach(function(column, colIndex) {

				var cellContent = [];

				column.forEach(function(field) {
					cellContent.push(upsRow[this.fields.indexOf(field)])
				}, this);

				cellContent = cellContent.join("<br />");

				// Inspect the last cell on this column and increase row span if the current cell has the same content
				var cH = cellHistory[colIndex];

				if (column.indexOf("driver") == -1 && cH && cH.html == cellContent)
					cH.rowSpan += 1;
				else {

					var cell = "";

					if (column.indexOf("driver") != -1) {

						cell = {
							html: cellContent,
							rowSpan: 1,
							cls: this.supportLevelClasses[upsRow[this.fields.indexOf("support-level") || ""]]
						};

					} else {

						cell = {
							html: cellContent,
							rowSpan: 1,
							cls: classes[currentClass]
						}

					}

					if (column.indexOf("support-level") != -1)
						cell.cls += " hidden";

					cells.push(cell);

					cellHistory[colIndex] = cell;

				}

			}, this);

			rows.push(cells);

		}, this);

		// Generate actual rows/cells tags
		rows.forEach(function(r, index) {

			r.forEach(function(c, index) {
				r[index] = [ "<td class='", c.cls, "' rowspan='", c.rowSpan, "'>", c.html, "</td>" ].join("");
			});

			rows[index] = [ "<tr>", r.join(""), "</tr>" ].join("");

		});

		list.html(rows.join(""));

	},

	// Initialize filters event listeners
	//  @param {Object} data
	buildFilters: function(data) {

		for (var f in this.filters) {

			var filter = this.filters[f];

			this.populateCombo(data, filter);

			filter.field.change(this.doFilter);

			var op = $("#op-" + (filter.index));

			if (op)
				op.change(this.doFilter);

		}

	},

	// Load data in filter combos
	//  @param {array} data
	//  @param {object} combo
	//  @param {integer} index
	populateCombo: function(data, filter) {

		var values = [];
		var valueDict = {};

		var combo = filter.field;
		var oldValue = combo.val();

		combo.html("<option value='-1'>---</option>");

		// Special case for connection type
		if (filter.field.attr("id") == "connection") {

			[ "Serial", "USB", "Network" ].forEach(function(type) {

				values.push([ type, type ]);

			}, this);

		} else {

			data.forEach(function(row) {

				var value = row[filter.index];

				if (value != "" && !valueDict[value]) {

					values.push([ value, this.renderFilter(filter.index, value) ]);

					valueDict[value] = true;

				}

			}, this);

			values = values.sort();

		}

		values.forEach(function(value) {

			var option = $(document.createElement("option"));

			option.val(value[0]);
			option.text(value[1]);

			combo.append(option);

		}, this);

		combo.val(oldValue);

	},

	// Apply selected filters on UPS list
	doFilter: function() {

		var initialRows = UPSData.slice();
		var filteredRows = UPSData.slice();

		// Applies a single filter on provided UPS data set
		//  @param {string} value
		//  @param {integer} index
		//  @param {array} data
		//  @returns {array} filtered data set
		var applyFilter = function(value, index, data) {

			var tmpData = [];
			tmpData = data.slice();

			tmpData.forEach(function(row, rowIndex) {

				var field = row[index];

				var handler = this.filterHandlers[this.fields[index]];

				if (handler) {

					if (!handler.apply(this, [ value, row ]))
						data.splice(data.indexOf(row), 1);

				} else if (row[index] != value)
					data.splice(data.indexOf(row), 1);

			}, this);

			return data;

		}

		// Sequentially apply filters
		for (var f in NUT.filters) {

			var filter = NUT.filters[f];
			var value = filter.field.val();

			// Is filter active, i.e have the user picked a value in the filter combo
			if (value != "-1") {
				var opField = $("#op-" + filter.index);
				filteredRows = applyFilter.apply(NUT, [ value, filter.index, filteredRows ]);
			}

		}

		// Rebuild UPS list and combos according to filtered data
		NUT.buildUPSList(filteredRows);

		[ "manufacturer", "model", "driver" ].forEach(function(id) {

			if (this.id != id)
				this.populateCombo(filteredRows, this.filters[id]);

		}, NUT);

	},

	resetCombos: function() {

		for (var f in this.filters) {

			var field = this.filters[f].field;

			this.populateCombo(UPSData, this.filters[f]);

			field.val("-1");

		}

		this.buildUPSList(UPSData);

	}

}

if (typeof Array.prototype.indexOf != "function") {

	Array.prototype.indexOf = function(elt) {

		var i = 0;

		while (i < this.length) {

			if (this[i] == elt)
				return i;

			i++;

		}

		return -1;

	}

}

if (typeof Array.prototype.forEach != "function") {

	Array.prototype.forEach = function(cb, scope) {

		for (var i = 0, n = this.length; i < n; i++) {

			if (i in this)
				cb.call(scope, this[i], i, this);

		}

	}

}

// Global initialization
$(function() {

	NUT.init.call(NUT);

});