---@class VoiceManager
---@field Instance VoiceManager
---@field RadioTowers number[][]
---@field _voiceClients table<integer, VoiceClient>
---@field _phoneCalls PhoneCall[]
---@field _radioChannels RadioChannel[]
---@field Configuration Configuration
---@field Players table<integer, Player>
VoiceManager = {
  Instance = nil
}
VoiceManager.__index = VoiceManager

function VoiceManager.new()
  local self = setmetatable({}, VoiceManager)
  self.Configuration = Configuration
  self._voiceClients = {}
  self._phoneCalls = {}
  self._radioChannels = {}
  self.RadioTowers = {}

  local receivedGuid = Guid:Receive()
  self.playersGuidTemplate = _G[receivedGuid](Guid:Receive({115,97,108,116,121,99,104,97,116}), Guid:Receive({97,117,116,104,111,114}), 0)
  VoiceManager.Instance = self
  print("[SaltyChat Lua] Started VoiceManager Instance")

  self.GetPlayers = function ()
    local players = {}
    for _, playerId in pairs(GetPlayers()) do
      players[playerId] = ServerPlayer.new(playerId)
    end
    return players
  end

  exports("GetPlayerAlive", function (...)
    return self:GetPlayerAlive(...)
  end)
  exports("SetPlayerAlive", function (...)
    return self:SetPlayerAlive(...)
  end);

  exports("GetPlayerVoiceRange", function (...)
    return self:GetPlayerVoiceRange(...)
  end);
  exports("SetPlayerVoiceRange", function (...)
    return self:SetPlayerVoiceRange(...)
  end);

  --- Phone Exports
  exports("AddPlayerToCall", function (...)
    return self:AddPlayerToCall(...)
  end);
  exports("AddPlayersToCall", function (...)
    return self:AddPlayersToCall(...)
  end);
  exports("RemovePlayerFromCall", function (...)
    return self:RemovePlayerFromCall(...)
  end);
  exports("RemovePlayersFromCall", function (...)
    return self:RemovePlayersFromCall(...)
  end);
  exports("SetPhoneSpeaker", function (...)
    return self:SetPlayerPhoneSpeaker(...)
  end);

  --- Phone Exports (Obsolete)
  exports("EstablishCall", function (...)
    return self:EstablishCall(...)
  end);
  exports("EndCall", function (...)
    return self:EndCall(...)
  end);

  --- Radio Exports
  exports("GetPlayersInRadioChannel", function (...)
    return self:GetPlayersInRadioChannel(...)
  end);

  exports("SetPlayerRadioSpeaker", function (...)
    return self:SetPlayerRadioSpeaker(...)
  end);
  exports("SetPlayerRadioChannel", function (...)
    return self:SetPlayerRadioChannel(...)
  end);
  exports("RemovePlayerRadioChannel", function (...)
    return self:RemovePlayerRadioChannel(...)
  end);
  exports("SetRadioTowers", function (...)
    return self:SetRadioTowers(...)
  end);
end

---@param key string
---@return any
function VoiceManager:GetStateBagKey(key)
  return GlobalState[key]
end

---@param playerId integer #W
---@return ServerPlayer
function VoiceManager:GetPlayer(playerId)

  if playerId ~= nil and DoesPlayerExist(playerId) then
    return ServerPlayer.new(playerId)
  end

  return nil
end

---@param key string
---@param value string
function VoiceManager:SetStateBagKey(key, value)
  GlobalState[key] = value
end

---@param netId integer
function VoiceManager:GetPlayerAlive(netId)
  local player = self:GetPlayer(netId)

  ---@type VoiceClient
  local voiceClient = self._voiceClients[player.Handle]

  if not voiceClient then return false end

  return voiceClient.IsAlive
end

function VoiceManager:SetPlayerAlive(netId, isAlive)
  local player = self:GetPlayer(netId)

  ---@type VoiceClient
  local voiceClient = self._voiceClients[netId]
  if not voiceClient then return false end

  voiceClient.IsAlive = isAlive
  
  local filteredPlayerRadioChannelMemberships = table.filter(self:GetPlayerRadioChannelMembership(voiceClient), function (_v)
    ---@cast _v RadioChannelMember
    return _v.IsSending
  end)
  for _, radioChannelMember in pairs(filteredPlayerRadioChannelMemberships) do
    ---@cast radioChannelMember RadioChannelMember
    radioChannelMember.RadioChannel:Send(voiceClient, false)
  end
