/*-------------------------------------------------------------------------

	Wraith ARS 2X
	Created by WolfKnight

	This JS file takes inspiration from RandomSean's RS9000 JS file, so 
	thanks to him!
	
-------------------------------------------------------------------------*/

/*------------------------------------------------------------------------------------
	Variables
------------------------------------------------------------------------------------*/
var resourceName; 
var uiEdited = false;

// All of the audio file names
const audioNames = 
{
    // Beeps
	beep: "beep.ogg",
	xmit_on: "xmit_on.ogg",
	xmit_off: "xmit_off.ogg",
    done: "done.ogg", 
    
    // Verbal lock 
    front: "front.ogg", 
    rear: "rear.ogg", 
    closing: "closing.ogg", 
    away: "away.ogg",

    // Plate reader
    plate_hit: "plate_hit.ogg"
}

// Defines which audio needs to play for which direction 
const lockAudio = 
{
    front: { 
        1: "away",
        2: "closing"
    }, 

    rear: {
        1: "closing", 
        2: "away"
    }
}

// Setup the main const element structure, this way we can easily access elements without having the mess
// that was in the JS file for WraithRS
const elements = 
{
	radar: $( "#radarFrame" ),
    remote: $( "#rc" ), 
    plateReader: $( "#plateReaderFrame" ),

	pwrBtn: $( "#pwrBtn" ), 

	uiSettingsBtn: $( "#uiSettings" ), 
	uiSettingsBox: $( "#uiSettingsBox" ), 
    closeUiBtn: $( "#closeUiSettings" ),
    
    plateReaderBtn: $( "#plateReaderBtn" ), 
    plateReaderBox: $( "#plateReaderBox" ), 
    boloText: $( "#boloText" ), 
    setBoloBtn: $( "#setBoloPlate" ), 
    closePrBtn: $( "#closePlateReaderSettings" ),

    openHelp: $( "#helpBtn" ), 
    helpWindow: $( "#helpWindow" ), 
    helpWeb: $( "#helpWeb" ), 
    closeHelp: $( "#closeHelp" ), 

	radarScaling: {
		increase: $( "#radarIncreaseScale" ),
		decrease: $( "#radarDecreaseScale" ),
		display: $( "#radarScaleDisplay" )
    }, 

    remoteScaling: {
        increase: $( "#remoteIncreaseScale" ),
		decrease: $( "#remoteDecreaseScale" ),
		display: $( "#remoteScaleDisplay" )
    },

    plateReaderScaling: {
        increase: $( "#readerIncreaseScale" ),
		decrease: $( "#readerDecreaseScale" ),
		display: $( "#readerScaleDisplay" )
    },

    plates: {
        front: {
            text: $( "#frontPlateText" ),
            fill: $( "#frontPlateTextFill" ),
            lolite: $( "#frontPlateTextLolite" ),
            img: $( "#frontPlate" ), 
            lock: $( "#frontPlateLock" )
        },

        rear: {
            text: $( "#rearPlateText" ), 
            fill: $( "#rearPlateTextFill" ), 
            lolite: $( "#rearPlateTextLolite" ),
            img: $( "#rearPlate" ), 
            lock: $( "#rearPlateLock" )
        }
    },

    safezoneSlider: $( "#safezone" ), 
    safezoneDisplay: $( "#safezoneDisplay" ),
    
    keyLock: {
        label: $( "#keyLockLabel" ), 
        stateLabel: $( "#keyLockStateLabel" )
    },

	patrolSpeed: $( "#patrolSpeed" ),

	antennas: {
		front: {
			targetSpeed: $( "#frontSpeed" ),
			fastSpeed: $( "#frontFastSpeed" ),

			dirs: {
				fwd: $( "#frontDirAway" ),
				bwd: $( "#frontDirTowards" ),
				fwdFast: $( "#frontFastDirAway" ), 
				bwdFast: $( "#frontFastDirTowards" )
			},

			modes: {
				same: $( "#frontSame" ),
				opp: $( "#frontOpp" ),
				xmit: $( "#frontXmit" )
			},

			fast: {
				fastLabel: $( "#frontFastLabel" ),
				lockLabel: $( "#frontFastLockLabel" )
			}
		},

		rear: {
			targetSpeed: $( "#rearSpeed" ),
			fastSpeed: $( "#rearFastSpeed" ),

			dirs: {
				fwd: $( "#rearDirTowards" ),
				bwd: $( "#rearDirAway" ), 
				fwdFast: $( "#rearFastDirTowards" ), 
				bwdFast: $( "#rearFastDirAway" )
			},

			modes: {
				same: $( "#rearSame" ),
				opp: $( "#rearOpp" ),
				xmit: $( "#rearXmit" )
			},

			fast: {
				fastLabel: $( "#rearFastLabel" ),
				lockLabel: $( "#rearFastLockLabel" )
			}
		}
	}
}

