--[[
	Chat Traffic

	By sarf

	This mod allows the user to restrain the amount of chat traffic that his or her client sends.

	Thanks goes to the load of people who complained about getting disconnected. Whiners... ;)
	
	CosmosUI URL:
	http://www.cosmosui.org/forums/viewtopic.php?t=1614
	
   ]]


-- Constants
CHATTRAFFIC_TIME_SAVED_MAX = 2;

CHATTRAFFIC_CHARACTERSPERSECOND_MIN = 30;
CHATTRAFFIC_CHARACTERSPERSECOND_MAX = 512;

CHATTRAFFIC_TRAFFIC_DELAY = 0.2;

-- how big a chunk of the "cps left" needs to be unused for big messages to be passed through
CHATTRAFFIC_TRAFFIC_BIG_MESSAGE_CHUNK = 0.9;

-- Variables
ChatTraffic_Enabled = 1;
ChatTraffic_CharactersPerSecond = 255;

-- contains the current traffic statistics
ChatTraffic_Traffic = {};

ChatTraffic_Saved_SendChatMessage = nil;
ChatTraffic_Cosmos_Registered = 0;

-- executed on load, calls general set-up functions
function ChatTraffic_OnLoad()
	ChatTraffic_Register();
end

-- registers the mod with Cosmos
function ChatTraffic_Register_Cosmos()
	if ( ( Cosmos_UpdateValue ) and ( Cosmos_RegisterConfiguration ) and ( ChatTraffic_Cosmos_Registered == 0 ) ) then
		Cosmos_RegisterConfiguration(
			"COS_CHATTRAFFIC",
			"SECTION",
			TEXT(CHATTRAFFIC_CONFIG_HEADER),
			TEXT(CHATTRAFFIC_CONFIG_HEADER_INFO)
		);
		Cosmos_RegisterConfiguration(
			"COS_CHATTRAFFIC_HEADER",
			"SEPARATOR",
			TEXT(CHATTRAFFIC_CONFIG_HEADER),
			TEXT(CHATTRAFFIC_CONFIG_HEADER_INFO)
		);
		Cosmos_RegisterConfiguration(
			"COS_CHATTRAFFIC_ENABLED",
			"CHECKBOX",
			TEXT(CHATTRAFFIC_ENABLED),
			TEXT(CHATTRAFFIC_ENABLED_INFO),
			ChatTraffic_Toggle_Enabled_NoChat,
			ChatTraffic_Enabled
		);
		Cosmos_RegisterConfiguration(
			"COS_CHATTRAFFIC_CPS",
			"SLIDER",
			TEXT(CHATTRAFFIC_CHARACTERS_PER_SECOND),
			TEXT(CHATTRAFFIC_CHARACTERS_PER_SECOND_INFO),
			ChatTraffic_Change_CharactersPerSecond_NoChat,
			1,
			ChatTraffic_CharactersPerSecond,
			CHATTRAFFIC_CHARACTERSPERSECOND_MIN,
			CHATTRAFFIC_CHARACTERSPERSECOND_MAX,
			CHATTRAFFIC_CPS_SLIDER_DESCRIPTION,
			1,
			1,
			CHATTRAFFIC_CPS_SLIDER_APPEND,
			1
		);
		if ( Cosmos_RegisterChatCommand ) then
			local ChatTrafficMainCommands = {"/chattraffic","/ct"};
			Cosmos_RegisterChatCommand (
				"CHATTRAFFIC_MAIN_COMMANDS", -- Some Unique Group ID
				ChatTrafficMainCommands, -- The Commands
				ChatTraffic_Main_ChatCommandHandler,
				CHATTRAFFIC_CHAT_COMMAND_INFO -- Description String
			);
		end
		ChatTraffic_Cosmos_Registered = 1;
	end
end

-- registers the mod with the system, integrating it with slash commands and "master" AddOns
function ChatTraffic_Register()
	if ( Cosmos_RegisterConfiguration ) and ( Cosmos_UpdateValue ) then
		ChatTraffic_Register_Cosmos();
	else
		SlashCmdList["CHATTRAFFICSLASHMAIN"] = ChatTraffic_Main_ChatCommandHandler;
		SLASH_CHATTRAFFICSLASHMAIN1 = "/chattraffic";
		SLASH_CHATTRAFFICSLASHMAIN2 = "/ct";
		this:RegisterEvent("VARIABLES_LOADED");
	end
