-- bully online chat - derpy54320 & SWEGTA

-- return true in "chat:StartTyping" event to stop from being usable

local gTyping
local gMessages = {n = 0}
local gScrolling = 0
local gDisplayActive = false
local gDisplayAlpha = 0 -- goes up/down if active or not
local gDisplayTimer
local gPlaySound -- sounds from net events are only played when the main thread runs
local gUseOneLine = not GetConfigBoolean(GetScriptConfig(),"always_show_under_name",false)
local gFontFamily = GetConfigString(GetScriptConfig(),"font_family","Arial")
local gFontStyle = GetConfigString(GetScriptConfig(),"font_style","black")
local gFontScale = GetConfigNumber(GetScriptConfig(),"font_scale",0.7)

-- Main.
function main()
	local draw
	while not SystemIsReady() do
		Wait(0)
	end
	draw = CreateDrawingThread("T_Draw")
	while IsThreadRunning(draw) do
		if F_StartTyping() then -- start or continue typing
			local submit = F_UpdateTyping()
			if submit then
				F_SubmitMessage(submit)
			elseif gTyping then
				F_Display() -- keep displaying as long as we didnt during the update
			end
		end
		if gDisplayActive then
			F_UpdateScrolling()
		end
		if gPlaySound then
			SoundPlay2D(gPlaySound) -- sounds from network events will only play once the main thread is reached (so it doesn't play when paused)
			gPlaySound = nil
		end
		Wait(0)
	end
	TextPrintString("chat script failed",10,1)
end
function F_StartTyping()
	if not gTyping then
		local slash = IsKeyBeingPressed("SLASH")
		if (slash or IsKeyBeingPressed("RETURN")) and RunLocalEvent("chat:StartTyping") then
			if slash then
				gTyping = StartTyping("/")
			else
				gTyping = StartTyping()
			end
			if gTyping then
				gScrolling = 0 -- reset scroll
				SoundPlay2D("ButtonUp")
				return true -- started typing
			end
		end
		return false -- not typing
	end
	return true -- already typing
end
function F_UpdateTyping()
	if not IsTypingActive(gTyping) then
		if not WasTypingAborted(gTyping) then
			local result = GetTypingString(gTyping) -- we want to submit this message
			gTyping = nil
			return result
		end
		gTyping = nil
		SoundPlay2D("NavInvalid")
	elseif IsConsoleActive() then -- if the console opens while typing then stop typing
		StopTyping(gTyping)
		gTyping = nil
		SoundPlay2D("NavInvalid")
	end
end
function F_UpdateScrolling()
	if IsKeyBeingPressed("PRIOR") and gScrolling < gMessages.n - 1 then
		F_Display()
		SoundPlay2D("NavUp")
		gScrolling = gScrolling + 1
	elseif IsKeyBeingPressed("NEXT") and gScrolling > 0 then
		F_Display()
		SoundPlay2D("NavDwn")
		gScrolling = gScrolling - 1
	end
end
function F_SubmitMessage(text)
	if string.sub(text,1,1) ~= '/' then
		local starts = string.find(text,"%S") -- check for non white space characters
		if starts then
			SendNetworkEvent("chat:SendMessage",string.sub(text,starts,GetConfigNumber(GetScriptConfig(),"max_message_length",100)+starts-1)) -- send message to server
		end
	else
		local starts,stops,cmd = string.find(text,"[/]*%s*(%S+)") -- get the command name just like the normal command parser would
		if starts then
			F_RunCommand(cmd,string.sub(text,stops+1))
		end
	end
end
function F_RunCommand(cmd,args)
	local event = RegisterLocalEventHandler("ConsolePrinted",CB_ConsolePrinted)
	local help = GetConfigString(GetScriptConfig(),"help_text")
	if help and string.lower(cmd) == "help" and not string.find(args,"%S") then
		CB_ConsolePrinted(string.gsub(help,"\\n","\n"),"output")
	elseif string.lower(cmd) == "clear" then
		gMessages = {n = 0}
	elseif not RunCommand(cmd..args) then
		F_AddMessage({r = 200,g = 50,b = 50,text = "unknown command: /"..cmd})
	end
	RemoveEventHandler(event)
end

-- Draw chat.
function T_Draw()
	local box = CreateTexture("chatbox.png")
	local banner = CreateTexture("chatbanner.png") -- same dimensions
	local boxh = 0.35
	local boxy = 1 - boxh - 0.01
	local typepadw = 0.005
	local typeh = 0.075 * boxh
	local typey = boxy + 0.97 * boxh - typeh
	local msgtopy = boxy + 0.2 * boxh
	while true do
		if not IsPauseMenuActive() and not IsMapMenuActive() and F_UpdateAlpha() then
			local boxw = boxh * GetTextureDisplayAspectRatio(box)
			local boxx = 1 - boxw - 0.01 / GetDisplayAspectRatio()
			local typex = boxx + ((1 - 0.95) / 2) * boxw
			local typew = 0.95 * boxw
			local typing = gTyping and IsTypingActive(gTyping)
			DrawTexture(box,boxx,boxy,boxw,boxh,255,255,255,255*gDisplayAlpha)
			if typing then
				local text = GetTypingString(gTyping)
				local typeclip = typew * (1 - typepadw)
				DrawRectangle(typex,typey,typew,typeh,0,0,0,150*gDisplayAlpha)
				F_SetupTextFont(false)
				SetTextColor(220,220,220,255*gDisplayAlpha)
				SetTextClipping(typeclip)
				SetTextHeight(typeh*0.67)
				SetTextShadow()
				if MeasureText(text) >= typeclip then
					SetTextPosition(typex+typeclip,typey+typeh/2)
					SetTextAlign("R","C")
				else
					SetTextPosition(typex+typew*typepadw,typey+typeh/2)
					SetTextAlign("L","C")
				end
				DrawText(text)
			end
			if gMessages.n ~= 0 then
				local namespace = 0.005 / GetDisplayAspectRatio()
				local y,w1,h1,w2,h2 = typey
				if not typing then
					y = y + typeh
				end
				for index = gMessages.n-gScrolling,1,-1 do
					local msg = gMessages[index]
					local oneline = gUseOneLine
					if y <= msgtopy then
						break
					end
					F_SetupTextFont(true)
					SetTextAlign("L","B")
					if msg.name then
						w1,h1 = MeasureText(msg.name..":") -- measure name
						w1 = w1 + namespace
					else
						w1,h1 = 0,0
					end
					w2,h2 = MeasureText(msg.text) -- measure text
					if oneline and typew - (w1 + w2) < 0.002 then
						oneline = false
					end
					SetTextWrapping(typew)
					w2,h2 = MeasureText(msg.text) -- re-measure with wrapping
					if msg.name then
						SetTextColor(msg.r,msg.g,msg.b,255*gDisplayAlpha)
						SetTextClipping(typew,y-msgtopy)
						if oneline then
							SetTextPosition(typex,y)
							DrawText(msg.name..":") -- draw name
						elseif y - h2 > msgtopy then
							SetTextPosition(typex,y-h2)
							DrawText(msg.name..":") -- draw name
						else
							DiscardText()
						end
						F_SetupTextFont(true)
						SetTextAlign("L","B")
						SetTextColor(230,230,230,255*gDisplayAlpha)
					else
						SetTextColor(msg.r,msg.g,msg.b,255*gDisplayAlpha)
					end
					SetTextClipping(nil,y-msgtopy)
					if oneline then
						SetTextPosition(typex+w1,y)
						y = y - math.max(h1,h2)
					else
						SetTextPosition(typex,y)
						SetTextWrapping(typew)
						y = y - h1 - h2
					end
					DrawText(msg.text) -- draw text
				end
			end
			DrawTexture(banner,boxx,boxy,boxw,boxh,255,255,255,255*gDisplayAlpha)
		end
		Wait(0)
	end
end
function F_Display()
	if RunLocalEvent("chat:UpdateDisplay") then
		gDisplayActive = true
		gDisplayTimer = GetTimer()
		return true
	end
	return false
end
function F_UpdateAlpha()
	if gDisplayActive and GetTimer() - gDisplayTimer >= 8000 then
		gDisplayActive = false
	end
	if gDisplayActive then
		if gDisplayAlpha ~= 1 then
			gDisplayAlpha = math.min(1,gDisplayAlpha+GetFrameTime()/0.2)
		end
	elseif gDisplayAlpha ~= 0 then
		gDisplayAlpha = math.max(0,gDisplayAlpha-GetFrameTime()/0.5)
	end
	return gDisplayAlpha ~= 0
end
function F_SetupTextFont(scale)
	SetTextFont(gFontFamily)
	if gFontStyle == "black" then
		SetTextBlack()
	elseif gFontStyle == "bold" then
		SetTextBold()
	end
	if scale then
		SetTextScale(gFontScale)
	end
end

-- Console output.
function CB_ConsolePrinted(message,type)
	if type == "output" then
		F_AddMessage({text = message,r = 150,g = 150,b = 150})
	elseif type == "error" then
		F_AddMessage({text = message,r = 200,g = 50,b = 50})
	elseif type == "warning" then
		F_AddMessage({text = message,r = 200,g = 150,b = 50})
	elseif type == "special" then
		F_AddMessage({text = message,r = 50,g = 150,b = 200})
	end
end

-- Add messages.
RegisterNetworkEventHandler("chat:PlayerJoined",function(name)
	if F_AddMessage({text = name.." has joined the game.",r = 150,g = 150,b = 150}) then
		--gPlaySound = "RightBtn"
	end
end)
RegisterNetworkEventHandler("chat:PlayerLeft",function(name)
	if F_AddMessage({text = name.." has left the game.",r = 150,g = 150,b = 150}) then
		--gPlaySound = "WrongBtn"
	end
end)
RegisterNetworkEventHandler("chat:PlayerMessage",function(name,message,r,g,b)
	if F_AddMessage({name = name,text = message,r = r,g = g,b = b}) then
		gPlaySound = "ButtonDown"
	end
end)
RegisterNetworkEventHandler("chat:ServerMessage",function(message)
	if F_AddMessage({text = message,r = 150,g = 150,b = 150}) then
		gPlaySound = "ButtonDown"
	end
end)
function F_AddMessage(msg)
	if gMessages.n >= 100 then
		table.remove(gMessages,1)
	end
	table.insert(gMessages,msg)
	gScrolling = 0
	return F_Display()
end

-- Exported functions.
function exports.Say(message)
	if F_AddMessage({text = message,r = 150,g = 150,b = 150}) then
		gPlaySound = "ButtonDown"
	end
end
function exports.Hide()
	gDisplayActive = false
end