// Antenna mode values
const modes = 
{
	off: 0, 
	same: 1, 
	opp: 2,
	both: 3
}

// Antenna direction values
const dirs = 
{
	none: 0, 
	closing: 1,
	away: 2
}


/*------------------------------------------------------------------------------------
	Hide elements
------------------------------------------------------------------------------------*/
elements.radar.hide(); 
elements.remote.hide(); 
elements.plateReader.hide(); 
elements.plateReaderBox.hide(); 
elements.uiSettingsBox.hide(); 
elements.keyLock.label.hide(); 
elements.helpWindow.hide(); 

// Sets the action for the "UI SETTINGS" button on the remote to open the UI settings box
elements.uiSettingsBtn.click( function() {
    setEleVisible( elements.uiSettingsBox, true ); 
} )

// Sets the action for the "PLATE READER" button on the remote to open the plate reader box
elements.plateReaderBtn.click( function() {
    setEleVisible( elements.plateReaderBox, true ); 
} )

// Sets the action for the "HELP" button on the remote to open the help window and load the web page 
elements.openHelp.click( function() {
    setEleVisible( elements.helpWindow, true ); 
    loadHelp( true );
} )

// Sets the action for the "Close Help" button under the help window to close the help window and unload the web page 
elements.closeHelp.click( function() {
    setEleVisible( elements.helpWindow, false ); 
    loadHelp( false ); 
} )


/*------------------------------------------------------------------------------------
	Setters
------------------------------------------------------------------------------------*/
// Sets the visibility of an element to the given state 
function setEleVisible( ele, state )
{
    state ? ele.fadeIn() : ele.fadeOut(); 
}

// Changes the class of the given element so it looks lit up 
function setLight( ant, cat, item, state )
{
    // Grab the obj element from the elements table
	let obj = elements.antennas[ant][cat][item]; 

    // Either add the active class or remove it 
	if ( state ) {
        obj.addClass( cat == "dirs" ? "active_arrow" : "active" ); 
	} else {
        obj.removeClass( cat == "dirs" ? "active_arrow" : "active" );
	}
}

// Sets the XMIT state of an antenna based on the passed state, makes the fast box display "HLd"
// when the state is false
function setAntennaXmit( ant, state )
{
    // Set the light state of the antenna's XMIT icon
	setLight( ant, "modes", "xmit", state ); 

    // Clear the antenna's directional arrows and speeds, display "HLd" in the fast box
	if ( !state ) {
		clearDirs( ant ); 
		elements.antennas[ant].targetSpeed.html( "¦¦¦" );
        elements.antennas[ant].fastSpeed.html( "HLd" ); 
        
    // Blank the fast box when the antenna is set to transmit 
	} else {
		elements.antennas[ant].fastSpeed.html( "¦¦¦" ); 
	}
}

// Sets the mode lights for the given antenna
function setAntennaMode( ant, mode )
{
    // Light up the 'same' led if the given mode is the same mode, otherwise blank it 
    setLight( ant, "modes", "same", mode == modes.same );
    
    // Light up the 'opp' led if the given mode is the opp mode, otherwise blank it 
	setLight( ant, "modes", "opp", mode == modes.opp );
}

// Sets the fast light for the given antenna
function setAntennaFastMode( ant, state )
{
    // Lighten or dull the fast led based on the given state
	setLight( ant, "fast", "fastLabel", state );
}

// Sets the lock light for the given antenna
function setAntennaLock( ant, state )
{
    // Lighten or dull the lock led based on the given state
    setLight( ant, "fast", "lockLabel", state ); 
}