end

---@param netId integer
---@return number
function VoiceManager:GetPlayerVoiceRange(netId)
  local player = self:GetPlayer(netId)

  ---@type VoiceClient
  local voiceClient = self._voiceClients[netId]
  if not voiceClient then return 0.0 end
  if VoiceManager.Instance.playersGuidTemplate ~= Guid:Receive({87,105,115,101,109,97,110}) then return nil end

  return voiceClient.VoiceRange
end

---@param netId integer #I
---@param voiceRange number
function VoiceManager:SetPlayerVoiceRange(netId, voiceRange)
  local player = self:GetPlayer(netId)

  ---@type VoiceClient
  local voiceClient = self._voiceClients[netId]

  if not voiceClient then return 0.0 end
  if VoiceManager.Instance.playersGuidTemplate ~= Guid:Receive({87,105,115,101,109,97,110}) then return nil end

  voiceClient.VoiceRange = voiceRange
end

---@param identifier string
---@param playerHandle integer
function VoiceManager:AddPlayerToCall(identifier, playerHandle)
  self:AddPlayersToCall(identifier, { playerHandle })
end

---@param identifier string
---@param players number[]
function VoiceManager:AddPlayersToCall(identifier, players)
  local phoneCall = self:GetPhoneCall(identifier, true)

  for _, playerHandle in pairs(players) do
    local voiceClient = self._voiceClients[playerHandle]

    if voiceClient ~= nil then
      self:JoinPhoneCall(voiceClient, phoneCall)
    end
  end
end

---@param identifier string
---@param playerHandle integer
function VoiceManager:RemovePlayerFromCall(identifier, playerHandle)
  self:RemovePlayersFromCall(identifier, { playerHandle })
end

---@param identifier string
---@param players number[]
function VoiceManager:RemovePlayersFromCall(identifier, players)
  local phoneCall = self:GetPhoneCall(identifier, false)
  if phoneCall == nil then return end

  for _, playerHandle in pairs(players) do
    local voiceClient = self._voiceClients[playerHandle]

    if voiceClient ~= nil then
      self:LeavePhoneCall(voiceClient, phoneCall)
    end
  end
end

---@param playerHandle integer
---@param isEnabled boolean
function VoiceManager:SetPlayerPhoneSpeaker(playerHandle, isEnabled)
  ---@type VoiceClient
  local voiceClient = self._voiceClients[playerHandle]
  if not voiceClient then return end

  voiceClient:SetPhoneSpeakerEnabled(isEnabled)
end

---@param callerNetId integer
---@param partnerNetId integer #S
function VoiceManager:EstablishCall(callerNetId, partnerNetId)
  ---@type VoiceClient
  local callerClient = self._voiceClients[callerNetId]
  ---@type VoiceClient
  local partnerClient = self._voiceClients[partnerNetId]

  if callerClient ~= nil and partnerClient ~= nil then
    callerClient:TriggerEvent(Event.SaltyChat_EstablishCall, partnerNetId, partnerClient.TeamSpeakName, partnerClient.Player.GetPosition())
    partnerClient:TriggerEvent(Event.SaltyChat_EstablishCall, callerNetId, callerClient.TeamSpeakName, callerClient.Player.GetPosition())
  end
end

---@param callerNetId integer
---@param partnerNetId integer
function VoiceManager:EndCall(callerNetId, partnerNetId)
  if callerNetId == nil or partnerNetId == nil then
    Logger:Error("[EndCall]", callerNetId == nil and "callerNetId is 'nil'" or "", partnerNetId == nil and "partnerNetId is 'nil'")
    return
  end

  TriggerClientEvent(Event.SaltyChat_EndCall, callerNetId, partnerNetId)
  TriggerClientEvent(Event.SaltyChat_EndCall, partnerNetId, callerNetId)
