--[[-----------------------------------------------------------------------

	Wraith ARS 2X
	Created by WolfKnight

-----------------------------------------------------------------------]]--

READER = {}

--[[----------------------------------------------------------------------------------
	Plate reader variables

	NOTE - This is not a config, do not touch anything unless you know what
	you are actually doing. 
----------------------------------------------------------------------------------]]--
READER.vars = 
{
    -- Whether or not the plate reader's UI is visible 
	displayed = false,

	-- Whether or not the plate reader should be hidden, e.g. the display is active but the player then steps
	-- out of their vehicle
    hidden = false,

    -- The BOLO plate 
    boloPlate = "", 
    
    -- Cameras, this table contains all of the data needed for operation of the front and rear plate reader 
    cams = {
        -- Variables for the front camera
        ["front"] = {
            plate = "",     -- The current plate caught by the reader
            index = "",     -- The index of the current plate
            locked = false  -- If the reader is locked 
        }, 

        -- Variables for the rear camera
        ["rear"] = {
            plate = "",     -- The current plate caught by the reader
            index = "",     -- The index of the current plate
            locked = false  -- If the reader is locked 
        }
    }
}

-- Gets the display state
function READER:GetDisplayState()
	return self.vars.displayed
end 

-- Toggles the display state of the plate reader system
function READER:ToggleDisplayState()
	-- Toggle the display variable 
	self.vars.displayed = not self.vars.displayed 

	-- Send the toggle message to the NUI side 
	SendNUIMessage( { _type = "setReaderDisplayState", state = self:GetDisplayState() } )
end 

-- Sets the display's hidden state to the given state 
function READER:SetDisplayHidden( state )
	self.vars.hidden = state 
end 

-- Returns if the display is hidden 
function READER:GetDisplayHidden()
	return self.vars.hidden 
end

-- Returns the stored plate for the given reader
function READER:GetPlate( cam )
    return self.vars.cams[cam].plate 
end 

-- Sets the plate for the given reader to the given plate 
function READER:SetPlate( cam, plate )
    self.vars.cams[cam].plate = plate 
end 

-- Returns the stored plate index for the given reader
function READER:GetIndex( cam )
    return self.vars.cams[cam].index
end 

-- Sets the plate index for the given reader to the given index
function READER:SetIndex( cam, index )
    self.vars.cams[cam].index = index 
end 

-- Returns the bolo plate
function READER:GetBoloPlate()
    return self.vars.boloPlate
end 

-- Sets the bolo plate to the given plate 
function READER:SetBoloPlate( plate )
    self.vars.boloPlate = plate
    UTIL:Notify( "BOLO plate set to: " .. plate )
end 

-- Returns if the given reader is locked
function READER:GetCamLocked( cam )
    return self.vars.cams[cam].locked
end 

-- Locks the given reader
function READER:LockCam( cam, playAudio )
    -- Check that plate readers can actually be locked
    if ( PLY:VehicleStateValid() and self:CanPerformMainTask() ) then 
        -- Toggle the lock state 
        self.vars.cams[cam].locked = not self.vars.cams[cam].locked

        -- Tell the NUI side to show/hide the lock icon 
        SendNUIMessage( { _type = "lockPlate", cam = cam, state = self:GetCamLocked( cam ) } )

		-- Play a beep 
        if ( self:GetCamLocked( cam ) ) then 
            if ( playAudio ) then 
                SendNUIMessage( { _type = "audio", name = "beep", vol = RADAR:GetSettingValue( "plateAudio" ) } )
            end 
            
            TriggerEvent( "wk:onPlateLocked", cam, self:GetPlate( cam ), self:GetIndex( cam ) )
		end 
    end 
end 

-- Returns if the plate reader system can perform tasks
function READER:CanPerformMainTask()
    return self.vars.displayed and not self.vars.hidden
end 

-- Returns if the given relative position value is for front or rear 
function READER:GetCamFromNum( relPos )
    if ( relPos == 1 ) then 
        return "front"
    elseif ( relPos == -1 ) then 
        return "rear"
    end 
end 

-- Runs when the "Toggle Display" button is pressed on the plate reder box 
RegisterNUICallback( "togglePlateReaderDisplay", function()
	-- Toggle the display state 
	READER:ToggleDisplayState()
end )

-- Runs when the "Set BOLO Plate" button is pressed on the plate reader box
RegisterNUICallback( "setBoloPlate", function( plate, cb )
    -- Set the BOLO plate 
    READER:SetBoloPlate( plate )
end )