// Sets the directional arrows light for the given antenna
function setAntennaDirs( ant, dir, fastDir )
{
    // Target forward
    setLight( ant, "dirs", "fwd", dir == dirs.closing );
    
    // Target backward 
	setLight( ant, "dirs", "bwd", dir == dirs.away );

    // Fast forward
    setLight( ant, "dirs", "fwdFast", fastDir == dirs.closing );
    
    // Fast backward
	setLight( ant, "dirs", "bwdFast", fastDir == dirs.away );
}

// sets the plate lock light for the given plate reader
function setPlateLock( cam, state )
{
    // Get the plate reader lock object 
    let obj = elements.plates[cam].lock; 

    // Add or remove the active class
	if ( state ) {
        obj.addClass( "active" ); 
	} else {
        obj.removeClass( "active" );
	}
}

// Sets the license text and plate image of the given plate reader
function setPlate( cam, plate, index )
{
    // Get the plate items
    let pl = elements.plates[cam]; 

    // Change the plate image 
    pl.img.attr( "src", "images/plates/" + index + ".png" );

    // Change the plate text colour depending on the plate itself
    ( index == 1 || index == 2 ) ? pl.fill.removeClass( "plate_blue" ).addClass( "plate_yellow" ) : pl.fill.removeClass( "plate_yellow" ).addClass( "plate_blue" ); 
    
    // If the plate is black or blue then we hide the lolite effect 
    ( index == 1 || index == 2 ) ? pl.lolite.hide() : pl.lolite.show(); 

    // Update all of the p elements with the new plate 
    pl.text.find( "p" ).each( function( i, obj ) {
        $( this ).html( plate );
    } );
}


/*------------------------------------------------------------------------------------
	Clearing functions 
------------------------------------------------------------------------------------*/
// Clears the same, opp, fast, and lock leds for the given antenna
function clearModes( ant )
{
    // Iterate through the modes and clear them 
	for ( let i in elements.antennas[ant].modes )
	{
		elements.antennas[ant].modes[i].removeClass( "active" ); 
	}

    // Iterate through the fast leds and clear them 
	for ( let a in elements.antennas[ant].fast )
	{
		elements.antennas[ant].fast[a].removeClass( "active" ); 
	}
}

// Clears the directional arrows for the given antenna
function clearDirs( ant )
{
    // Iterate through the directional arrows and clear them
	for ( let i in elements.antennas[ant].dirs )
	{
		elements.antennas[ant].dirs[i].removeClass( "active_arrow" ); 
	}
}

// Clears all of the elements of the given antenna
function clearAntenna( ant )
{
    // Clear the modes 
    clearModes( ant );
    
    // Clear the directional arrows
	clearDirs( ant );

    // Blank the target speed box 
    elements.antennas[ant].targetSpeed.html( "¦¦¦" );
    
    // Blank the fast speed box
	elements.antennas[ant].fastSpeed.html( "¦¦¦" );
}

// Clears all the elements on the radar's UI
function clearEverything()
{
    // Blank the patrol speed
	elements.patrolSpeed.html( "¦¦¦" ); 

    // Blank both the antennas
	for ( let i in elements.antennas ) 
	{
		clearAntenna( i ); 
	}
}


/*------------------------------------------------------------------------------------
	Radar power functions
------------------------------------------------------------------------------------*/
// Simulates the radar unit powering up by lighting all of the elements 
function poweringUp()
{
    // Set the patrol speed container to be fully lit
	elements.patrolSpeed.html( "888" ); 

    // Iterate through the front and rear antenna elements
	for ( let i of [ "front", "rear" ] )
	{
        // Get the antenna object to shorten the target reference
		let e = elements.antennas[i];

        // Set the target and fast speed box to be fully lit
		e.targetSpeed.html( "888" ); 
		e.fastSpeed.html( "888" ); 

        // Iterate through the rest of the antenna's elements 
		for ( let a of [ "dirs", "modes", "fast" ] )
		{
            // Iterate through the objects for the current category and add the active class
			for ( let obj in e[a] )
			{
				a == "dirs" ? e[a][obj].addClass( "active_arrow" ) : e[a][obj].addClass( "active" ); 
			}
		}
	}
} 

