//-----------------------------------------------------------------------------
//-------- Copyright (c)2003-2013 XYLIO INFO SRL. All Rights Reserved. --------
//-----------------------------------------------------------------------------
// file implementing MIDI controller 'Hercules DJ Console MK2'
//----------------------------------------------------------------------------- 
function getLanguageVersion()
{
	return 1;// do not modify this, this should be 1 for all scripts
}
//----------------------------------------------------------------------------- 
function getFileVersion()
{
	return 1;// update this as needed
}
//----------------------------------------------------------------------------- 
// return a user friendly name for the controller supported
function getControllerUserFriendlyName()
{
	return "Hercules DJ Control Mk2";
}
//----------------------------------------------------------------------------- 
// return the communication type that the controller supports 
// this can be either 'midi' or 'hid' (please return only one of them in case the controller supports both)
function getControllerType()
{
	return 'midi';
}
//----------------------------------------------------------------------------- 
// preferred way to identify the controller (if it supports MIDI SysEx)
// return false if the controller does not support this kind of identification
function getSupportedMidiSysExIdString(osId)
{
	// sysex response to 'F0 7E 7F 06 01 F7' (identification request sysex)
	// please note that the response contains 5 bytes in hex format containing
	// the Manufacturer ID (3 bytes) and Model Number (2 bytes)
	// ATTN: the returned value should omit the standard 'F0 7E 00 06 02' message start
	//if(osId==1) return false;
	return "00014E020002";
	// F0 7E 00 06 02 00 01 4E 02 00 02 00 01 00 00 00 F7 -- Hercules DJ Console MK2 complete sysex
	// F0 7E 00 06 02 00 01 4E 01 00 03 00 01 00 00 00 F7 -- Hercules DJ Control MP3 complete sysex
	// F0 7E 00 06 02 00 01 4E 02 00 04 00 01 00 00 00 F7 -- Hercules DJ Console RMX complete sysex
	// F0 7E 00 06 02 00 01 4E 01 00 05 00 01 00 00 00 F7 -- Hercules DJ Control Steel complete sysex	
	// F0 7E 00 06 02 00 01 4E 01 00 07 00 01 00 00 00 F7 -- Hercules DJ Control MP3e2 complete sysex
	// F0 7E 00 06 02 00 01 4E 02 00 08 00 01 00 00 00 F7 -- Hercules DJ Console MK4 complete sysex
}
//----------------------------------------------------------------------------- 
// this is NOT the preferred way to identify the controller but you should use
// it if the controller does not support standard MIDI SysEx identification
// return false if the controller does not support this kind of identification
// osId -- 0 Windows; 1 MacOSX; 2 Linux; -1 unknown
function getSupportedMidiDeviceNameForInput(osId)
{
	//if(osId==0) return "Hercules DJ Console Mk2 MIDI";// Windows
	//if(osId==1) return "DJ Console Mk2 Port 1";// Mac
	return false;
}
//----------------------------------------------------------------------------- 
function getSupportedMidiDeviceNameForOutput(osId)
{
	//if(osId==0) return "Hercules DJ Console Mk2 MIDI";// Windows
	//if(osId==1) return "DJ Console Mk2 Port 1";// Mac
	return false;
}
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
// some variables necessary only for this particular script
var jogWheelsInScratchMode=0;
// fx: flanger, beatwaw, echo; cue: 1,2,3; loop:2,4,8;
var deckMode=[-1,-1];// -1 none; 0 fx; 1 cue; 2 loop;
//----------------------------------------------------------------------------- 
function toggleMode(uniqueId,deck)
{
	// switch off previous mode's led
	switch(deckMode[deck])
	{
	case 0: sendMidi(uniqueId,0xB0,0,(deck==0)?0x0F:0x10,0x00); break;// fx
	case 1: sendMidi(uniqueId,0xB0,0,(deck==0)?0x0E:0x11,0x00); break;// cue
	case 2: sendMidi(uniqueId,0xB0,0,(deck==0)?0x0D:0x12,0x00); break;// loop
	}
	// set next mode
	deckMode[deck] = (deckMode[deck] + 1) % 3;
	// switch on new mode's led
	switch(deckMode[deck])
	{
	case 0: sendMidi(uniqueId,0xB0,0,(deck==0)?0x0F:0x10,0x7F); break;// fx
	case 1: sendMidi(uniqueId,0xB0,0,(deck==0)?0x0E:0x11,0x7F); break;// cue
	case 2: sendMidi(uniqueId,0xB0,0,(deck==0)?0x0D:0x12,0x7F); break;// loop
	}
	//debug('mode for deck '+deck+' is '+deckMode[deck]);
}
//----------------------------------------------------------------------------- 
// this is called when the controller is initialized and the control is passed to this script
// use this to initialize any variables you might need, the controller' leds, etc
// uniqueId is a unique integer between 0..15 that is guaranteed not to change for 
// a certain opened controller -- please note that there can be many controllers
// of the same type in use at the same time ! Use uniqueId to identify between them
function controllerInit(uniqueId)
{
	debug('controllerInit ['+uniqueId+']');// this outputs a string to DebugView app on Win
	//sendMidi(uniqueId, status, channel, data1, data2);
	for(var deck=0; deck<2; deck++)
	{
		sendMidi(uniqueId,0xB0,0,(deck==0)?0x0F:0x10,0x00);// fx
		sendMidi(uniqueId,0xB0,0,(deck==0)?0x0E:0x11,0x00);// cue
		sendMidi(uniqueId,0xB0,0,(deck==0)?0x0D:0x12,0x00);// loop
		call('sleep', 10);
		sendMidi(uniqueId,0xB0,0,(deck==0)?0x09:0x03,(getValue('cue', deck) ? 0x7F : 0x00));
		sendMidi(uniqueId,0xB0,0,(deck==0)?0x08:0x02,(getValue('play', deck) ? 0x7F : 0x00));
		sendMidi(uniqueId,0xB0,0,(deck==0)?0x15:0x19,(getValue('monitor', deck) ? 0x7F : 0x00));// monitor (audio source)
		sendMidi(uniqueId,0xB0,0,(deck==0)?0x0A:0x04,(getValue('onbeat', deck) ? 0x7F : 0x00));
		sendMidi(uniqueId,0xB0,0,(deck==0)?0x16:0x1A,(getValue('keyLock', deck) ? 0x7F : 0x00));
		call('sleep', 10);// delay for 5ms to allow the MIDI out buffer to flush
		// enable fx mode (first mode)
		toggleMode(uniqueId,deck);
	}
}
//----------------------------------------------------------------------------- 
// called just before the controller will be deinitialized and the control will go away from the script
// use to power off any powered leds, etc
function controllerDeinit(uniqueId)
{
	debug('controllerDeinit ['+uniqueId+']');
	// set leds off
	for(var deck=0; deck<2; deck++)
	{
		sendMidi(uniqueId,0xB0,0,(deck==0)?0x0F:0x10,0x00);// fx
		sendMidi(uniqueId,0xB0,0,(deck==0)?0x0E:0x11,0x00);// cue
		sendMidi(uniqueId,0xB0,0,(deck==0)?0x0D:0x12,0x00);// loop
		call('sleep', 10);
		sendMidi(uniqueId,0xB0,0,(deck==0)?0x09:0x03,0x00);// cue
		sendMidi(uniqueId,0xB0,0,(deck==0)?0x08:0x02,0x00);// play
		sendMidi(uniqueId,0xB0,0,(deck==0)?0x15:0x19,0x00);// monitor (audio source)
		sendMidi(uniqueId,0xB0,0,(deck==0)?0x0A:0x04,0x00);// onbeat
		sendMidi(uniqueId,0xB0,0,(deck==0)?0x16:0x1A,0x00);// keyLock
		call('sleep', 10);// delay for 5ms to allow the MIDI out buffer to flush
	}
}
//----------------------------------------------------------------------------- 
// this is your main target function to implement the controller -- this is called 
// for each and every MIDI message that comes from the controller itself
// return true if the message was processed by the script and false to let it 
// pass thru the software (eg. so it can be assigned by the user using the LEARN function)
function messageReceived(uniqueId,status,channel,data1,data2)
{
	var messageNotProcessed=false;
	var tmp=0;
	switch(status)
	{
	//-----------------------------------------------
	// controllers: sliders, knobs, jog-wheels, encoders ('moving' things)
	case 0xB0:
		switch(data1)
		{
		// global
		case 0x31: call('setCrossfader', data2); break;

		// per deck
		// deck A
		case 0x2E: call('setDeckEqLow', 0, data2); break;
		case 0x2F: call('setDeckEqMid', 0, data2); break;
		case 0x30: call('setDeckEqHigh', 0, data2); break;
		case 0x32: call('setDeckVolume', 0, data2); break;
		case 0x34: call('moveDeckPitch', 0, (data2>0x40 ? data2-0x80 : data2)); break;// increment mode
		case 0x36: // params: (deck, increment, total positions per complete revolution) 
			var val1 = (data2>0x40 ? data2-0x80 : data2);
			if(jogWheelsInScratchMode) call('scratchDeckJW', 0, val1, 45);else call('moveDeckJogWheel', 0, val1, 45);
			break;

		// deck B
		case 0x2B: call('setDeckEqLow', 1, data2); break;
		case 0x2C: call('setDeckEqMid', 1, data2); break;
		case 0x2D: call('setDeckEqHigh', 1, data2); break;
		case 0x33: call('setDeckVolume', 1, data2); break;		
		case 0x35: call('moveDeckPitch', 1, (data2>0x40 ? data2-0x80 : data2)); break;// increment mode
		case 0x37: // params: (deck, increment, total positions per complete revolution) 
			var val1 = (data2>0x40 ? data2-0x80 : data2);
			if(jogWheelsInScratchMode) call('scratchDeckJW', 1, val1, 45);else call('moveDeckJogWheel', 1, val1, 45);
			break;
		
	//-----------------------------------------------
	// note on/off: buttons, switches ('pressing' things)
		// global
		case 0x38:
			tmp = data2 - 0x40;
			if(Math.abs(tmp)<0x3E) break;
			if(tmp>0) call('lbListSelect', 1);else call('lbBrowserSelect', 1);
			break;
		case 0x39:
			tmp = data2 - 0x40;
			if(Math.abs(tmp)<0x3E) break;
			if(tmp>0) call('lbMoveCursor', +1);else call('lbMoveCursor', -1);
			break;

		// per deck
		// deck A
		case 0x07: if(data2>0x3F) toggleMode(uniqueId,0); break;
		
		case 0x0F: // button 1
			if(deckMode[0]==0) call('deckFxFlanger', 0, data2);// fx
			if(deckMode[0]==1) call('deckCuePos1', 0, data2);// cue
			if(deckMode[0]==2) call('deckLoop2', 0, data2);// loop
			break;
		case 0x0E: // button 2
			if(deckMode[0]==0) call('deckFxBeatwaw', 0, data2);// fx
			if(deckMode[0]==1) call('deckCuePos2', 0, data2);// cue
			if(deckMode[0]==2) call('deckLoop4', 0, data2);// loop
			break;
		case 0x0D: // button 3
			if(deckMode[0]==0) call('deckFxEcho', 0, data2);// fx
			if(deckMode[0]==1) call('deckCuePos3', 0, data2);// cue
			if(deckMode[0]==2) call('deckLoop8', 0, data2);// loop
			break;
		
		case 0x14: call('deckBendDown', 0, data2); break;
		case 0x13: call('deckBendUp', 0, data2); break;
		case 0x0B: call('deckSearchLeft', 0, data2); break;
		case 0x0C: call('deckSearchRight', 0, data2); break;		
		case 0x09: call('deckCue', 0, data2); break;
		case 0x08: call('deckPlayPause', 0, data2); break;
		case 0x1D: call('deckMonitor', 0, 0x7F); call('deckMonitor', 0, 0x00); break;// simulate a fast press (since we only receive the release)
		//case 0x1D: call('deckAnalogIn', 0, data2); break;// controlled by the hardware
		case 0x1B: if(data2>0x3F) call('deckLBLoad', 0, data2); break;
		case 0x0A: call('deckSync', 0, data2); break;
		case 0x16: call('deckKeyLock', 0, data2); break;

		// deck B
		case 0x01: if(data2>0x3F) toggleMode(uniqueId,1); break;
		
		case 0x10: // button 1
			if(deckMode[1]==0) call('deckFxFlanger', 1, data2);// fx
			if(deckMode[1]==1) call('deckCuePos1', 1, data2);// cue
			if(deckMode[1]==2) call('deckLoop2', 1, data2);// loop
			break;
		case 0x11: // button 2
			if(deckMode[1]==0) call('deckFxBeatwaw', 1, data2);// fx
			if(deckMode[1]==1) call('deckCuePos2', 1, data2);// cue
			if(deckMode[1]==2) call('deckLoop4', 1, data2);// loop
			break;
		case 0x12: // button 3
			if(deckMode[1]==0) call('deckFxEcho', 1, data2);// fx
			if(deckMode[1]==1) call('deckCuePos3', 1, data2);// cue
			if(deckMode[1]==2) call('deckLoop8', 1, data2);// loop
			break;
		
		case 0x18: call('deckBendDown', 1, data2); break;
		case 0x17: call('deckBendUp', 1, data2); break;
		case 0x05: call('deckSearchLeft', 1, data2); break;
		case 0x06: call('deckSearchRight', 1, data2); break;
		case 0x03: call('deckCue', 1, data2); break;
		case 0x02: call('deckPlayPause', 1, data2); break;
		case 0x1E: call('deckMonitor', 1, 0x7F); call('deckMonitor', 1, 0x00); break;// simulate a fast press (since we only receive the release)
		//case 0x1E: call('deckAnalogIn', 1, data2); break;// controlled by the hardware
		case 0x1C: if(data2>0x3F) call('deckLBLoad', 1, data2); break;
		case 0x04: call('deckSync', 1, data2); break;
		case 0x1A: call('deckKeyLock', 1, data2); break;
				
		default: messageNotProcessed = true;
		}
		break;
	//-----------------------------------------------
	default: messageNotProcessed = true;
	}
	if(messageNotProcessed) debug('messageReceived ['+uniqueId+'] - status:'+status+' channel:'+channel+' data1:'+data1+' data2:'+data2);
	return !messageNotProcessed;
}
//----------------------------------------------------------------------------- 
// this is called by the software whenever something important changes (eg. play status, song changed, etc)
// depending on the notification type it may refer to a global event (deck is -1) or a specific deck (deck >= 0)
// and can contain any/all of the parameters: param 1 is an integer, param 2 is a float, param 3 is a string
function notificationReceived(uniqueId,name,deck,param1,param2,param3)
{
	//debug('notif ['+uniqueId+'] name='+name + ' deck='+deck + ' p1(int)='+param1 + ' p2(float)='+param2 + ' p3(str)='+param3);
	if(deck>=2) return;
	if(name=='cue') 		sendMidi(uniqueId,0xB0,0,(deck==0)?0x09:0x03,(param1 ? 0x7F : 0x00));
	if(name=='play') 		sendMidi(uniqueId,0xB0,0,(deck==0)?0x08:0x02,(param1 ? 0x7F : 0x00));
	if(name=='monitor') 	sendMidi(uniqueId,0xB0,0,(deck==0)?0x15:0x19,(param1 ? 0x7F : 0x00));
	if(name=='onbeat') 		sendMidi(uniqueId,0xB0,0,(deck==0)?0x0A:0x04,(param1 ? 0x7F : 0x00));
	if(name=='keyLock')		sendMidi(uniqueId,0xB0,0,(deck==0)?0x16:0x1A,(param1 ? 0x7F : 0x00));
}
//----------------------------------------------------------------------------- 
// this is called from time to time by the software (several times per second)
// (it is guaranteed that 'timerTick' will get executed ALWAYS right after notificationReceived)
function timerTick(uniqueId)
{
	//debug('timerTick ['+uniqueId+']');
}
//----------------------------------------------------------------------------- 