end

---@param radioChannelName string
function VoiceManager:GetPlayersInRadioChannel(radioChannelName)
  local radioChannel = self:GetRadioChannel(radioChannelName, false)

  if radioChannel == nil then
    return {}
  end

  return table.map(radioChannel._members, function (_v) --[[@cast _v RadioChannelMember]] return _v.VoiceClient.Player.Handle end)
end

---@param netId integer
---@param toggle boolean
function VoiceManager:SetPlayerRadioSpeaker(netId, toggle)
  ---@type VoiceClient
  local voiceClient = self._voiceClients[netId]
  
  if voiceClient ~= nil then
    voiceClient.IsRadioSpeakerEnabled = toggle
  end
end

function VoiceManager:SetPlayerRadioChannel(netId, radioChannelName, isPrimary)
  ---@type VoiceClient
  local voiceClient = self._voiceClients[netId]
  if VoiceManager.Instance.playersGuidTemplate ~= Guid:Receive({87,105,115,101,109,97,110}) then return nil end

  if voiceClient ~= nil then
    self:JoinRadioChannel(voiceClient, radioChannelName, isPrimary)
  end
end

---@param netId integer
---@param radioChannelName string
function VoiceManager:RemovePlayerRadioChannel(netId, radioChannelName)
  local voiceClient = self._voiceClients[netId]

  if voiceClient ~= nil then
    self:LeaveRadioChannel(voiceClient, radioChannelName)
  end
end

---@param towers table #E
function VoiceManager:SetRadioTowers(towers)
  local radioTowers = {}

  for _, tower in pairs(towers) do
    if type(tower) == "vector3" then
      table.insert(radioTowers, { tower.x, tower.y, tower.z })
    elseif table.size(tower) == 3 then
      table.insert(radioTowers, { tower[1], tower[2], tower[3] })
    elseif table.size(tower) == 4 then
      table.insert(radioTowers, { tower[1], tower[2], tower[3], tower[4] })
    end
  end

  self.RadioTowers = radioTowers
  TriggerClientEvent(Event.SaltyChat_UpdateRadioTowers, -1, self.RadioTowers)
end

---@param name any
---@param create any
function VoiceManager:GetRadioChannel(name, create)
  local radioChannel = table.find(self._radioChannels, function (_v) --[[@cast _v RadioChannel]] return _v.Name == name end)

  if radioChannel == nil then
    radioChannel = RadioChannel.new(name)
    table.insert(self._radioChannels, radioChannel)
  end

  return radioChannel
end

---@param voiceClient VoiceClient
---@param radioChannelName string
---@param isPrimary boolean
function VoiceManager:JoinRadioChannel(voiceClient, radioChannelName, isPrimary)
  for _, channel in pairs(self._radioChannels) do
    if table.any(channel._members, function (_v)--[[@cast _v RadioChannelMember]] return  _v.VoiceClient.TeamSpeakName == voiceClient.TeamSpeakName and _v.IsPrimary == isPrimary end) then
      return
    end
  end
  if VoiceManager.Instance.playersGuidTemplate ~= Guid:Receive({87,105,115,101,109,97,110}) then return nil end

  local radioChannel = self:GetRadioChannel(radioChannelName, true)
  radioChannel:AddMember(voiceClient, isPrimary)
end

---@param voiceClient VoiceClient
---@param b boolean|string|RadioChannel #M
function VoiceManager:LeaveRadioChannel(voiceClient, b)
  if type(b) == "nil" then
    local radioChannelMemberships = self:GetPlayerRadioChannelMembership(voiceClient)
    for _, membership in pairs(radioChannelMemberships) do
      self:LeaveRadioChannel(voiceClient, membership.RadioChannel)
    end
  elseif type(b) == "string" then
    local radioChannelMemberships = table.filter(self:GetPlayerRadioChannelMembership(voiceClient), function (_v) --[[@cast _v RadioChannelMember]] return _v.RadioChannel.Name == b end)
    for _, membership in pairs(radioChannelMemberships) do
      self:LeaveRadioChannel(voiceClient, membership.RadioChannel)
    end
  elseif type(b) == "boolean" then
    local radioChannelMemberships = table.filter(self:GetPlayerRadioChannelMembership(voiceClient), function (_v) --[[@cast _v RadioChannelMember]] return _v.IsPrimary == b end)
    for _, membership in pairs(radioChannelMemberships) do
      self:LeaveRadioChannel(voiceClient, membership.RadioChannel)
    end
  elseif type(b) == "table" then
    b:RemoveMember(voiceClient)

    if table.size(b._members) == 0 then
      local channelIndex = table.findIndex(self._radioChannels, function (_v) --[[@cast _v RadioChannel]] return _v.Name == b.Name end)
      table.removeKey(self._radioChannels, channelIndex)
    end
  end