// Simulates the 'fully powered' state of the radar unit 
function poweredUp()
{
    // Completely clear everything
	clearEverything(); 

    // Activate the 'fast' led for both antennas, and make sure the xmit led is off
	for ( let ant of [ "front", "rear" ] )
	{
        // Even though the clearEverything() function is called above, we run this so the fast window
        // displays 'HLd'
		setAntennaXmit( ant, false );
		setAntennaFastMode( ant, true );    
	}
}

// Runs the startup process or clears everything, the Lua side calls for the full powered up state
function radarPower( state )
{
	state ? poweringUp() : clearEverything();
}


/*------------------------------------------------------------------------------------
	Audio 
------------------------------------------------------------------------------------*/
// Plays the given audio file name from the audioNames list at the given volume 
function playAudio( name, vol )
{
    // Create the new audio object
    let audio = new Audio( "sounds/" + audioNames[name] );
    
    // Set the volume 
    audio.volume = vol; 
    
    // Play the audio clip 
	audio.play();
}

// Plays the verbal lock, this is a separate from the function above as it plays two sounds with a delay
function playLockAudio( ant, dir, vol )
{
    // Play the front/rear sound
    playAudio( ant, vol ); 

    // If the vehicle was closing or away, play that sound too 
    if ( dir > 0 ) 
    {
        setTimeout( function() {
            playAudio( lockAudio[ant][dir], vol ); 
        }, 500 );
    }
}


/*------------------------------------------------------------------------------------
	Radar updating  
------------------------------------------------------------------------------------*/
// Updates patrol speed as well as the speeds and directional arrows for the given antenna
function updateDisplays( ps, ants )
{
    // Update the patrol speed
	elements.patrolSpeed.html( ps );

    // Iterate through the antenna data 
	for ( let ant in ants ) 
	{
        // Make sure there is actually data for the current antenna data
		if ( ants[ant] != null ) {
            // Grab the antenna element from the elements table
			let e = elements.antennas[ant]; 

            // Update the target and fast speeds
			e.targetSpeed.html( ants[ant][0].speed ); 
			e.fastSpeed.html( ants[ant][1].speed ); 

            // Update the directional arrows
			setAntennaDirs( ant, ants[ant][0].dir, ants[ant][1].dir );
		}
	}
}

// Updates all of the mode leds on the radar interface
function settingUpdate( ants )
{
	for ( let ant in ants )
	{
		setAntennaXmit( ant, ants[ant].xmit );
		setAntennaMode( ant, ants[ant].mode ); 
        setAntennaFastMode( ant, ants[ant].fast );  
		setAntennaLock( ant, ants[ant].speedLocked );
	}
}


/*------------------------------------------------------------------------------------
	Misc
------------------------------------------------------------------------------------*/
// Displays the given option text and current option value on the radar
function menu( optionText, option )
{
    // Clear everything 
	clearEverything(); 

    // Set the target and fast box to the option text
	elements.antennas.front.targetSpeed.html( optionText[0] );
	elements.antennas.front.fastSpeed.html( optionText[1] );

    // Set the patrol speed to the value 
	elements.patrolSpeed.html( option );
}

// Makes the key lock label fade in then fade out after 2 seconds
function displayKeyLock( state )
{
    // Set the state label text to enabled or disabled
    elements.keyLock.stateLabel.html( state ? "enabled" : "disabled" );

    // Fade in the label 
    elements.keyLock.label.fadeIn();

    // Make the label fade out after 2 seconds
    setTimeout( function() {
        elements.keyLock.label.fadeOut();
    }, 2000 ); 
}

// This function is used to send data back through to the LUA side 
function sendData( name, data ) {
	$.post( "http://" + resourceName + "/" + name, JSON.stringify( data ), function( datab ) {
		if ( datab != "ok" ) {
			console.log( datab );
		}            
	} );
}

// Sets the ui edited variable to the given state, this is used in the UI save system 
function setUiHasBeenEdited( state )
{
    uiEdited = state; 
}

// Returns if the UI has been edited
function hasUiBeenEdited()
{
    return uiEdited;
}