end

function ChatTraffic_Extract_NextParameter(msg)
	local params = msg;
	local command = params;
	local index = strfind(command, " ");
	if ( index ) then
		command = strsub(command, 1, index-1);
		params = strsub(params, index+1);
	else
		params = "";
	end
	return command, params;
end

-- Handles chat - e.g. slashcommands - enabling/disabling the ChatTraffic
function ChatTraffic_Main_ChatCommandHandler(msg)
	
	local func = ChatTraffic_Toggle_Enabled;
	
	local toggleFunc = true;
	
	if ( ( not msg) or ( strlen(msg) <= 0 ) ) then
		ChatTraffic_Print(CHATTRAFFIC_CHAT_COMMAND_USAGE);
		return;
	end
	
	local commandName, params = ChatTraffic_Extract_NextParameter(msg);
	
	if ( ( commandName ) and ( strlen(commandName) > 0 ) ) then
		commandName = string.lower(commandName);
	else
		commandName = "";
	end
	
	if ( strfind( commandName, "state" ) ) then
		func = ChatTraffic_Toggle_Enabled;
	elseif ( ( strfind( commandName, "cps" ) ) or ( strfind( commandName, "traffic" ) ) )then
		func = ChatTraffic_Change_CharactersPerSecond;
		toggleFunc = false;
		local cps = nil;
		cps, params = ChatTraffic_Extract_NextParameter(params);
		func(cps);
		return;
	else
		ChatTraffic_Print(CHATTRAFFIC_CHAT_COMMAND_USAGE);
		return;
	end
	
	if ( toggleFunc ) then
		-- Toggle appropriately
		if ( (string.find(params, 'on')) or ((string.find(params, '1')) and (not string.find(params, '-1')) ) ) then
			func(1);
		else
			if ( (string.find(params, 'off')) or (string.find(params, '0')) ) then
				func(0);
			else
				func(-1);
			end
		end
	else
		func();
	end
end

function ChatTraffic_GetMaximumCharactersPerSecondLeft()
	return ( ChatTraffic_CharactersPerSecond * CHATTRAFFIC_TIME_SAVED_MAX );
end

function ChatTraffic_GetCharactersPerSecondLeft()
	ChatTraffic_UpdateTrafficSent();
	local i = 0;
	for k, v in ChatTraffic_Traffic do
		i = i + v.chars;
	end
	return ( ChatTraffic_GetMaximumCharactersPerSecondLeft() - i);
end

function ChatTraffic_ExemptFromTrafficLimitation(message)
	if ( ( not message ) or ( strlen(message) <= 0 ) or ( strsub(message, 1, 1) == "/") ) then
		return true;
	else
		return false;
	end
end

function ChatTraffic_AddTrafficSent(message)
	if ( ChatTraffic_ExemptFromTrafficLimitation(message) ) then
		return;
	end
	ChatTraffic_UpdateTrafficSent();
	table.insert(ChatTraffic_Traffic, { chars = strlen(message), time = GetTime() } );
end

function ChatTraffic_UpdateTrafficSent()
	local curTime = GetTime();
	local removeIndices = {};
	for k, v in ChatTraffic_Traffic do
		if ( curTime-v.time >= CHATTRAFFIC_TIME_SAVED_MAX ) then
			table.insert(removeIndices, k);
		end
	end
	for k, v in removeIndices do
		table.remove(ChatTraffic_Traffic, v);
	end
end

