--[[
	Title: Messages

	Handles messaging like logging, debug, etc.
]]


--[[
	Function: tsay

	Prints a message in talk say as well as in the user's consoles.

	Parameters:

		ply - The player to print to, set to nil to send to everyone. (Ignores this param if called on client)
		msg - The message to print.
		wait - *(Optional, defaults to false)* Wait one frame before posting. (Useful to use from things like chat hooks)
		wasValid - *(INTERNAL USE ONLY)* This is flagged on waiting if the player *was* valid.

	Revisions:

		v2.10 - Initial
]]
function ULib.tsay( ply, msg, wait, wasValid )
	ULib.checkArg( 1, "ULib.tsay", {"nil","Player","Entity"}, ply )
	ULib.checkArg( 2, "ULib.tsay", "string", msg )
	ULib.checkArg( 3, "ULib.tsay", {"nil","boolean"}, wait )

	if wait then ULib.namedQueueFunctionCall( "ULibChats", ULib.tsay, ply, msg, false, ply and ply:IsValid() ) return end -- Call next frame

	if SERVER and ply and not ply:IsValid() then -- Server console
		if wasValid then -- This means we had a valid player that left, so do nothing
			return
		end
		Msg( msg .. "\n" )
		return
	end

	if CLIENT then
		LocalPlayer():ChatPrint( msg )
		return
	end

	if ply then
		ply:ChatPrint( msg )
	else
		local players = player.GetAll()
		for _, player in ipairs( players ) do
			player:ChatPrint( msg )
		end
	end
end

local serverConsole = {} -- Used in the function below to identify the server console (internal use)