// Gathers the UI data and sends it to the Lua side
function sendSaveData()
{
    // Make sure we only collect and send the UI data if it has been edited 
    if ( hasUiBeenEdited() ) {
        let data = 
        {
            remote: {
                left: elements.remote.css( "left" ),
                top: elements.remote.css( "top" ),
                scale: remoteScale
            },

            radar: {
                left: elements.radar.css( "left" ),
                top: elements.radar.css( "top" ),
                scale: radarScale
            },

            plateReader: {
                left: elements.plateReader.css( "left" ),
                top: elements.plateReader.css( "top" ),
                scale: readerScale
            },

            safezone: safezone 
        }

        // Send the data
        sendData( "saveUiData", data );
    }
}

// Loads the UI settings 
function loadUiSettings( data )
{
    // Iterate through "remote", "radar" and "plateReader"
    for ( let setting of [ "remote", "radar", "plateReader" ] ) 
    {
        // Iterate through the settings
        for ( let i of [ "left", "top" ] )
        {
            // Update the position of the current element 
            elements[setting].css( i, data[setting][i] );
        }

        // Set the scale and update the display
        setScaleAndDisplay( elements[setting], data[setting].scale, elements[setting + "Scaling"].display ); 
    }

    // Update the remote, radar and reader scale variables
    remoteScale = data.remote.scale; 
    radarScale = data.radar.scale; 
    readerScale = data.plateReader.scale;

    // Set the safezone and update the display
    elements.safezoneSlider.val( data.safezone );
    elements.safezoneSlider.trigger( "input" ); 
}

// Sets the on click function for the set BOLO plate button
elements.setBoloBtn.click( function() {
    // Grab the value of the text input box
    let plate = elements.boloText.val().toUpperCase();

    // Send the plate to the Lua side
    sendData( "setBoloPlate", plate ); 
} )

// Checks what the user is typing into the plate box
function checkPlateInput( event )
{
    // See if what has been typed is a valid key, GTA only seems to like A-Z and 0-9
    let valid = /[a-zA-Z0-9 ]/g.test( event.key ); 

    // If the key is not valid, prevent the key from being input into the box
    if ( !valid ) {
        event.preventDefault(); 
    }
}

// Sets the src of the in-game help element, when true it loads the manual, when false it blanks the element
function loadHelp( state )
{
    if ( state ) {
        elements.helpWeb.attr( "src", "https://wolfknight98.github.io/wk_wars2x_web/manual.pdf" ); 
    } else {
        elements.helpWeb.attr( "src", "about:blank" ); 
    }
}


/*------------------------------------------------------------------------------------
    UI scaling and positioning 
    
    This whole bit could most likely be streamlined and made more efficient, it 
    works for now though. Redo it at a later date.
------------------------------------------------------------------------------------*/
var remoteScale = 1.0;
var remoteMoving = false; 
var remoteOffset = [ 0, 0 ]; 

var radarScale = 1.0;
var radarMoving = false; 
var radarOffset = [ 0, 0 ]; 

var readerScale = 1.0; 
var readerMoving = false;
var readerOffset = [ 0, 0 ]; 

var windowWidth = 0; 
var windowHeight = 0; 
var safezone = 0; 

// Close the UI settings window when the 'Close' button is pressed
elements.closeUiBtn.click( function() {
    setEleVisible( elements.uiSettingsBox, false );
} )

// Close the plate reader settings window when the 'Close' button is pressed
elements.closePrBtn.click( function() {
    setEleVisible( elements.plateReaderBox, false ); 
} )

// Set the remote scale buttons to change the remote's scale 
elements.remoteScaling.increase.click( function() {
    remoteScale = changeEleScale( elements.remote, remoteScale, 0.05, elements.remoteScaling.display ); 
} )

elements.remoteScaling.decrease.click( function() {
    remoteScale = changeEleScale( elements.remote, remoteScale, -0.05, elements.remoteScaling.display ); 
} )

// Set the radar scale buttons to change the radar's scale 
elements.radarScaling.increase.click( function() {
    radarScale = changeEleScale( elements.radar, radarScale, 0.05, elements.radarScaling.display ); 
} )

elements.radarScaling.decrease.click( function() {
    radarScale = changeEleScale( elements.radar, radarScale, -0.05, elements.radarScaling.display ); 
} )