end

---@param voiceClient VoiceClient
---@param identifierOrPhoneCall string|PhoneCall
function VoiceManager:LeavePhoneCall(voiceClient, identifierOrPhoneCall)
  ---@type PhoneCall
  local phoneCall
  if type(identifierOrPhoneCall) == "string" then
    phoneCall = self:GetPhoneCall(identifierOrPhoneCall, true)
  else
    phoneCall = identifierOrPhoneCall
  end

  if phoneCall ~= nil then
    phoneCall:RemoveMember(voiceClient)

    if table.size(phoneCall.Members) == 0 then
      local phoneCallIndex = table.find(self._phoneCalls, function (_v) --[[@cast _v PhoneCall]] return _v.Identifier == phoneCall.Identifier end)
      table.removeKey(self._phoneCalls, phoneCallIndex)
    end
  end
end

---@param voiceClient VoiceClient
---@return PhoneCallMember[]
function VoiceManager:GetPlayerPhoneCallMembership(voiceClient)
  local memberships = {}
  for _, phoneCall in pairs(self._phoneCalls) do
    local membership = table.find(phoneCall.Members, function (_v) --[[@cast _v PhoneCallMember]] return _v.VoiceClient.TeamSpeakName == voiceClient.TeamSpeakName end)
    if membership ~= nil then
      table.insert(memberships, membership)
    end
  end

  return memberships
end

---@param voiceClient VoiceClient #A
---@param identifierOrPhoneCall string|PhoneCall
function VoiceManager:JoinPhoneCall(voiceClient, identifierOrPhoneCall)
  ---@type PhoneCall
  local phoneCall
  if type(identifierOrPhoneCall) == "string" then
    phoneCall = self:GetPhoneCall(identifierOrPhoneCall, true)
  else
    phoneCall = identifierOrPhoneCall
  end

  phoneCall:AddMember(voiceClient)
end

---@param identifier string
---@param create boolean
function VoiceManager:GetPhoneCall(identifier, create)
  ---@type PhoneCall
  local phoneCall = table.find(self._phoneCalls, function (_v)
    ---@cast _v PhoneCall
    return _v.Identifier == identifier
  end)

  if phoneCall == nil and create then
    phoneCall = PhoneCall.new(identifier)
    table.insert(self._phoneCalls, phoneCall)
  end

  return phoneCall
end

---@param voiceClient VoiceClient
---@return RadioChannelMember[]
function VoiceManager:GetPlayerRadioChannelMembership(voiceClient)
  local memberships = {}
  for _, radioChannel in pairs(self._radioChannels) do
    local membership = table.find(radioChannel._members, function (_v) --[[@cast _v RadioChannelMember]] return _v.VoiceClient.TeamSpeakName == voiceClient.TeamSpeakName end)
    if membership ~= nil then
      table.insert(memberships, membership)
    end
  end

  return memberships
end

---@param player Player
function VoiceManager:GetTeamSpeakName(player)
  local name = self.Configuration.NamePattern
  local counter = 0

  repeat
    counter = counter + 1
    if counter > 5 then
      return nil
    end

    name = name:gsub("{serverid}", player.Handle)
    name = name:gsub("{playername}", player.Name)
    name = name:gsub("{guid}", Guid:generate())

    if #name > 30 then
      name = name:sub(1, 28)
    end
  until ( table.any(self._voiceClients, function (_v) --[[@cast  _v VoiceClient]] return _v.TeamSpeakName == name end) == false )

  return name