-- This is the main function that runs and scans all vehicles in front and behind the patrol vehicle
function READER:Main()
    -- Check that the system can actually run 
    if ( PLY:VehicleStateValid() and self:CanPerformMainTask() ) then 
        -- Loop through front (1) and rear (-1)
        for i = 1, -1, -2 do 
            -- Get the world position of the player's vehicle
            local pos = GetEntityCoords( PLY.veh )

            -- Get a start position 5m in front/behind the player's vehicle
            local start = GetOffsetFromEntityInWorldCoords( PLY.veh, 0.0, ( 5.0 * i ), 0.0 )

            -- Get the end position 40m in front/behind the player's vehicle
            local offset = GetOffsetFromEntityInWorldCoords( PLY.veh, -2.5, ( 50.0 * i ), 0.0 )

            -- Run the ray trace to get a vehicle 
            local veh = UTIL:GetVehicleInDirection( PLY.veh, start, offset )

            -- Get the plate reader text for front/rear
            local cam = self:GetCamFromNum( i )
            
            -- Only proceed to read a plate if the hit entity is a valid vehicle and the current camera isn't locked
            if ( DoesEntityExist( veh ) and IsEntityAVehicle( veh ) and not self:GetCamLocked( cam ) ) then 
                -- Get the heading of the player's vehicle and the hit vehicle
                local ownH = UTIL:Round( GetEntityHeading( PLY.veh ), 0 )
                local tarH = UTIL:Round( GetEntityHeading( veh ), 0 )

                -- Get the relative direction between the player's vehicle and the hit vehicle
                local dir = UTIL:GetEntityRelativeDirection( ownH, tarH )

                -- Only run the rest of the plate check code if we can see the front or rear of the vehicle
                if ( dir > 0 ) then 
                    -- Get the licence plate text from the vehicle 
                    local plate = GetVehicleNumberPlateText( veh )

                    -- Get the licence plate index from the vehicle 
                    local index = GetVehicleNumberPlateTextIndex( veh )

                    -- Only update the stored plate if it's different, otherwise we'd keep sending a NUI message to update the displayed
                    -- plate and image even though they're the same 
                    if ( self:GetPlate( cam ) ~= plate ) then 
                        -- Set the plate for the current reader
                        self:SetPlate( cam, plate )

                        -- Set the plate index for the current reader
                        self:SetIndex( cam, index )

                        -- Automatically lock the plate if the scanned plate matches the BOLO
                        if ( plate == self:GetBoloPlate() ) then 
                            self:LockCam( cam, false )

                            SendNUIMessage( { _type = "audio", name = "plate_hit", vol = RADAR:GetSettingValue( "plateAudio" ) } )
                        end 

                        -- Send the plate information to the NUI side to update the UI
                        SendNUIMessage( { _type = "changePlate", cam = cam, plate = plate, index = index } )

                        -- Trigger the event so developers can hook into the scanner
                        TriggerEvent( "wk:onPlateScanned", cam, plate, index )
                    end 
                end 
            end 
        end 
    end 
end 

-- Main thread
Citizen.CreateThread( function()
    while ( true ) do
        -- Run the main plate reader function
        READER:Main()

        -- Wait half a second
        Citizen.Wait( 500 )
    end 
end )

-- This function is pretty much straight from WraithRS, it does the job so I didn't see the point in not 
-- using it. Hides the radar UI when certain criteria is met, e.g. in pause menu or stepped out ot the 
-- patrol vehicle 
function READER:RunDisplayValidationCheck()
	if ( ( ( PLY.veh == 0 or ( PLY.veh > 0 and not PLY.vehClassValid ) ) and self:GetDisplayState() and not self:GetDisplayHidden() ) or IsPauseMenuActive() and self:GetDisplayState() ) then
		self:SetDisplayHidden( true ) 
		SendNUIMessage( { _type = "setReaderDisplayState", state = false } )
	elseif ( PLY.veh > 0 and PLY.vehClassValid and PLY.inDriverSeat and self:GetDisplayState() and self:GetDisplayHidden() ) then 
		self:SetDisplayHidden( false ) 
		SendNUIMessage( { _type = "setReaderDisplayState", state = true } )
	end 
end

-- Runs the display validation check for the radar
Citizen.CreateThread( function() 
	Citizen.Wait( 100 )

    while ( true ) do 
        -- Run the check 
		READER:RunDisplayValidationCheck()

        -- Wait half a second 
		Citizen.Wait( 500 )
	end 
end )