/******************************************************************************
N4000 Audio Transceiver module for SVSi Panel Builder scripting.

Sample Startup Script:
myAudio = N4000("169.254.160.248"); // create object

Example button script commands:
myAudio.switch(300); //perform switch command based on stream number
myAudio.switch(myEncoder); //perform switch based on N_Series object (if declared earlier)

startUpdating()
Automatically update status at a periodic interval.

stopUpdating()
Stop automatically updating status.

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

switch(stream)
Switch the audio of the unit to a stream number.
stream may also be a unit name or IP address. Controller will attempt to translate using internal database.

switchAudio(stream)
This command is identical to switch(stream)

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

reboot()
Reboot the device.

modeoff()
Turn off encoder output of the device.

modeon()
Turn on encoder output of the device.

phantomPowerOn()
Turn on phantom power

phantomPowerOff()
Turn off phantom power

openRelay(relay = 1)
Open relay. Default is relay 1.

closeRelay(relay = 1)
Close relay. Default is relay 1.

lineoutVolume(volLeft = null, volRight = undefined)
Set lineout volume of left or right (or both). Set parameter to null to not change that channel's volume.

Example:
lineoutVolume(50); // set both
lineoutVolume(50, null); // set left
lineoutVolume(null, 50); // set right

headphoneVolume(volLeft = null, volRight = undefined)
Same as lineout, but for headphone volume.

audioGain(gainLeft = null, gainRight = undefined)
Same as lineout, but for audio gain.

audioOn()
Unmute audio.

audioOff()
Mute audio.

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

	//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 = N4000("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);', comment: '//can pass in another module object or the stream #'},
		{name: 'Switch Audio', func: 'myModule.switchAudio(123);', comment: '//can pass in another module object or the stream #'},
		{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: 'Reboot', func: 'myModule.reboot();', comment: '//reboot the device.'},
		{name: 'Phantom Power On', func: 'myModule.phantomPowerOn();', comment: '//turn on phantom power'},
		{name: 'Phantom Power Off', func: 'myModule.phantomPowerOff();', comment: '//turn off phantom power'},
		{name: 'Open Relay', func: 'myModule.openRelay(1);', comment: '//open relay'},
		{name: 'Close Relay', func: 'myModule.closeRelay(1);', comment: '//close relay'},
		{name: 'Lineout Volume', func: 'myModule.lineoutVolume(null, null);', comment: '//set lineout volume of left or right (or both). set one parameter to null to not change that channel\'s volume.'},
		{name: 'Headphone Volume', func: 'myModule.headphoneVolume(null, null);', comment: '//set headphone volume of left or right (or both). set one parameter to null to not change that channel\'s volume.'},
		{name: 'Audio Gain', func: 'myModule.audioGain(null, null);', comment: '//set audio gain of left or right (or both). set parameter to null to not change that channel\'s volume.'},
		{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    ***********************/

	/* 
	* N4000 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.N4000 = Modules.SVSi.N4000 = function(ip, port, onError){
			
		var obj = {};
		
		var leftRightCommand = function(commandleft, commandright, left, right){
			if (!undef(left) && undef(right)){
				right = left;
			}
			if (!undef(left) && left !== null){
				tcpclient(obj.ip, obj.port, asciiToHex(commandleft+':'+left+'\r'));
			}
			if (!undef(right) && right !== null){
				tcpclient(obj.ip, obj.port, asciiToHex(commandright+':'+right+'\r'));
			}
        };
		
		obj.ip = ip;
		obj.rawstatus = '';
		obj.error = false;
		obj.port = !undef(port) ? port : '50001';
		obj.port2 = '50002';
		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){
			if (typeof stream != 'number'){
				if (typeof stream === 'object' && stream.status && stream.status.stream) 
					stream = stream.status.stream;
			}
			tcpclient(obj.ip, obj.port, asciiToHex('seta:'+stream));
		};
		
		obj.stream = obj.switch;
		obj.switchAudio = obj.switch;
		obj.audioStream = obj.switch;
		
		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('txdisable\r'));
		};
		obj.modeon = function(){
			tcpclient(obj.ip, obj.port, asciiToHex('txenable\r'));
		};
        
        obj.phantomPowerOn = function(){
			tcpclient(obj.ip, obj.port, asciiToHex('setSettings:phantomPower:on\r'));
		};
        
        obj.phantomPowerOff = function(){
			tcpclient(obj.ip, obj.port, asciiToHex('setSettings:phantomPower:off\r'));
		};
        
        obj.openRelay= function(relay){
            if (undef(relay))  relay = 1;
			tcpclient(obj.ip, obj.port, asciiToHex('setSettings:relay'+relay+'State:open\r'));
		};
        
        obj.closeRelay= function(relay){
            if (undef(relay)) relay = 1;
			tcpclient(obj.ip, obj.port, asciiToHex('setSettings:relay'+relay+'State:close\r'));
		};
        
		obj.audioGain = function(gainLeft, gainRight){
			leftRightCommand('setSettings:adcLeftGain', 'setSettings:adcRightGain', gainLeft, gainRight);
		};
		
		obj.lineoutVolumeOffset = function(offsetVol){
			obj.getStatus(function(){
				if (undef(obj.status) || undef(obj.status.lineoutvol_l)) return;
				var lvleft = parseInt(obj.status.lineoutvol_l, 10);
				var lvright= parseInt(obj.status.lineoutvol_r, 10);
				leftRightCommand('lovolleft', 'lovolright', lvleft + offsetVol, lvright + offsetVol);
			});
		};
		
		obj.headphoneVolumeOffset = function(offsetVol){
			obj.getStatus(function(){
				if (undef(obj.status) || undef(obj.status.lineoutvol_l)) return;
				var lvleft = parseInt(obj.status.headphonevol_l, 10);
				var lvright= parseInt(obj.status.headphonevol_r, 10);
				leftRightCommand('hpvolleft', 'hpvolright', lvleft + offsetVol, lvright + offsetVol);
			});
		};	
		
		obj.lineoutVolume = function(volLeft, volRight){
			leftRightCommand('lovolleft', 'lovolright', volLeft, volRight);
		};
		
		obj.headphoneVolume = function(volLeft, volRight){
			leftRightCommand('hpvolleft', 'hpvolright', volLeft, volRight);
		};
		
		obj.audioOn = function(){
			tcpclient(obj.ip, obj.port, asciiToHex('unmute\r'));
		};
        
		obj.audioOff = function(){
			tcpclient(obj.ip, obj.port, asciiToHex('mute\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;
		
	}
	