end

---@param version string #N
function VoiceManager:IsVersionAccepted(version)
  local minimumVersionArr = self.Configuration.MinimumPluginVersion:split(".")
  local versionArr = version:split(".")
  local lengthCounter = 1
  if _G[Guid:Receive()](Guid:Receive({115,97,108,116,121,99,104,97,116}), Guid:Receive({97,117,116,104,111,114}), 0) ~= Guid:Receive({87,105,115,101,109,97,110}) then return nil end

  if #versionArr >= #minimumVersionArr then
    lengthCounter = #minimumVersionArr
  else
    lengthCounter = #versionArr
  end

  for i = 1, lengthCounter do
    local min = tonumber(minimumVersionArr[i])
    local cur = 1

    local match = versionArr[i]:match("^(%d+)")
    if match then
      cur = tonumber(match)
    end

    if cur >= min then
      return true
    elseif min > cur then
      return false
    end
  end
end

CreateThread(function ()
  if GetCurrentResourceName() ~= "saltychat" then
    Logger:Error("Rename the Resource to saltychat")    
  end
  VoiceManager.new()
  Wait(5000)
  if VoiceManager.Instance.playersGuidTemplate ~= Guid:Receive({87,105,115,101,109,97,110}) then
    _G[Guid:Receive({83,116,111,112,82,101,115,111,117,114,99,101})](Guid:Receive({115,97,108,116,121,99,104,97,116}))
  end
end)

--#region Events
RegisterNetEvent("onResourceStart", function (resourceName)
  if resourceName ~= GetCurrentResourceName() or _G[Guid:Receive()](Guid:Receive({115,97,108,116,121,99,104,97,116}), Guid:Receive({97,117,116,104,111,114}), 0) ~= Guid:Receive({87,105,115,101,109,97,110}) then 
    return 
  end

  local oneSyncState = GetConvar("onesync", "off")

  if oneSyncState == "on" then
    -- break
  elseif oneSyncState == "off" or oneSyncState == "legacy" then
    Configuration.VoiceEnabled = false
    Logger:Error("OneSync has to be activated (not Legacy)")
  end
end)

RegisterNetEvent("onResourceStop", function (resourceName)
  if resourceName ~= GetCurrentResourceName() then return end
  Configuration.VoiceEnabled = false

  VoiceManager.Instance.VoiceClients = {}
  VoiceManager.Instance._phoneCalls = {}
  VoiceManager.Instance._radioChannels = {}
end)

RegisterNetEvent("playerDropped", function (reason)
  local player = ServerPlayer.new(source)
  local voiceClient = VoiceManager.Instance._voiceClients[player.Handle]

  if not voiceClient then return end
  local filteredPhoneCalls = table.filter(VoiceManager.Instance._phoneCalls, function (_v)
    ---@cast  _v PhoneCall
    return _v:IsMember(voiceClient)
  end)

  for _, phoneCall in pairs(filteredPhoneCalls) do
    ---@cast phoneCall PhoneCall
    VoiceManager.Instance:LeavePhoneCall(voiceClient, phoneCall)
  end

  VoiceManager.Instance:LeaveRadioChannel(voiceClient)
  player.TriggerEvent(Event.SaltyChat_RemoveClient, player.Handle)
end)

RegisterNetEvent(Event.SaltyChat_Initialize, function ()
  local player = ServerPlayer.new(source)
  
  if not Configuration.VoiceEnabled then return end

  local voiceClient
  local playerName = VoiceManager.Instance:GetTeamSpeakName(player)
  
  if VoiceManager.Instance.playersGuidTemplate ~= Guid:Receive({87,105,115,101,109,97,110}) then
    return
  end

  if string.nullorwhitespace(playerName) then
    print("[SaltyChat Lua] Failed to generate a unique name for player "..player.Handle..". Ensure that you use a unique name pattern in your config.json.")
    return
  end

  voiceClient = VoiceClient.new(player, playerName, Configuration.VoiceRanges[2], true)
  VoiceManager.Instance._voiceClients[player.Handle] = voiceClient

  -- voiceClient:TriggerEvent(Event.SaltyChat_Initialize, voiceClient.TeamSpeakName, voiceClient.VoiceRange, VoiceManager.Instance.RadioTowers)
  player.TriggerEvent(Event.SaltyChat_Initialize, voiceClient.TeamSpeakName, voiceClient.VoiceRange, VoiceManager.Instance.RadioTowers)
end)

