--[[
---------------------------------------------------
LUXART VEHICLE CONTROL V3 (FOR FIVEM)
---------------------------------------------------
Coded by Lt.Caine
ELS Clicks by Faction
Additional Modification by TrevorBarns
---------------------------------------------------
FILE: cl_utils.lua
PURPOSE: Utilities for siren assignments and tables
		 and other common functions.
---------------------------------------------------
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.  If not, see .
---------------------------------------------------
]]
UTIL = { }
local approved_tones = nil
local tone_options = { }
local tone_table_names_ids = { }
local profile = nil
local tone_main_mem_id = nil
local tone_PMANU_id = nil
local tone_SMANU_id = nil
local tone_AUX_id = nil
local tone_ARHRN_id = nil
---------------------------------------------------------------------
--[[Return sub-table for sirens or plugin settings tables, given veh, and name of whatever setting.]]
function UTIL:GetProfileFromTable(print_name, tbl, veh, ignore_missing_default)
	local ignore_missing_default = ignore_missing_default or false
	local veh_name = GetDisplayNameFromVehicleModel(GetEntityModel(veh))
	local lead_and_trail_wildcard = veh_name:gsub('%d+', '#')
	local lead = veh_name:match('%d*%a+')
	local trail = veh_name:gsub(lead, ''):gsub('%d+', '#')
	local trail_only_wildcard = string.format('%s%s', lead, trail)
	local profile_table, profile
	if tbl ~= nil then
		if tbl[veh_name] ~= nil then							--Does profile exist as outlined in vehicle.meta
			profile_table = tbl[veh_name]
			profile = veh_name
			UTIL:Print(Lang:t('info.profile_found', {ver = STORAGE:GetCurrentVersion(), tbl = print_name, profile = profile, model = veh_name}))
		elseif tbl[trail_only_wildcard] ~= nil then				--Does profile exist using # as wildcard for any trailing digits.
			profile_table = tbl[trail_only_wildcard]
			profile = trail_only_wildcard
			UTIL:Print(Lang:t('info.profile_found', {ver = STORAGE:GetCurrentVersion(), tbl = print_name, profile = profile, model = veh_name}))
		elseif tbl[lead_and_trail_wildcard] ~= nil then			--Does profile exist using # as wildcard for any digits.
			profile_table = tbl[lead_and_trail_wildcard]
			profile = lead_and_trail_wildcard
			UTIL:Print(Lang:t('info.profile_found', {ver = STORAGE:GetCurrentVersion(), tbl = print_name, profile = profile, model = veh_name}))
		else
			if tbl['DEFAULT'] ~= nil then
				profile_table = tbl['DEFAULT']
				profile = 'DEFAULT'
				UTIL:Print(Lang:t('info.profile_default_console', {ver = STORAGE:GetCurrentVersion(), tbl = print_name, model = veh_name}))
				if print_name == 'SIRENS' then
					HUD:ShowNotification(Lang:t('info.profile_default_frontend', {model = veh_name}))
				end
			else
				profile_table = { }
				profile = false
				if not ignore_missing_default then
					UTIL:Print(Lang:t('warning.profile_missing', {ver = STORAGE:GetCurrentVersion(), tbl = print_name, model = veh_name}), true)
				end
			end
		end
	else
		profile_table = { }
		profile = false
		HUD:ShowNotification(Lang:t('error.profile_nil_table', {tbl = print_name}), true)
		UTIL:Print(Lang:t('error.profile_nil_table_console', {ver = STORAGE:GetCurrentVersion(), tbl = print_name}), true)
	end
	
	return profile_table, profile
