/**********************************************************************************************
* 
* JMRI JSON WebSockets and Ajax communication javascript 
* File version 1.0 (old): script 'ThrottleMonitor.py' must be loaded for throttle support
* File version 2.0: native JSON throttle support - doesn't need script 'ThrottleMonitor.py' anymore
* 
* >>> This file version: 2.1 - from original 'jquery.jmri.js' modified by Oscar Moutinho (oscar.moutinho@gmail.com)
* 
* Using HTML5 and jQuery 2...
* Note that this JMRI extension relies on the jquery-websocket v.0.0.4 extension.
* 
**********************************************************************************************/

//----------------------------------------- Manage JMRI WebSockets
(function($) {
	$.extend({
		JMRI: function(url, bindings) {
			var jmri = {};
			if (typeof(url) === 'string') {
				jmri.url = url;
			} else {
				jmri.url = document.URL.split('/')[0] + '//' + document.URL.split('/')[2] + '/json/';
				bindings = url;
			}
			// Default event handlers that do nothing
			jmri.toSend = function(data) {};
			jmri.fullData = function(data) {};
			jmri.error = function(code, message) {};
			jmri.end = function() {};
			jmri.ready = function(jsonVersion, jmriVersion, railroadName) {};
			jmri.throttle = function(name, address, speed, forward, fs) {};
			jmri.light = function(name, userName, comment, state) {};
			jmri.reporter = function(name, userName, state, comment, report, lastReport) {};
			jmri.sensor = function(name, userName, comment, inverted, state) {};
			jmri.turnout = function(name, userName, comment, inverted, state) {};
			jmri.signalHead = function(name, userName, comment, lit, appearance, held, state, appearanceName) {};
			jmri.signalMast = function(name, userName, aspect, lit, held, state) {};
			jmri.route = function(name, userName, comment, state) {};
			jmri.memory = function(name, userName, comment, value) {};
			jmri.power = function(state) {};
			// Add user-defined handlers to the settings object
			$.extend(jmri, bindings);
			// Constants
			jmri.powerUNKNOWN = 0;
			jmri.powerON = 2;
			jmri.powerOFF = 4;
			jmri.turnoutUNDEFINED = 0;
			jmri.turnoutUNKNOWN = 1;
			jmri.turnoutCLOSED = 2;
			jmri.turnoutTHROWN = 4;
			jmri.routeDISABLED = 0;
			jmri.routeUNDEFINED = 1;
			jmri.routeACTIVE = 2;
			jmri.routeINACTIVE = 4;
			jmri.TRUE = true;
			jmri.FALSE = false;
			jmri.YES = 1;
			jmri.NO = 0;
			jmri.EMERGENCY_STOP = '-1.0';
			jmri.STOP = '0.0';
			jmri.FULL_SPEED = '1.0';
			// Getters and Setters
			var roster = null;
			jmri.getRosterGroups = function() {
				if (!roster) roster = loadRoster();
				var rosterGroupsList = [];
				if (roster) {
					roster.find('roster-config roster locomotive').each(function() { 
						$(this).find('attributepairs keyvaluepair').each(function() { 
							var key = $(this).find('key').text();
							if (key.split(':')[0] == 'RosterGroup') {
								var rosterGroup = key.split(':')[1];
								var value = $(this).find('value').text();
								if (value == 'yes' && rosterGroupsList.indexOf(rosterGroup) == -1) rosterGroupsList.push(rosterGroup);
							}
						});
					});
				}
				return rosterGroupsList.sort();
			};
			jmri.getRoster = function(group) {
				if (!roster) roster = loadRoster();
				var locoList = [];
				if (roster) {
					roster.find('roster-config roster locomotive').each(function() { 
						var inGroup = false;
						if (group) {
							$(this).find('attributepairs keyvaluepair').each(function() { 
								var key = $(this).find('key').text();
								if (key.split(':')[0] == 'RosterGroup') {
									key = key.split(':')[1];
									var value = $(this).find('value').text();
									if (key == group && value == 'yes') inGroup = true;
								}
							});
						} else inGroup = true;
						if (inGroup) {
							var loco = {
								name: $(this).attr('id'),
								roadNumber: $(this).attr('roadNumber'),
								roadName: $(this).attr('roadName'),
								mfg: $(this).attr('mfg'),
								owner: $(this).attr('owner'),
								model: $(this).attr('model'),
								dccAddress: parseInt($(this).attr('dccAddress'), 10),
								imageFilePath: $.trim($(this).attr('imageFilePath')),
								iconFilePath: $.trim($(this).attr('iconFilePath'))
							};
							locoList.push(loco);
						}
					});
				}
				return locoList;
			};
			jmri.getRosterItem = function(name) {
				if (!roster) roster = loadRoster();
				var loco = null;
				if (roster) {
					roster.find('roster-config roster locomotive').each(function() { 
						if ($(this).attr('id') == name) {
							loco = {
								name: $(this).attr('id'),
								fileName: $(this).attr('fileName'),
								roadNumber: $(this).attr('roadNumber'),
								roadName: $(this).attr('roadName'),
								mfg: $(this).attr('mfg'),
								owner: $(this).attr('owner'),
								model: $(this).attr('model'),
								dccAddress: parseInt($(this).attr('dccAddress'), 10),
								comment: $(this).attr('comment'),
								maxSpeed: parseInt($(this).attr('maxSpeed'), 10),
								imageFilePath: $.trim($(this).attr('imageFilePath')),
								iconFilePath: $.trim($(this).attr('iconFilePath')),
								URL: $.trim($(this).attr('URL')),
								IsShuntingOn: $.trim($(this).attr('IsShuntingOn')).toUpperCase(),
								f: new Array(29)
							};
							for (var i = 0; i < 29; i++) loco.f[i] = null;
							$(this).find('functionlabels functionlabel').each(function() { 
								var n = parseInt($(this).attr('num'), 10);
								var f = {
									lockable: ($(this).attr('lockable') == 'true'),
									functionlabel: $(this).text(),
									functionImage: $.trim($(this).attr('functionImage')),
									functionImageSelected: $.trim($(this).attr('functionImageSelected'))
								};
								loco.f[n] = f;
							});
						}
					});
				}
				return loco;
			};
			var loadRoster = function() {	//Retrieve roster
				var roster = null;
				$.ajax({
					url: '/roster?format=xml',
					async: false,
					cache: false,
					type: 'GET',
					dataType: 'xml',
					error: function(jqXHR, textStatus, errorThrown) {
						if(jqXHR.status == 404) jmri.error(jqXHR.status, 'Roster empty.\nNo locos defined in JMRI.');
						else jmri.error(jqXHR.status, 'Response:\n' + jqXHR.responseText + '\n\nError:\n' + errorThrown);
					},
					success: function(xmlReturned, status, jqXHR) {
                                            roster = $(xmlReturned);
                                            if (xmlReturned === null) {
                                                jmri.error(200, 'Roster empty.\nNo locos defined in JMRI.');
                                            }
                                        }
				});
				return roster;
			};
			jmri.getObjectList = function(listType) {	//Retrieve a JSON list of objects
				var list = [];
				$.ajax({
					url: jmri.url + listType,
					async: false,
					cache: false,
					type: 'GET',
					dataType: 'json',
					error: function(jqXHR, textStatus, errorThrown) {
						jmri.error(jqXHR.status, 'Response:\n' + jqXHR.responseText + '\n\nError:\n' + errorThrown);
					},
					success: function(listReturned, status, jqXHR) {list = listReturned;}
				});
				return list;
			};
			var heartbeatDelay;	// Heartbeat timer delay
			var heartbeat = null;	// Heartbeat timer
			jmri.closeSocket = function() {jmri.socket.close();};
			jmri.getJMRI = function(type, name) {
				if (!heartbeat) {jmri.error(0, 'The JMRI WebSocket service is not ready.\nSolve the problem and refresh web page.'); return;}
				var lp = (name) ? {"name":name} : {};
				jmri.toSend(JSON.stringify({"type":type,"data":lp}));
				jmri.socket.send(type, lp);
			};
			jmri.setJMRI = function(type, name, args, method) {
				if (!heartbeat) {jmri.error(0, 'The JMRI WebSocket service is not ready.\nSolve the problem and refresh web page.'); return;}
				var lp = (name) ? {"name":name} : {};
				jmri.toSend(JSON.stringify({"type":type, "data":jmri.jsonConcat(lp, args)}));
				jmri.socket.send(type, jmri.jsonConcat(lp, args), method);
			};
			jmri.jsonConcat = function(o1, o2) {	// Concatenate JSON name-value pair lists
				var o = {};
				var key;
				for (key in o1) o[key] = o1[key];
				for (key in o2) o[key] = o2[key];
				return o;
			};
			// WebSocket
			jmri.socket = $.websocket(jmri.url.replace(/^http/, 'ws'), {
				close: function() {	// Stop the heartbeat when the socket closes
					if (heartbeat) {
						clearInterval(heartbeat);
						heartbeat = null;
					}
				},
				message: function(e) {jmri.fullData(e.originalEvent.data);},
				events: {
					error: function(e) {jmri.error(e.data.code, e.data.message);},
					pong: function(e) {},
					goodbye: function(e) {
						jmri.socket.close();
						jmri.end();
					},
					hello: function(e) {	// Handle the initial handshake response from the server
						heartbeatDelay = e.data.heartbeat;
						heartbeat = setInterval(function() {jmri.toSend(JSON.stringify({"type":"ping"})); jmri.socket.send('ping');}, heartbeatDelay);
						jmri.ready(e.data.json, e.data.JMRI, e.data.railroad);
					},
					throttle: function(e) {
						var functions = new Array(29);
						for (var i = 0; i < 29; i++) functions[i] = e.data['F' + i];
						jmri.throttle(e.data.throttle, e.data.address, e.data.speed, e.data.forward, functions);
					},
					light: function(e) {jmri.light(e.data.name, e.data.userName, e.data.comment, e.data.state);},
					reporter: function(e) {jmri.reporter(e.data.name, e.data.userName, e.data.state, e.data.comment, e.data.report, e.data.lastReport);},
					sensor: function(e) {jmri.sensor(e.data.name, e.data.userName, e.data.comment, e.data.inverted, e.data.state);},
					turnout: function(e) {jmri.turnout(e.data.name, e.data.userName, e.data.comment, e.data.inverted, e.data.state);},
					signalHead: function(e) {jmri.signalHead(e.data.name, e.data.userName, e.data.comment, e.data.lit, e.data.appearance, e.data.held, e.data.state, e.data.appearanceName);},
					signalMast: function(e) {jmri.signalMast(e.data.name, e.data.userName, e.data.aspect, e.data.lit, e.data.held, e.data.state);},
					route: function(e) {jmri.route(e.data.name, e.data.userName, e.data.comment, e.data.state);},
					memory: function(e) {jmri.memory(e.data.name, e.data.userName, e.data.comment, e.data.value);},
					power: function(e) {jmri.power(e.data.state);}
				}
			});
			return jmri;
		}
	});
})(jQuery);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ End of file