-- Does things with the hooked function
function ChatTraffic_SendChatMessage(message, ...)
	if ( ( ChatTraffic_Enabled == 1 ) and ( not ChatTraffic_ExemptFromTrafficLimitation(message) ) ) then
		local messageLen = strlen(message);
		local maxTraffic = ChatTraffic_GetMaximumCharactersPerSecondLeft();
		local currentTrafficLeft = ChatTraffic_GetCharactersPerSecondLeft();
		if ( messageLen >= maxTraffic ) then
			if ( currentTrafficLeft >= (maxTraffic*CHATTRAFFIC_TRAFFIC_BIG_MESSAGE_CHUNK) ) then
				ChatTraffic_Saved_SendChatMessage(message, unpack(arg));
				ChatTraffic_AddTrafficSent(message);
				return;
			end
		end
		if ( currentTrafficLeft < messageLen ) then
			Cosmos_Schedule(CHATTRAFFIC_TRAFFIC_DELAY, ChatTraffic_SendChatMessage, message, unpack(arg));
			return;
		else
			ChatTraffic_AddTrafficSent(message);
		end
	end
	ChatTraffic_Saved_SendChatMessage(message, unpack(arg));
end

-- Hooks/unhooks functions. If toggle is 1, hooks functions, otherwise it unhooks functions.
--  Hooking functions mean that you replace them with your own functions and then call the 
--  original function at your leisure.
function ChatTraffic_Setup_Hooks(toggle)
	if ( toggle == 1 ) then
		if ( ( SendChatMessage ~= ChatTraffic_SendChatMessage ) and (ChatTraffic_Saved_SendChatMessage == nil) ) then
			ChatTraffic_Saved_SendChatMessage = SendChatMessage;
			SendChatMessage = ChatTraffic_SendChatMessage;
		end
	else
		if ( SendChatMessage == ChatTraffic_SendChatMessage) then
			SendChatMessage = ChatTraffic_Saved_SendChatMessage;
			ChatTraffic_Saved_SendChatMessage = nil;
		end
	end
end

-- Handles events
function ChatTraffic_OnEvent(event)
	if ( event == "VARIABLES_LOADED" ) then
		if ( ChatTraffic_Cosmos_Registered == 0 ) then
			local value = getglobal("ChatTraffic_Enabled");
			if (value == nil ) then
				-- defaults to off
				value = 0;
			end
			ChatTraffic_Toggle_Enabled(value);
			local value = getglobal("ChatTraffic_CharactersPerSecond");
			if (value == nil ) then
				-- defaults to 60
				value = 60;
			end
			ChatTraffic_Change_CharactersPerSecond(value);
		end
	end
end

-- Toggles the enabled/disabled state of an option and returns the new state
--  if toggle is 1, it's enabled
--  if toggle is 0, it's disabled
--   otherwise, it's toggled
function ChatTraffic_Generic_Toggle(toggle, variableName, CVarName, enableMessage, disableMessage, CosmosVarName)
	local oldvalue = getglobal(variableName);
	local newvalue = toggle;
	if ( ( toggle ~= 1 ) and ( toggle ~= 0 ) ) then
		if (oldvalue == 1) then
			newvalue = 0;
		elseif ( oldvalue == 0 ) then
			newvalue = 1;
		else
			newvalue = 0;
		end
	end
	setglobal(variableName, newvalue);
	setglobal(CVarName, newvalue);
	if ( newvalue ~= oldvalue ) then
		local text = "";
		if ( newvalue == 1 ) then
			if ( enableMessage ) then
				text = TEXT(getglobal(enableMessage));
			end
		else
			if ( disableMessage ) then
				text = TEXT(getglobal(disableMessage));
			end
		end
		if ( text ) and ( strlen(text) > 0 ) then
			ChatTraffic_Print(text);
		end
	end
	ChatTraffic_Register_Cosmos();
	RegisterForSave(variableName);
	if ( ChatTraffic_Cosmos_Registered == 1 ) then 
		ChatTraffic_Generic_CosmosUpdateCheckOnOff(CVarName, newvalue);
		ChatTraffic_Generic_CosmosUpdateCheckOnOff(CosmosVarName, newvalue);
	end
	return newvalue;
end

function ChatTraffic_Generic_CosmosUpdateCheckOnOff(varName, value)
	if ( not Cosmos_UpdateValue ) then
		return;
	end
	local name = varName;
	if ( ( not name ) or ( strlen(name) <= 0 ) ) then
		return
	end
	if ( strfind(name, "_X" ) ) then
		name = strsub(name, 1, strlen(name)-2);
	end
	if ( ( name ) and ( strlen(name) > 0 ) ) then
		Cosmos_UpdateValue(name, CSM_CHECKONOFF, value);
	end