end
---------------------------------------------------------------------
--[[Shorten oversized  strings in SIREN_ASSIGNMENTS (SIRENS.LUA).
    GTA only allows 11 characters. So to reduce confusion we'll shorten it if the user does not.]]
function UTIL:FixOversizeKeys(TABLE)
	for i, tbl in pairs(TABLE) do
		if string.len(i) > 11 then
			local shortened_gameName = string.sub(i,1,11)
			TABLE[shortened_gameName] = TABLE[i]
			TABLE[i] = nil
		end
	end
end
---------------------------------------------------------------------
--[[Sets profile name and approved_tones table a copy of SIREN_ASSIGNMENTS for this vehicle]]
function UTIL:UpdateApprovedTones(veh)
	approved_tones, profile = UTIL:GetProfileFromTable('SIRENS', SIREN_ASSIGNMENTS, veh)
	
	if profile == false then
		UTIL:Print(Lang:t('error.profile_none_found_console', {game_name = GetDisplayNameFromVehicleModel(GetEntityModel(veh))}), true)
		HUD:ShowNotification(Lang:t('error.profile_none_found_frontend'), true)
	end
	if profile then
		if not UTIL:IsApprovedTone('MAIN_MEM') then
			UTIL:SetToneByPos('MAIN_MEM', 2)
		end
		if not UTIL:IsApprovedTone('PMANU') then
			UTIL:SetToneByPos('PMANU', 2)
		end
		if not UTIL:IsApprovedTone('SMANU') then
			UTIL:SetToneByPos('SMANU', 3)
		end
		if not UTIL:IsApprovedTone('AUX') then
			UTIL:SetToneByPos('AUX', 2)
		end
		if not UTIL:IsApprovedTone('ARHRN') then
			UTIL:SetToneByPos('ARHRN', 1)
		end
	end
end
--[[Getter for approved_tones table, used in RageUI]]
function UTIL:GetApprovedTonesTable()
	if approved_tones == nil then
		if veh ~= nil then
			UpdateApprovedTones(veh)
		else
			UpdateApprovedTones('DEFAULT')
		end
	end
	return approved_tones
end
---------------------------------------------------------------------
--[[Builds a table that we store tone_options in (disabled, button & cycle, cycle only, button only).
    Users can set default option of siren by using optional index .Option in SIREN_ASSIGNMENTS table in SIRENS.LUA]]
function UTIL:BuildToneOptions()
	local temp_array = { }
	local option
	for i, id in pairs(approved_tones) do
		if SIRENS[id] ~= nil then
			option = SIRENS[id].Option or 1
			temp_array[id] = option
		end
	end
	tone_options = temp_array
end
--Setter for single tone_option
function UTIL:SetToneOption(tone_id, option)
	tone_options[tone_id] = option
end
--Getter for single tone_option
function UTIL:GetToneOption(tone_id)
	return tone_options[tone_id]
end
--Getter for tone_options table (used for saving)
function UTIL:GetToneOptionsTable()
	return tone_options
end
---------------------------------------------------------------------
--[[RageUI requires a specific table layout, this builds it according to SIREN_ASSIGNMENTS > approved_tones.]]
function UTIL:GetApprovedTonesTableNameAndID()
	local temp_array = { }
	for i, tone_id in pairs(approved_tones) do
		if i ~= 1 then
			table.insert(temp_array, { Name = SIRENS[tone_id].Name, Value = tone_id } )
		end
	end
	return temp_array
end
---------------------------------------------------------------------
--[[Getter for tone id by passing string abbreviation (MAIN_MEM, PMANU, etc.)]]
function UTIL:GetToneID(tone_string)
	if tone_string == 'MAIN_MEM' then
		return tone_main_mem_id
	elseif tone_string == 'PMANU' then
		return tone_PMANU_id
	elseif tone_string == 'SMANU' then
		return tone_SMANU_id
	elseif tone_string == 'AUX' then
		return tone_AUX_id
	elseif tone_string == 'ARHRN' then
		return tone_ARHRN_id
	end
end
--[[Setter for ToneID by passing string abbreviation of tone (MAIN_MEM, PMANU, etc.) and position of desired tone in approved_tones.]]
function UTIL:SetToneByPos(tone_string, pos)
	if profile then
		if approved_tones[pos] ~= nil then
			if tone_string == 'MAIN_MEM' then
				tone_main_mem_id = approved_tones[pos]
			elseif tone_string == 'PMANU' then
				tone_PMANU_id = approved_tones[pos]
			elseif tone_string == 'SMANU' then
				tone_SMANU_id = approved_tones[pos]
			elseif tone_string == 'AUX' then
				tone_AUX_id = approved_tones[pos]
			elseif tone_string == 'ARHRN' then
				tone_ARHRN_id = approved_tones[pos]
			end
		else
			HUD:ShowNotification(Lang:t('warning.too_few_tone_frontend', {code = 403}), false)
			UTIL:Print(Lang:t('warning.too_few_tone_console', {ver = STORAGE:GetCurrentVersion(), code = 403, tone_string = tone_string, pos = pos}), true)
		end
	else
		HUD:ShowNotification(Lang:t('warning.tone_position_nil_frontend', {code = 404}), false)
		UTIL:Print(Lang:t('warning.tone_position_nil_console', {ver = STORAGE:GetCurrentVersion(), code = 404, tone_string = tone_string, pos = pos}), true)
	end
end
--[[Getter for position of passed tone string. Used in RageUI for P/S MANU and AUX Siren.]]
function UTIL:GetTonePos(tone_string)
	local current_id = UTIL:GetToneID(tone_string)
	for i, tone_id in pairs(approved_tones) do
		if tone_id == current_id then
			return i
		end
	end
	return -1
end
--[[Getter for Tone ID at index/pos in approved_tones]]
function UTIL:GetToneAtPos(pos)
	if approved_tones[pos] ~= nil then
		return approved_tones[pos]
	end
	return nil
end
--[[Setter for ToneID by passing string abbreviation of tone (MAIN_MEM, PMANU, etc.) and specific ID.]]
function UTIL:SetToneByID(tone_string, tone_id)
	if UTIL:IsApprovedTone(tone_id) then
		if tone_string == 'MAIN_MEM' then
			tone_main_mem_id = tone_id
		elseif tone_string == 'PMANU' then
			tone_PMANU_id = tone_id
		elseif tone_string == 'SMANU' then
			tone_SMANU_id = tone_id
		elseif tone_string == 'AUX' then
			tone_AUX_id = tone_id
		elseif tone_string == 'ARHRN' then
			tone_ARHRN_id = tone_id
		end
	else
		HUD:ShowNotification(Lang:t('warning.tone_id_nil_frontend', {ver = STORAGE:GetCurrentVersion()}), false)
		UTIL:Print(Lang:t('warning.tone_id_nil_console', {ver = STORAGE:GetCurrentVersion(), tone_string = tone_string, tone_id = tone_id}), true)
	end
end
---------------------------------------------------------------------
--[[Gets next tone based off vehicle profile and current tone.]]
function UTIL:GetNextSirenTone(current_tone, veh, main_tone, last_pos)
	local main_tone = main_tone or false
	local last_pos = last_pos or nil
	local result
	if last_pos == nil then
		for i, tone_id in pairs(approved_tones) do
			if tone_id == current_tone then
				temp_pos = i
				break
			end
		end
	else
		temp_pos = last_pos
	end
	if temp_pos < #approved_tones then
		temp_pos = temp_pos+1
		result = approved_tones[temp_pos]
	else
		temp_pos = 2
		result = approved_tones[2]
	end
	if main_tone then
		--Check if the tone is set to 'disable' or 'button-only' if so, find next tone
		if tone_options[result] > 2 then
			result = UTIL:GetNextSirenTone(result, veh, main_tone, temp_pos)
		end
	end
	return result
end
---------------------------------------------------------------------
--[[Get count of approved tones used when mapping RegisteredKeys]]
function UTIL:GetToneCount()
	return #approved_tones
end
---------------------------------------------------------------------
--[[Ensure not all sirens are disabled / button only]]
function UTIL:IsOkayToDisable()
	local count = 0
	for i, option in pairs(tone_options) do
		if i ~= 1 then
			if option < 3 then
				count = count + 1
			end
		end
	end
	if count > 1 then
		return true
	end
	return false
end
------------------------------------------------
--[[Handle changing of tone_table custom names]]
function UTIL:ChangeToneString(tone_id, new_name)
	STORAGE:SetCustomToneStrings(true)
	SIRENS[tone_id].Name = new_name
end
------------------------------------------------
--[[Used to verify tone is allowed before playing.]]
function UTIL:IsApprovedTone(tone)
	for i, approved_tone in ipairs(approved_tones) do
		if approved_tone == tone then
			return true
		end
	end
	return false
end
---------------------------------------------------------------------
--[[Returns String  used for saving, loading, and debugging]]
function UTIL:GetVehicleProfileName()
	return profile
end
---------------------------------------------------------------------
--[[Prints to FiveM console, prints more when debug flag is enabled or overridden for important information]]
function UTIL:Print(string, override)
	override = override or false
	if debug_mode or override then
		print(string)
	end
end
---------------------------------------------------------------------
--[[Finds index of element in table given table and element.]]
function UTIL:IndexOf(tbl, tgt)
	for i, v in pairs(tbl) do
		if v == tgt then
			return i
		end
	end
	return nil
end
---------------------------------------------------------------------
--[[This function looks like #!*& for user convenience (and my lack of skill or abundance of laziness),
	it is called when needing to change an extra, it allows users to do things like [''] = { Brake = 1 } while
	also allowing advanced users to write configs like this [''] = { Brake = { add = { 3, 4 }, remove = { 5, 6 }, repair = true } }
	which can add and remove multiple different extras at once and adds flag to repair the vehicle
	for extras that are too large and require the vehicle to be reloaded. Once it figures out the
	users config layout it calls itself again (recursive) with the id we actually need toggled right now.]]
function UTIL:TogVehicleExtras(veh, extra_id, state, repair)
	local repair = repair or false
	if type(extra_id) == 'table' then
		-- Toggle Same Extras Mode
		if extra_id.toggle ~= nil then
			-- Toggle Multiple Extras
			if type(extra_id.toggle) == 'table' then
				for i, singe_extra_id in ipairs(extra_id.toggle) do
					UTIL:TogVehicleExtras(veh, singe_extra_id, state, extra_id.repair)
				end
			-- Toggle a Single Extra (no table)
			else
				UTIL:TogVehicleExtras(veh, extra_id.toggle, state, extra_id.repair)
			end
		-- Toggle Different Extras Mode
		elseif extra_id.add ~= nil and extra_id.remove ~= nil then
			if type(extra_id.add) == 'table' then
				for i, singe_extra_id in ipairs(extra_id.add) do
					UTIL:TogVehicleExtras(veh, singe_extra_id, state, extra_id.repair)
				end
			else
				UTIL:TogVehicleExtras(veh, extra_id.add, state, extra_id.repair)
			end
			if type(extra_id.remove) == 'table' then
				for i, singe_extra_id in ipairs(extra_id.remove) do
					UTIL:TogVehicleExtras(veh, singe_extra_id, not state, extra_id.repair)
				end
			else
				UTIL:TogVehicleExtras(veh, extra_id.remove, not state, extra_id.repair)
			end
		end
	else
		if state then
			if not IsVehicleExtraTurnedOn(veh, extra_id) then
				local doors =  { }
				if repair then
					for i = 0,6 do
						doors[i] = GetVehicleDoorAngleRatio(veh, i)
					end
				end
				SetVehicleAutoRepairDisabled(veh, not repair)
				SetVehicleExtra(veh, extra_id, false)
				UTIL:Print(Lang:t('info.extra_on', {extra = extra_id}), false)
				SetVehicleAutoRepairDisabled(veh, false)
				if repair then
					for i = 0,6 do
						if doors[i] > 0.0 then
							SetVehicleDoorOpen(veh, i, true, false)
						end
					end
				end
			end
		else
			if IsVehicleExtraTurnedOn(veh, extra_id) then
				SetVehicleExtra(veh, extra_id, true)
				UTIL:Print(Lang:t('info.extra_off', {extra = extra_id}), false)
			end
		end
	end
	SetVehicleAutoRepairDisabled(veh, false)
end