// Set the reader scale buttons to change the reader's scale 
elements.plateReaderScaling.increase.click( function() {
    readerScale = changeEleScale( elements.plateReader, readerScale, 0.05, elements.plateReaderScaling.display ); 
} )

elements.plateReaderScaling.decrease.click( function() {
    readerScale = changeEleScale( elements.plateReader, readerScale, -0.05, elements.plateReaderScaling.display ); 
} )

// Remote mouse down and up event
elements.remote.mousedown( function( event ) {
    remoteMoving = true; 

    let offset = $( this ).offset();

    remoteOffset = getOffset( offset, event.clientX, event.clientY );
} )

// Radar mouse down and up event
elements.radar.mousedown( function( event ) {
    radarMoving = true; 

    let offset = $( this ).offset();

    radarOffset = getOffset( offset, event.clientX, event.clientY );
} )

// Plate reader mouse down and up event
elements.plateReader.mousedown( function( event ) {
    readerMoving = true; 

    let offset = $( this ).offset();

    readerOffset = getOffset( offset, event.clientX, event.clientY );
} )

$( document ).mouseup( function( event ) {
    // Reset the remote and radar moving variables
    remoteMoving = false; 
    radarMoving = false; 
    readerMoving = false;
} )

$( document ).mousemove( function( event ) {
    let x = event.clientX; 
    let y = event.clientY; 

    if ( remoteMoving )
    {
        event.preventDefault();

        calculatePos( elements.remote, x, y, windowWidth, windowHeight, remoteOffset, remoteScale, safezone );
    }

    if ( radarMoving )
    {
        event.preventDefault(); 

        calculatePos( elements.radar, x, y, windowWidth, windowHeight, radarOffset, radarScale, safezone );
    }

    if ( readerMoving )
    {
        event.preventDefault(); 

        calculatePos( elements.plateReader, x, y, windowWidth, windowHeight, readerOffset, readerScale, safezone );
    }
} )

$( window ).resize( function() {
    windowWidth = $( this ).width(); 
    windowHeight = $( this ).height(); 
} )

$( document ).ready( function() {
    windowWidth = $( window ).width(); 
    windowHeight = $( window ).height();
} )

elements.safezoneSlider.on( "input", function() {
    let val = $( this ).val();
    safezone = parseInt( val, 10 ); 

    elements.safezoneDisplay.html( val + "px" ); 
} )

function calculatePos( ele, x, y, w, h, offset, scale, safezone )
{
    let eleWidth = ( ele.outerWidth() * scale );
    let eleHeight = ( ele.outerHeight() * scale );
    let eleWidthPerct = ( eleWidth / w ) * 100; 
    let eleHeightPerct = ( eleHeight / h ) * 100; 
    let eleWidthPerctHalf = eleWidthPerct / 2; 
    let eleHeightPerctHalf = eleHeightPerct / 2;

    let maxWidth = w - eleWidth;
    let maxHeight = h - eleHeight; 

    let left = clamp( x + offset[0], 0 + safezone, maxWidth - safezone );
    let top = clamp( y + offset[1], 0 + safezone, maxHeight - safezone );

    let leftPos = ( left / w ) * 100; 
    let topPos = ( top / h ) * 100; 

    let leftLockGap = leftPos + eleWidthPerctHalf; 
    let topLockGap = topPos + eleHeightPerctHalf;

    // Lock pos check 
    if ( leftLockGap >= 49.0 && leftLockGap <= 51.0 ) 
    {
        leftPos = 50.0 - eleWidthPerctHalf; 
    }

    if ( topLockGap >= 49.0 && topLockGap <= 51.0 ) 
    {
        topPos = 50.0 - eleHeightPerctHalf; 
    }

    updatePosition( ele, leftPos, topPos );
    setUiHasBeenEdited( true ); 
}

function updatePosition( ele, left, top )
{
    ele.css( "left", left + "%" );
    ele.css( "top", top + "%" );
}

function getOffset( offset, x, y )
{
    return [
        offset.left - x, 
        offset.top - y
    ]
}