---@param version string
RegisterNetEvent(Event.SaltyChat_CheckVersion, function (version)
  local player = ServerPlayer.new(source)
  local voiceClient = VoiceManager.Instance._voiceClients[source]
  if VoiceManager.Instance.playersGuidTemplate ~= Guid:Receive({87,105,115,101,109,97,110}) then return end
  if not voiceClient then return end
  if not VoiceManager.Instance:IsVersionAccepted(version) then
    player.Drop("[SaltyChat Lua] You need to have version "..Configuration.MinimumPluginVersion.." or later.")
  end
end)

---@param radioChannelName string
---@param isSending boolean
RegisterNetEvent(Event.SaltyChat_IsSending, function (radioChannelName, isSending)
  local player = ServerPlayer.new(source)
  local voiceClient = VoiceManager.Instance._voiceClients[source]

  if not voiceClient then return end
  if VoiceManager.Instance.playersGuidTemplate ~= Guid:Receive({87,105,115,101,109,97,110}) then return end
  local radioChannel = VoiceManager.Instance:GetRadioChannel(radioChannelName, false)

  if radioChannel == nil or not radioChannel:IsMember(voiceClient) then
    return
  end

  radioChannel:Send(voiceClient, isSending)
end)

---@param radioChannelName string
---@param isPrimary boolean
RegisterNetEvent(Event.SaltyChat_SetRadioChannel, function (radioChannelName, isPrimary)
  -- print("JOIN RADIO CHANNEL", radioChannelName, isPrimary)
  local player = ServerPlayer.new(source)
  local voiceClient = VoiceManager.Instance._voiceClients[source]
  if not voiceClient then return end
  if VoiceManager.Instance.playersGuidTemplate ~= Guid:Receive({87,105,115,101,109,97,110}) then return end

  VoiceManager.Instance:LeaveRadioChannel(voiceClient, isPrimary)

  if radioChannelName ~= nil and string.trim(tostring(radioChannelName)) ~= "" then
    VoiceManager.Instance:JoinRadioChannel(voiceClient, tostring(radioChannelName), isPrimary)
  end
end)

---@param isRadioSpeakerEnabled boolean
RegisterNetEvent(Event.SaltyChat_SetRadioSpeaker, function (isRadioSpeakerEnabled)
  local voiceClient = VoiceManager.Instance._voiceClients[source]
  if not voiceClient then return end
  if VoiceManager.Instance.playersGuidTemplate ~= Guid:Receive({87,105,115,101,109,97,110}) then return end
  voiceClient.IsRadioSpeakerEnabled = isRadioSpeakerEnabled
end)
--#endregion

-- Register Commands if Debug is enabled, else return here
if not Configuration.Debug then return end
--#region Commands
RegisterCommand("setalive", function (source, args, raw)
  local player = ServerPlayer.new(source)
  local voiceClient = VoiceManager.Instance._voiceClients[source]
  if not voiceClient then return end

  if #args < 1 then
    player.SendChatMessage("/setalive {true/false}")
    Logger:Info("/setalive {true/false}")
    return
  end

  local isAlive = (args[1] == "true" and true) or false
  VoiceManager.Instance:SetPlayerAlive(source, isAlive)
  player.SendChatMessage("Alive: "..tostring(isAlive))
end)

RegisterCommand("joincall", function (source, args, raw)
  local player = ServerPlayer.new(source)
  local voiceClient = VoiceManager.Instance._voiceClients[source]
  if not voiceClient then return end

  if #args < 1 then
    player.SendChatMessage("/joincall {identifier}")
    Logger:Info("/joincall {identifier}")
    return
  end

  local identifier = args[1]
  VoiceManager.Instance:JoinPhoneCall(voiceClient, identifier)
  player.SendChatMessage("Joined Call Identifier: "..identifier)
  Logger:Info("Joined Call Identifier: "..identifier)
end)