end

function ChatTraffic_Generic_CosmosUpdateValue(varName, value)
	if ( not Cosmos_UpdateValue ) then
		return;
	end
	local name = varName;
	if ( ( not name ) or ( strlen(name) <= 0 ) ) then
		return
	end
	if ( strfind(name, "_X" ) ) then
		name = strsub(name, 1, strlen(name)-2);
	end
	if ( ( name ) and ( strlen(name) > 0 ) ) then
		Cosmos_UpdateValue(name, CSM_SLIDERVALUE, value);
	end
end



-- Toggles the enabled/disabled state of the ChatTraffic
--  if toggle is 1, it's enabled
--  if toggle is 0, it's disabled
--   otherwise, it's toggled
function ChatTraffic_Toggle_Enabled(toggle)
	ChatTraffic_DoToggle_Enabled(toggle, true);
end

-- does the actual toggling
function ChatTraffic_DoToggle_Enabled(toggle, showText)
	local newvalue = 0;
	if ( showText ) then
		newvalue = ChatTraffic_Generic_Toggle(toggle, "ChatTraffic_Enabled", "COS_CHATTRAFFIC_ENABLED_X", "CHATTRAFFIC_CHAT_ENABLED", "CHATTRAFFIC_CHAT_DISABLED");
	else
		newvalue = ChatTraffic_Generic_Toggle(toggle, "ChatTraffic_Enabled", "COS_CHATTRAFFIC_ENABLED_X");
	end
	ChatTraffic_Setup_Hooks(newvalue);
end

-- toggling - no text
function ChatTraffic_Toggle_Enabled_NoChat(toggle)
	ChatTraffic_DoToggle_Enabled(toggle, false);
end

function ChatTraffic_Change_CharactersPerSecond_NoChat(toggle, value)
	ChatTraffic_DoChange_CharactersPerSecond(value, "ChatTraffic_CharactersPerSecond", "COS_CHATTRAFFIC_CPS");
end

function ChatTraffic_Change_CharactersPerSecond(value)
	ChatTraffic_DoChange_CharactersPerSecond(value, "ChatTraffic_CharactersPerSecond", "COS_CHATTRAFFIC_CPS", "CHATTRAFFIC_CHAT_CHARACTERS_PER_SECOND");
end

function ChatTraffic_DoChange_CharactersPerSecond(value, variableName, CVarName, message, CosmosVarName)
	local oldvalue = getglobal(variableName);
	local newvalue = value;
	if ( newvalue <= CHATTRAFFIC_CHARACTERSPERSECOND_MIN ) then
		newvalue = CHATTRAFFIC_CHARACTERSPERSECOND_MIN;
	end
	setglobal(variableName, newvalue);
	setglobal(CVarName, newvalue);
	if ( newvalue ~= oldvalue ) then
		local text = "";
		if ( message ) then
			text = TEXT(getglobal(message));
		end
		if ( text ) and ( strlen(text) > 0 ) then
			ChatTraffic_Print(text);
		end
	end
	ChatTraffic_Register_Cosmos();
	RegisterForSave(variableName);
	if ( ChatTraffic_Cosmos_Registered == 1 ) then 
		ChatTraffic_Generic_CosmosUpdateCheckOnOff(CVarName, newvalue);
		ChatTraffic_Generic_CosmosUpdateCheckOnOff(CosmosVarName, newvalue);
	end
	return newvalue;
end

-- Prints out text to a chat box.
function ChatTraffic_Print(msg,r,g,b,frame,id,unknown4th)
	if ( Print ) then
		Print(msg, r, g, b, frame, id, unknown4th);
	end
	if(unknown4th) then
		local temp = id;
		id = unknown4th;
		unknown4th = id;
	end
				
	if (not r) then r = 1.0; end
	if (not g) then g = 1.0; end
	if (not b) then b = 0.0; end
	if ( frame ) then 
		frame:AddMessage(msg,r,g,b,id,unknown4th);
	else
		if ( DEFAULT_CHAT_FRAME ) then 
			DEFAULT_CHAT_FRAME:AddMessage(msg, r, g, b,id,unknown4th);
		end
	end
end
