/******************************************************************************
Windowing Processor module for SVSi Panel Builder scripting.

Example:
myWP = WP("169.254.160.248"); // create object
myWP.getStatus(); //get current status
myWP.switch(300, 1); //switch window 1 to stream based on number

getStatus(callBack)
Refreshes the status of the device. The callBack is a function to be executed when complete.

switch(stream, win = 1)
Switch stream of window. Default is window 1. 

switchAudio(stream) 
Switch windowing processor audio to stream.

delay(milliseconds = 500) 
Pause execution for specified milliseconds. Default is 500.

reboot()
Reboot the device.

modeoff()
Turn off encoder output of the device.

modeon()
Turn on encoder output of the device.

setBackground(bkgnd = 1)
Set background. Default is the first background.

setPriority(win = 1, priority = 4) 
Set window priority. Default is first window. Default is priority 4.
If no parameters are set, it will stack all windows with priorities 4,3,2,1


borderOn(win = all windows)
Turn on window borders. Default is all windows.

borderOff(win = all windows)
Turn off window borders. Default is all windows.

setWindow(win = 1, x1 = 0, y1 = 0, x2 = 1279, y2 = 719)
Set window to (x1, y1),(x2, y2). Default window is 1. Default set is 720p starting from upper left.

cropref(win = all windows, c1=0, c2=0, c3=0, c4=0) 
Crop window based on a 1080p reference. Default is all windows. Default crop is 0 for no crop.

audioOn()
Unmute device.

audioOff()
Mute device.

windowOn(win = all windows)
Turn on window. Default is all windows.

windowOff(win)
Turn off window. Default is all windows.

preset(presetNum = 10)
Execute internal preset. Default is 10 for the default first user preset.


*******************************************************************************/

	//define namespace if it doesn't exist
	if (!Modules.SVSi) Modules.SVSi = {};
	
	//Command list displays in the dropdowns in the Panel Builder script editor under 'modules'
	module_command_list = [
		{name: 'Constructor', func: 'myModule = WP("169.254.1.2", 50001);', comment: '//port is optional'},
		{name: 'Get Status', func: 'myModule.getStatus(function onComplete(){});', comment: '//function passed in will run on complete'},
		{name: 'Start Updating', func: 'myModule.startUpdating(2);', comment: '//will begin updating module object\'s status'},
		{name: 'Stop Updating', func: 'myModule.stopUpdating();', comment: '//will stop updating module object\'s status'},
		{name: 'Switch', func: 'myModule.switch(123, 1);', comment: '//can pass in another module object or the stream #, second argument is window #'},
		{name: 'Switch Audio', func: 'myModule.switchAudio(123, 1);', comment: '//can pass in another module object or the stream #, second argument is window #'},
		{name: 'Mute', func: 'myModule.audioOff();', comment: '//mute'},
		{name: 'Unmute', func: 'myModule.audioOn();', comment: '//unmute'},
		{name: 'Mode On', func: 'myModule.modeon();', comment: '//turn on encoder output of the device.'},
		{name: 'Mode Off', func: 'myModule.modeoff();', comment: '//turn off encoder output of the device.'},
		{name: 'Window On', func: 'myModule.windowOn();', comment: '//Turn on window. Default is all windows.'},
		{name: 'Window Off', func: 'myModule.windowOff();', comment: '//Turn off window. Default is all windows.'},
		{name: 'Set Background', func: 'myModule.setBackground(1);', comment: '//Set background. Default is the first background.'},
		{name: 'Set Priority', func: 'myModule.setPriority(1, 4);', comment: '//Set window priority. Default is first window. Default is priority 4.  If no parameters are set, it will stack all windows with priorities 4,3,2,1'},
		{name: 'Border On', func: 'myModule.borderOn();', comment: '//Turn on window borders. Default is all windows.'},
		{name: 'Border Off', func: 'myModule.borderOff();', comment: '//Turn off window borders. Default is all windows.'},
		{name: 'Set Window', func: 'myModule.setWindow(1, 0, 0, 1279, 719);', comment: '//Set window to (x1,y1),(x2,x3). Default window is 1. Default set is 720p starting from upper left.'},
		{name: 'Crop Reference', func: 'myModule.cropref(null, 0, 0, 0, 0);', comment: '//Crop window based on a 1080p reference. Default is all windows. Default crop is 0 for no crop.'},
		{name: 'Preset', func: 'myModule.preset(10);', comment: '//Execute internal preset. Default is 10 for the default first user preset.'},
		{name: 'Print', func: 'myModule.print();', comment: '//Prints this module object and all of its properties to the Javascript console.'},
		{name: 'Popup', func: 'myModule.popup();', comment: '//Displays an alert dialog with this module object and all of its properties.'},
	];

	/**************************    Utility Functions    ************************/
	function getStatusOfUnit(obj, onComplete, delay) {
		tcpclient(obj.ip, obj.port, asciiToHex('getStatus\r'), function(data){
			var numberKeys = [
				/*N4000*/ "againl","againr","dm_cgain","dm_fgain","dm_slgain","dm_srgain","gpihighevntdly","gratarpint",
				"headphonevol_l","headphonevol_r","inputgainleft","inputgainright","lineoutvol_l","lineoutvol_r","rxaudiodelay",
				"outstream","txstream","rxstream","stream","txaudiodelay","txsample","unsolstatusint",
				/*N_Series*/ "baud","discoveryintervalsec","discoveryport","dvicevtdly","dvidevtdly", "garpint","nd_a_drop",
				"nd_a_drop1s","nd_f_drop","nd_f_drop1s","nd_v_drop","nd_v_drop1s","playlist","streamaudio","unsolstint",
				/*NVR*/ "copypct","dvrmode","free","mpegpcnt","s_play0a","s_play0b","s_play1a","s_play2a","s_play3a","s_play4a",
				"s_play5a","s_play6a","s_play7a","s_play8a","s_play9a","s_reca0a","s_reca0b","s_reca1a","s_reca2a","s_reca3a",
				"s_reca4a","s_reca5a","s_reca6a","s_reca7a","s_reca8a","s_reca9a","s_recv0a","s_recv0b","s_recv1a","s_recv2a",
				"s_recv3a","s_recv4a","s_recv5a","s_recv6a","s_recv7a","s_recv8a","s_recv9a","stream","ttl",
				/*WP*/ "audiostream","w0stream","w1stream","w2stream","w3stream"
			];
			var keyList = ["stream"];
			obj.status = {}; obj.rawstatus = data;
			var str = obj.rawstatus;
			while (str.indexOf('\r') != -1){
				var key = str.substring(0, str.indexOf(':')).toLowerCase();
				var valueString = str.substring(str.indexOf(':')+1, str.indexOf('\r')), valueFloat = parseFloat(valueString);
				if (numberKeys.indexOf(key) != -1) obj.status[key] = valueFloat;	
				else obj.status[key] = valueString;
				keyList.push(key);
				str = str.substring(str.indexOf('\r')+1);
			}
			if (obj.rawstatus === '') obj.error = 'Empty response returned. Maybe no connection to unit?';
			if (obj.error && obj.onError) obj.onError(data, obj.error);
			if (onComplete) onComplete(data);
		});
	};
	function getStreamFromUnit(name){
		for (var i=0; i<unitlist.length; i++) if (unitlist[i].ip == name) return unitlist[i].videostream;
		for (var i=0; i<unitlist.length; i++) if (unitlist[i].name == name) return unitlist[i].videostream;
		for (var i=0; i<unitlist.length; i++) if (unitlist[i].mac.toUpperCase() == name.toUpperCase()) return unitlist[i].videostream;
		return false;
	};
	function getAudioStreamFromUnit(name){
		for (var i=0; i<unitlist.length; i++) if (unitlist[i].ip == name) return unitlist[i].audiostream;
		for (var i=0; i<unitlist.length; i++) if (unitlist[i].name == name) return unitlist[i].audiostream;
		for (var i=0; i<unitlist.length; i++) if (unitlist[i].mac.toUpperCase() == name.toUpperCase()) return unitlist[i].audiostream;
		return false;
	};
	/************************    End of Utility Functions    ***********************/
	
	/* 
	* WP function returns an object representing an N-Series unit
	* ip is the ip address, port is the port to open socket on,
	* onError is a function to be called on errors
	*/
	Modules.WP = Modules.SVSi.WP = function(ip, port, onError){
			
		var obj = {};
		
		obj.ip = ip;
		obj.rawstatus = '';
		obj.error = false;
		obj.port = !undef(port) ? port : '50001';
		obj.onError = !undef(onError) ? onError : noop;
		obj.status = {};
		
		//sets port, doesn't reestablish socket to that port
		obj.setPort = function(port){ obj.port = port; };
		
		obj._updateInterval = false;

		//gets the status of the unit and fills it in as properties on the object
		obj.getStatus = function(onComplete){ getStatusOfUnit(obj, onComplete); };
		
		//gets the status of the unit and fills it in as properties on the object
		//also sets a timeout to call the function again and calls onUpdate if specified
		obj.startUpdating = function(interval, onUpdate){
			if (undef(interval)) interval = 3;
			if (undef(onUpdate)) onUpdate = noop;
			obj._updateInterval = setInterval(function(){
				obj.getStatus(onUpdate);
			}, interval*1000);
		};
		
		obj.stopUpdating = function(){ clearInterval(obj._updateInterval); };
		
		obj.switch = function(stream, win){
			if (undef(win)) win = 1;
			if (typeof stream !== 'number'){
				if (typeof stream === 'object' && stream.ip) stream = stream.ip;
				else stream = getStreamFromUnit(stream);
			}
			win--;
			tcpclient(obj.ip, obj.port, asciiToHex('set:'+win+':'+stream));
		};
		
		obj.stream = obj.switch;
		
		obj.switchAudio = function(stream){
			if (typeof stream !== 'number'){
				if (typeof stream === 'object' && stream.ip) stream = stream.ip;
				else stream = getAudioStreamFromUnit(stream);
			}
			tcpclient(obj.ip, obj.port, asciiToHex('seta:'+stream));
		};
		
		obj.audioStream = obj.switchAudio;
		
		obj.delay = function(milliseconds){
			if (undef(milliseconds)) milliseconds = 500;
			var start = new Date().getTime();
			for (var i=0; i<1e7; i++){
				if ((new Date().getTime() - start) > milliseconds) break;
			}
		};
		
		obj.reboot = function(){
			tcpclient(obj.ip, obj.port, asciiToHex('setSettings:reboot:reboot\r\n'));
		};
		
		obj.modeoff = function(){
			tcpclient(obj.ip, obj.port, asciiToHex('setSettings:mode:off\r'));
		};
		
		obj.modeon = function(){
			tcpclient(obj.ip, obj.port, asciiToHex('setSettings:mode:on\r'));
		};
		
		obj.setBackground = function(bg){
			if (undef(bg)) bg = 1;
			bg--;
			tcpclient(obj.ip, obj.port, asciiToHex('setbkgd:'+bg+'\r'));
		};

		obj.setPriority = function(win, priority){
			//default is to stack the windows.
			if (undef(win) && undef(priority)){
				tcpclient(obj.ip, obj.port, asciiToHex('setz:0:4\rsetz:1:3\rsetz:2:2\rsetz:3:1\r'));
			}
			if (undef(win) || win === null) win = 1;
			if (priority == undefined || priority == null) priority = 4;
			win--;
			tcpclient(obj.ip, obj.port, asciiToHex('setz:'+win+':'+priority+'\r'));
		};
		
		obj.borderOn = function(win){
			// default is all windows.
			if (undef(win) || win === null) {
				tcpclient(obj.ip, obj.port, asciiToHex('bordon:0\rbordon:1\rbordon:2\rbordon:3\r'));
			} 
			else {
				win--;
				tcpclient(obj.ip, obj.port, asciiToHex('bordon:'+win+'\r'));
			}
		};
		
		obj.borderOff = function(win){
			// default is all windows.
			if (undef(win) || win === null) {
				tcpclient(obj.ip, obj.port, asciiToHex('bordoff:0\rbordoff:1\rbordoff:2\rbordoff:3\r'));
			} 
			else {
				win--;
				tcpclient(obj.ip, obj.port, asciiToHex('bordoff:'+win+'\r'));
			}
		};
		
		obj.setWindow = function(win,x1,y1,x2,y2){ 
			if (undef(win) || win === null) win = 1;
			if (undef(x1)) x1 = 0; if (undef(x2)) x2 = 1279;
			if (undef(y1)) y1 = 0; if (undef(y2)) y2 = 719;
			win--;
			tcpclient(obj.ip, obj.port, asciiToHex('winset:'+win+':'+x1+','+y1+','+x2+','+y2+'\r'));
		};
		
		obj.cropref = function(win,c1,c2,c3,c4){ 
			if (undef(c1)) c1 = 0; if (undef(c2)) c2 = 0;
			if (undef(c3)) c3 = 0; if (undef(c4)) c4 = 0;
			// default is all windows.
			if (undef(win) || win === null){
				tcpclient(obj.ip, obj.port, asciiToHex('cropref:0:'+c1+','+c2+','+c3+','+c4+'\r' +
														'cropref:1:'+c1+','+c2+','+c3+','+c4+'\r' +
														'cropref:2:'+c1+','+c2+','+c3+','+c4+'\r' +
														'cropref:3:'+c1+','+c2+','+c3+','+c4+'\r'));
			} 
			else {
				win--;
				tcpclient(obj.ip, obj.port, asciiToHex('cropref:'+win+':'+c1+','+c2+','+c3+','+c4+'\r'));
			}
		};
		
		obj.audioOn = function(){
			tcpclient(obj.ip, obj.port, asciiToHex('unmute\r'));
		};
		
		obj.audioOff = function(){
			tcpclient(obj.ip, obj.port, asciiToHex('mute\r'));
		};
		
		obj.windowOn = function(win){
			// default is all windows.
			if (undef(win) || win === null){
				tcpclient(obj.ip, obj.port, asciiToHex('winon:0\rwinon:1\rwinon:2\rwinon:3\r'));
			} 
			else {
				win--;
				tcpclient(obj.ip, obj.port, asciiToHex('winon:'+win+'\r'));
			}
		};
		
		obj.windowOff = function(win){
			// default is all windows.
			if (undef(win) || win === null){
				tcpclient(obj.ip, obj.port, asciiToHex('winoff:0\rwinoff:1\rwinoff:2\rwinoff:3\r'));
			} 
			else {
				win--;
				tcpclient(obj.ip, obj.port, asciiToHex('winoff:'+win+'\r'));
			}
		};

		obj.preset = function(presetNum){
			if (undef(presetNum) || presetNum === null){
				presetNum = 10;
			}
			tcpclient(obj.ip, obj.port, asciiToHex('preset:'+presetNum+'\r'));
		};
		
		obj.print = function(){ console.log(obj); };
		
		obj.popup = function(){
			var output = '';
			for (var property in obj){
				if (typeof obj[property] !== 'function') output += property + ': ' + obj[property]+'; \n\n';
			}
			alert(output);
		};
		
		obj.getStatus();
		
		return obj;
		
	}
	