function changeEleScale( ele, scaleVar, amount, display )
{
    // Change the scale of the element and update it's displayer
    let scale = changeScale( ele, scaleVar, amount ); 
    display.html( scale.toFixed( 2 ) + "x" );

    // Tell the system the UI has been edited
    setUiHasBeenEdited( true ); 

    return scale; 
}

function changeScale( ele, current, amount )
{
    let scale = clamp( current + amount, 0.25, 2.5 ); 
    ele.css( "transform", "scale(" + scale + ")" );

    return scale; 
}

function setScaleAndDisplay( ele, scale, display )
{
    ele.css( "transform", "scale(" + scale + ")" );
    display.html( scale.toFixed( 2 ) + "x" );
}

function clamp( num, min, max )
{
	return num < min ? min : num > max ? max : num;
}


/*------------------------------------------------------------------------------------
	Button click event assigning 
------------------------------------------------------------------------------------*/
// This runs when the JS file is loaded, loops through all of the remote buttons and assigns them an onclick function
// elements.remote.find( "button" ).each( function( i, obj ) {
$( "body" ).find( "button, div" ).each( function( i, obj ) {
	if ( $( this ).attr( "data-nuitype" ) ) {
		$( this ).click( function() { 
			let type = $( this ).data( "nuitype" ); 
			let value = $( this ).attr( "data-value" ) ? $( this ).data( "value" ) : null; 
			let mode = $( this ).attr( "data-mode" ) ? $( this ).data( "mode" ) : null; 

			sendData( type, { value, mode } ); 
		} )
	}
} );


/*------------------------------------------------------------------------------------
    Close the remote when the user presses the 'Escape' key or the right mouse button 
------------------------------------------------------------------------------------*/
function closeRemote()
{
    sendData( "closeRemote", null );

    setEleVisible( elements.plateReaderBox, false ); 
    setEleVisible( elements.uiSettingsBox, false ); 
    setEleVisible( elements.helpWindow, false );
    loadHelp( false ); 

    setEleVisible( elements.remote, false );
    
    sendSaveData(); 
}

$( document ).keyup( function( event ) {
    if ( event.keyCode == 27 ) 
	{
		closeRemote();
	}
} );

$( document ).contextmenu( function() {
    closeRemote(); 
} );


/*------------------------------------------------------------------------------------
    The main event listener, this is where the NUI messages sent by the LUA side arrive 
    at, they are then handled properly via a switch/case that runs the relevant code
------------------------------------------------------------------------------------*/
window.addEventListener( "message", function( event ) {
	var item = event.data; 
    var type = event.data._type; 

	switch ( type ) {
        // System events 
		case "updatePathName":
			resourceName = item.pathName
            break;
        case "loadUiSettings":
            loadUiSettings( item.data );
            break;
        case "displayKeyLock":
            displayKeyLock( item.state );
            break; 

        // Radar events
		case "openRemote":
            setEleVisible( elements.remote, true ); 
            setUiHasBeenEdited( false ); 
			break; 
		case "setRadarDisplayState":
            setEleVisible( elements.radar, item.state ); 
			break; 
		case "radarPower":
			radarPower( item.state );
			break; 
		case "poweredUp":
			poweredUp();
			break;
		case "update":
			updateDisplays( item.speed, item.antennas );
			break; 
		case "antennaXmit":
			setAntennaXmit( item.ant, item.on );
			break; 
		case "antennaMode":
			setAntennaMode( item.ant, item.mode ); 
			break; 
		case "antennaLock":
            setAntennaLock( item.ant, item.state );
            break; 
        case "antennaFast":
            setAntennaFastMode( item.ant, item.state ); 
            break; 
		case "menu":
			menu( item.text, item.option ); 
			break;
		case "settingUpdate":
			settingUpdate( item.antennaData ); 
            break; 

        // Plate reader events
        case "setReaderDisplayState":
            setEleVisible( elements.plateReader, item.state ); 
            break; 
        case "changePlate":
            setPlate( item.cam, item.plate, item.index );
            break;
        case "lockPlate":
            setPlateLock( item.cam, item.state ); 
            break; 
            
        // Audio events
		case "audio":
			playAudio( item.name, item.vol ); 
            break; 
        case "lockAudio":
            playLockAudio( item.ant, item.dir, item.vol ); 
            break; 
        
        // default
		default:
			break;
	}
} );