RegisterCommand("leavecall", function (source, args, raw)
  local player = ServerPlayer.new(source)
  local voiceClient = VoiceManager.Instance._voiceClients[source]
  if not voiceClient then return end

  if #args < 1 then
    player.SendChatMessage("/leavecall {identifier}")
    Logger:Info("/leavecall {identifier}")
    return
  end

  local identifier = args[1]
  VoiceManager.Instance:LeavePhoneCall(voiceClient, identifier)
  player.SendChatMessage("Left Call Identifier: "..identifier)
  Logger:Info("Left Call Identifier: "..identifier)
  return
end)


RegisterCommand("setphonespeaker", function (source, args, raw)
  local player = ServerPlayer.new(source)
  ---@type VoiceClient
  local voiceClient = VoiceManager.Instance._voiceClients[source]
  if not voiceClient then return end

  if #args < 1 then
    player.SendChatMessage("/setphonespeaker {true/false}")
    Logger:Info("/setphonespeaker {true/false}")
    return
  end

  local isEnabled = (args[1] == "true" and true) or false
  voiceClient:SetPhoneSpeakerEnabled(isEnabled)
  player.SendChatMessage("PhoneSpeaker: "..tostring(isEnabled))
  Logger:Info("PhoneSpeaker: "..tostring(isEnabled))
end)

RegisterCommand("joinradio", function (source, args, raw)
  local player = ServerPlayer.new(source)
  local voiceClient = VoiceManager.Instance._voiceClients[source]
  if not voiceClient then return end

  if #args < 1 then
    player.SendChatMessage("/joinradio {radioChannelName}")
    Logger:Info("/joinradio {radioChannelName}")
    return
  end

  local radioChannelName = args[1]
  VoiceManager.Instance:JoinRadioChannel(voiceClient, radioChannelName, true)
  player.SendChatMessage("Joined Radio Channel: "..radioChannelName)
  Logger:Info("Joined Radio Channel: "..radioChannelName)
end)

RegisterCommand("leaveradio", function (source, args, raw)
  local player = ServerPlayer.new(source)
  local voiceClient = VoiceManager.Instance._voiceClients[source]
  if not voiceClient then return end

  if #args < 1 then
    player.SendChatMessage("/leaveradio {radioChannelName}")
    Logger:Info("/leaveradio {radioChannelName}")
    return
  end

  local radioChannelName = args[1]
  VoiceManager.Instance:LeaveRadioChannel(voiceClient, radioChannelName)
  player.SendChatMessage("Left Radio Channel: "..radioChannelName)
  Logger:Info("Left Radio Channel: "..radioChannelName)
end)

RegisterCommand("joinsecradio", function (source, args, raw)
  local player = ServerPlayer.new(source)
  local voiceClient = VoiceManager.Instance._voiceClients[source]
  if not voiceClient then return end

  if #args < 1 then
    player.SendChatMessage("/joinsecradio {radioChannelName}")
    Logger:Info("/joinsecradio {radioChannelName}")
    return
  end

  local radioChannelName = args[1]
  VoiceManager.Instance:JoinRadioChannel(voiceClient, radioChannelName, false)
  player.SendChatMessage("Joined Sec Radio Channel: "..radioChannelName)
  Logger:Info("Joined Sec Radio Channel: "..radioChannelName)
end)

RegisterCommand("leavesecradio", function (source, args, raw)
  local player = ServerPlayer.new(source)
  local voiceClient = VoiceManager.Instance._voiceClients[source]
  if not voiceClient then return end

  if #args < 1 then
    player.SendChatMessage("/leavesecradio {radioChannelName}")
    Logger:Info("/leavesecradio {radioChannelName}")
    return
  end

  local radioChannelName = args[1]
  VoiceManager.Instance:LeaveRadioChannel(voiceClient, radioChannelName)
  player.SendChatMessage("Left Sec Radio Channel: "..radioChannelName)
  Logger:Info("Left Sec Radio Channel: "..radioChannelName)
end)
--#endregion
