local receivers
local currentChatText = {}
local receiverConfigs = {}
local currentConfig

function DarkRP.addChatReceiver(prefix, text, hearFunc)
	receiverConfigs[prefix] = {
		text = text, 
		hearFunc = hearFunc
	}
end

function DarkRP.removeChatReceiver(prefix)
	receiverConfigs[prefix] = nil
end

local function DrawChatReceivers()
	if !receivers then return end
	local shouldDraw = hook.Call("HUDShouldDraw", GAMEMODE, "DarkRP_ChatReceivers")
	if shouldDraw == false then return end
	local x, y = chat.GetChatBoxPos()
	local fontHeight = draw.GetFontHeight("DarkRPHUD1")
	y = y - fontHeight - 4
	local receiversCount = #receivers
	if receiversCount == 0 then
		draw.WordBox(2, x, y, DarkRP.getPhrase("hear_noone", currentConfig.text), "DarkRPHUD1", Color(0, 0, 0, 160), Color(255, 0, 0, 255))
		return
	elseif receiversCount == player.GetCount() - 1 then
		draw.WordBox(2, x, y, DarkRP.getPhrase("hear_everyone"), "DarkRPHUD1", Color(0, 0, 0, 160), Color(0, 255, 0, 255))
		return
	end
	draw.WordBox(2, x, y - (receiversCount * (fontHeight + 4)), DarkRP.getPhrase("hear_certain_persons", currentConfig.text), "DarkRPHUD1", Color(0, 0, 0, 160), Color(0, 255, 0, 255))
	for i = 1, receiversCount, 1 do
		if !IsValid(receivers[i]) then
			receivers[i] = receivers[#receivers]
			receivers[#receivers] = nil
			continue
		end
		draw.WordBox(2, x, y - (i - 1) * (fontHeight + 4), receivers[i]:Name(), "DarkRPHUD1", Color(0, 0, 0, 160), color_white)
	end
end

local function ChatGetRecipients()
	if !currentConfig then return end
	receivers = {}
	local local_ply = LocalPlayer()
	for _, ply in ipairs(player.GetAll()) do
		local hide_ply = hook.Run("chatHideRecipient", ply)
		if ply == local_ply or hide_ply then continue end
		local val = currentConfig.hearFunc(ply, currentChatText)
		if val == nil then
			receivers = nil
			return
		elseif val == true then
			table.insert(receivers, ply)
		end
	end
end

local function StartFind()
	local shouldDraw = hook.Call("HUDShouldDraw", GAMEMODE, "DarkRP_ChatReceivers")
	if shouldDraw == false then return end
	currentConfig = receiverConfigs[""]
	hook.Add("Think", "DarkRP_ChatRecipients", ChatGetRecipients)
	hook.Add("HUDPaint", "DarkRP_DrawChatReceivers", DrawChatReceivers)
end
hook.Add("StartChat", "DarkRP_StartFindChatReceivers", StartFind)

local function StopFind()
	hook.Remove("Think", "DarkRP_ChatRecipients")
	hook.Remove("HUDPaint", "DarkRP_DrawChatReceivers")
end
hook.Add("FinishChat", "DarkRP_StopFindChatReceivers", StopFind)

local function FindConfig(text)
	local split = string.Explode(' ', text)
	local prefix = string.lower(split[1])
	currentChatText = split
	currentConfig = receiverConfigs[prefix] or receiverConfigs[""]
end
hook.Add("ChatTextChanged", "DarkRP_FindChatRecipients", FindConfig)

local function StartFindVoice(ply)
	if ply != LocalPlayer() then return end
	local shouldDraw = hook.Call("HUDShouldDraw", GAMEMODE, "DarkRP_ChatReceivers")
	if shouldDraw == false then return end
	currentConfig = receiverConfigs["speak"]
	hook.Add("Think", "DarkRP_ChatRecipients", ChatGetRecipients)
	hook.Add("HUDPaint", "DarkRP_DrawChatReceivers", DrawChatReceivers)
end
hook.Add("PlayerStartVoice", "DarkRP_VoiceChatReceiverFinder", StartFindVoice)

local function StopFindVoice(ply)
	if ply != LocalPlayer() then return end
	StopFind()
end
hook.Add("PlayerEndVoice", "DarkRP_VoiceChatReceiverFinder", StopFindVoice)

local function IsInSight(ply)
	local tr = util.TraceLine({
		start = LocalPlayer():EyePos(),
		endpos = ply:EyePos(),
		filter = function(ent)
			if ent:IsVehicle() or ent:IsRagdoll() then return end
			local class = ent:GetClass()
			if string.find(class, "prop_") or string.find(class, "func_") then
				return true
			else
				return false
			end
		end
	})
	if tr.Hit or tr.HitWorld then return false end
	return true
end

local function LoadChatReceivers()
	local talkDistance = GAMEMODE.Config.talkDistance * GAMEMODE.Config.talkDistance
	DarkRP.addChatReceiver("", DarkRP.getPhrase("talk"), function(ply)
		if GAMEMODE.Config.alltalk then return nil end
		return ply:Alive() and !ply:GetNoDraw() and !ply:IsDormant() and LocalPlayer():GetPos():DistToSqr(ply:GetPos()) < talkDistance and IsInSight(ply)
	end)

	DarkRP.addChatReceiver("/ooc", DarkRP.getPhrase("speak_in_ooc"), function(ply) return true end)

	DarkRP.addChatReceiver("/advert", DarkRP.getPhrase("speak_in_ooc"), function(ply) return true end)

	DarkRP.addChatReceiver("//", DarkRP.getPhrase("speak_in_ooc"), function(ply) return true end)

	DarkRP.addChatReceiver("/a", DarkRP.getPhrase("speak_in_ooc"), function(ply) return true end)

	local whisperDistance = GAMEMODE.Config.whisperDistance * GAMEMODE.Config.whisperDistance
	DarkRP.addChatReceiver("/w", DarkRP.getPhrase("whisper"), function(ply)
		return ply:Alive() and !ply:GetNoDraw() and !ply:IsDormant() and LocalPlayer():GetPos():DistToSqr(ply:GetPos()) < whisperDistance and IsInSight(ply)
	end)

	local yellDistance = GAMEMODE.Config.yellDistance * GAMEMODE.Config.yellDistance
	DarkRP.addChatReceiver("/y", DarkRP.getPhrase("yell"),
		function(ply) return ply:Alive() and !ply:GetNoDraw() and !ply:IsDormant() and LocalPlayer():GetPos():DistToSqr(ply:GetPos()) < yellDistance and IsInSight(ply)
	end)

	local meDistance = GAMEMODE.Config.meDistance * GAMEMODE.Config.meDistance
	DarkRP.addChatReceiver("/me", DarkRP.getPhrase("perform_your_action"), function(ply)
		return ply:Alive() and !ply:GetNoDraw() and !ply:IsDormant() and LocalPlayer():GetPos():DistToSqr(ply:GetPos()) < meDistance and IsInSight(ply)
	end)

	DarkRP.addChatReceiver("/g", DarkRP.getPhrase("talk_to_your_group"), function(ply)
		local local_ply = LocalPlayer()
		for _, func in pairs(GAMEMODE.DarkRPGroupChats) do
			if func(local_ply) and func(ply) then
				return true
			end
		end
		return false
	end)

	DarkRP.addChatReceiver("/pm", "PM", function(ply, text)
		if !isstring(text[2]) then return false end
		text[2] = string.lower(tostring(text[2]))
		return string.find(string.lower(ply:Name()), text[2], 1, true) != nil or string.lower(ply:SteamID()) == text[2]
	end)

	local voiceDistance = GM.Config.voiceDistance * GM.Config.voiceDistance
	DarkRP.addChatReceiver("speak", DarkRP.getPhrase("speak"), function(ply)
		local local_ply = LocalPlayer()
		if !local_ply.DRPIsTalking then return nil end
		return ply:Alive() and !ply:GetNoDraw() and !ply:IsDormant() and local_ply:GetPos():DistToSqr(ply:GetPos()) < voiceDistance and IsInSight(ply)
	end)

	-- Custom

	DarkRP.addChatReceiver("/looc", "LOOC", function(ply)
		return ply:Alive() and !ply:GetNoDraw() and !ply:IsDormant() and LocalPlayer():GetPos():DistToSqr(ply:GetPos()) < meDistance and IsInSight(ply)
	end)
end
hook.Add("loadCustomDarkRPItems", "loadChatListeners", LoadChatReceivers)