local function tsayColorCallback( ply, ... )
	if CLIENT then
		chat.AddText( ... )
		return
	end

	if ply and ply ~= serverConsole and not ply:IsValid() then return end -- Player must have left the server

	local args = { ... }

	if ply == serverConsole then
		for i=2, #args, 2 do
			Msg( args[ i ] )
		end
		Msg( "\n" );
		return
	end

	local current_chunk = { size = 0 }
	local chunks = { current_chunk }
	local max_chunk_size = 240
	while #args > 0 do
		local arg = table.remove( args, 1 )
		local typ = type( arg )
		local arg_size = typ == "table" and 4 or #arg + 2 -- Include null in strings, bool in both
		if typ == "string" and current_chunk.size + arg_size > max_chunk_size then -- Split a large string up into multiple messages
			local substr = arg:sub( 1, math.max( 1, max_chunk_size - current_chunk.size - 2 ) )
			if #substr > 0 then
				table.insert( current_chunk, substr )
			end
			table.insert( args, 1, arg:sub( #substr + 1) )

			current_chunk = { size = 0 }
			table.insert( chunks, current_chunk )
		else
			if current_chunk.size + arg_size > max_chunk_size then
				current_chunk = { size = 0 }
				table.insert( chunks, current_chunk )
			end
			current_chunk.size = current_chunk.size + arg_size
			table.insert( current_chunk, arg )
		end
	end

	for chunk_num=1, #chunks do
		local chunk = chunks[ chunk_num ]
		
		net.Start("tsayc")
			net.WriteBool(chunk_num == #chunks)
			net.WriteInt( #chunk, 8 )
			for i=1, #chunk do
				local arg = chunk[ i ]
				if type( arg ) == "string" then
					net.WriteBool( true )
					net.WriteString( arg )
				else
					net.WriteBool( false )
					net.WriteColor( arg )
				end
			end
		
		
		if IsValid(ply) then
			net.Send(ply)
		else
			net.Broadcast()
		end

	end
end

if CLIENT then

	local accumulator = {}

	net.Receive( "tsayc", function( len )

		local last = net.ReadBool()
		local argn = net.ReadInt(8)
		for i=1, argn do
			if net.ReadBool() then
				table.insert( accumulator, net.ReadString() )
			else
				table.insert( accumulator, net.ReadColor() )
			end
		end
		
		if last then
			chat.AddText( unpack( accumulator ) )
			accumulator = {}
		end

	end )

end


--[[
	Function: tsayColor

	Prints a tsay message in color!

	Parameters:

		ply - The player to print to, set to nil to send to everyone. (Ignores this param if called on client)
		wait - *(Optional, defaults to false)* Wait one frame before posting. (Useful to use from things like chat hooks)
		... - color arg and text arg ad infinitum, color needs to come before the text it's coloring.

	Revisions:

		v2.40 - Initial.
]]
function ULib.tsayColor( ply, wait, ... )
	if SERVER and ply and not ply:IsValid() then ply = serverConsole end -- Mark as server

	if wait then ULib.namedQueueFunctionCall( "ULibChats", tsayColorCallback, ply, ... ) return end -- Call next frame
	tsayColorCallback( ply, ... )
end


--[[
	Function: tsayError

	Just like tsay, but prints the string in red

	Parameters:

		ply - The player to print to, set to nil to send to everyone. (Ignores this param if called on client)
		msg - The message to print.
		wait - *(Optional, defaults to false)* Wait one frame before posting. (Useful to use from things like chat hooks)

	Revisions:

		v2.40 - Initial.
]]
function ULib.tsayError( ply, msg, wait )
	return ULib.tsayColor( ply, wait, Color( 255, 140, 39 ), msg )
end


--[[
	Function: csay

	Prints a message in center of the screen as well as in the user's consoles.

	Parameters:

		ply - The player to print to, set to nil to send to everyone. (Ignores this param if called on client)
		msg - The message to print.
		color - *(Optional, defaults to 255, 255, 255, 255)* The color of the text.
		duration - *(Optional)* The amount of time to show the text.
		fade - *(Optional, defaults to 0.5)* The length of fade time

	Revisions:

		v2.10 - Added fade parameter. Fixed it sending the message multiple times.
		v2.40 - Changed to use clientRPC.
]]
function ULib.csay( ply, msg, color, duration, fade )
	if CLIENT then
		ULib.csayDraw( msg, color, duration, fade )
		Msg( msg .. "\n" )
		return
	end

	ULib.clientRPC( ply, "ULib.csayDraw", msg, color, duration, fade )
	ULib.console( ply, msg )
end


--[[
	Function: console

	Prints a message in the user's consoles.

	Parameters:

		ply - The player to print to, set to nil to send to everyone. (Ignores this param if called on client)
		msg - The message to print.
]]
function ULib.console( ply, msg )
	if CLIENT or (ply and not ply:IsValid()) then
		Msg( msg .. "\n" )
		return
	end

	if ply then
		ply:PrintMessage( HUD_PRINTCONSOLE, msg .. "\n" )
	else
		local players = player.GetAll()
		for _, player in ipairs( players ) do
			player:PrintMessage( HUD_PRINTCONSOLE, msg .. "\n" )
		end
	end
end


--[[
	Function: error

	Gives an error to console.

	Parameters:

		s - The string to use as the error message
]]
function ULib.error( s )
	if CLIENT then
		Msg( "[LC ULIB ERROR] " .. s .. "\n" )
	else
		Msg( "[LS ULIB ERROR] " .. s .. "\n" )
	end
end


--[[
	Function: debugFunctionCall

	Prints a function call, very useful for debugging.

	Parameters:

		name - The name of the function called.
		... - all arguments to the function.

	Revisions:

		v2.40 - Now uses print instead of Msg, since Msg seems to have a low max length.
			Changed how the variable length params work so you can pass nil followed by more params
]]
function ULib.debugFunctionCall( name, ... )
	local args = { ... }

	print( "Function '" .. name .. "' called. Parameters:" )
	for i=1, #args do
		local value = ULib.serialize( args[ i ] )
		print( "[PARAMETER " .. i .. "]: Type=" .. type( args[ i ] ) .. "\tValue=(" .. value .. ")" )
	end
end
