--[[

	The Cosmos Configuration Master

	   This is a set of functions and a window, designed
	to 	accept a list of variables, editable in a simple
	configuration view.

	by Alexander Brazie

	==> Demo at the bottom <==
	
	Revision: $Rev: 2105 $
	Last Author: $Author: Arbi $
	Last Change: $Date: 2006-11-10 18:47:10 +0900 (Fri, 17 Nov 2006) $
 ]]--

-- Global values
local COSMOSMASTER_VERSION = "$Rev: 2105 $";
local COSMOSMASTER_UPDATE = "$Date: 2006-11-10 18:47:10 +0900 (Fri, 17 Nov 2006) $";
HANULARIMASTER_UPDATE = "2007년 11월 15일";
HANULARIMASTER_VERSION = "하늘아리 2.8";

-- These contain the registered variables, and their respective settings
CosmosMaster_Configurations = { };
local CosmosMaster_ChatCommands = { };
CosmosMaster_ChatWatches = { };
local CosmosMaster_ChatTypeMap = { };
CosmosMaster_Buttons = { };

-- Keywords (for use by anyone)
CSM_VARIABLE = "variable";
CSM_TYPE	= "type";
CSM_STRING	= "string";
CSM_DESCRIPTION = "description";
CSM_HANDLER = "onchange";
CSM_CHECKONOFF = "checked";
CSM_SLIDERSTRING = "substring";
CSM_DEFAULTVALUE = "slidervalue";
CSM_SLIDERVALUE = "slidervalue";
CSM_SLIDERMIN = "slidermin";
CSM_SLIDERMAX = "slidermax";
CSM_SLIDERSTEP = "sliderstep";
CSM_SLIDERVALUETEXTTOGGLE = "slidervaluetexttoggle";
CSM_SLIDERTEXTAPPEND = "append";
CSM_SLIDERVALUETEXTMULTIPLIER = "multiplier";
CSM_SECTION = "section";
CSM_ID = "id";
CSM_NAME = "name";
CSM_ICON = "icon";
CSM_LONGDESCRIPTION = "longdescription";
CSM_CALLBACK = "callback";
CSM_TESTFUNCTION = "testfunction";
CSM_ALIASES = "aliases";
CSM_PREVIOUSFUNCTION = "prevfunction";
CSM_CHAINPRE = "chainpre"; -- Chain commands before you call yourself
CSM_CHAINPOST = "chainpost"; -- Chain commands after you call yourself
CSM_CHAINNONE = "chainnone"; -- Don't chain
CSM_CHAINPREVIOUSONLY = "chainpreviousonly"; -- Ignore yourself if you already exist
CSM_TYPELIST = "typelist";
CSM_CHANNELINDEX = "chanindex";
CSM_CHANNELNAME = "channame";
CSM_CHANNELDESC = "chandesc";
CSM_CHANNELSTATE = "chanstate";
CSM_CHANNELCREATE = "chantimeorder";

-- These contain global variables, used for timing/state checks
CosmosMaster_LastPlayer = nil;
CosmosMaster_LastRealm = nil;
CosmosMaster_ChansLoaded = nil;
local CosmosMaster_LastAsked = nil;
CosmosMaster_Section = "COS_COS";
local CosmosMaster_LastSection = CosmosMaster_Section;
local CosmosMaster_LastSeparator = nil;
local CosmosMaster_RunOnce = false;

-- This registers a variable with the configuration client. --

--[[

   This MUST be called for a variable to appear in the configuration menu.

	ParameterList:
   cos_variable - the global var used and string for the CVar
   cos_type - CHECKBOX, SLIDER, BOTH, BUTTON, SEPARATOR, or SECTION
   cos_string - Associated string
   cos_description -  A three line max description of the variable for the users sake
   cos_handerfunction - Function called with value as first parameter, and second as toggle state when changed.
   cos_defaultcheckedvalue - 1 = Checked, 0 = Unchecked
   cos_defaultvalue - Default value returned when checked
   cos_min - Minimum slider value
   cos_max - Maximum slider value
   cos_substring - Slider or button text
   cos_sliderstep - Increments at which the slider may move
   cos_slidervaluetexton - Toggle for the exact value display of a slider
   cos_slidervalueappend - Whats added to the end of the number
   cos_slidervaluemultiplier - Whats the value multiplied by for display.

  ]]--

function Cosmos_RegisterConfiguration(
	cos_variable,
	cos_type,
	cos_string,
	cos_description,
	cos_handlerfunction,
	cos_defaultcheckedvalue,
	cos_defaultvalue,
	cos_min,
	cos_max,
	cos_substring,
	cos_sliderstep,
	cos_slidervaluetexton,
	cos_slidervalueappend,
	cos_slidervaluemultiplier )

    -- Prepare for nil values
	if ( cos_description == nil ) then cos_description = ""; end
	if ( cos_defaultcheckedvalue == nil or cos_defaultcheckedvalue == false or cos_defaultcheckedvalue == 0 ) then cos_defaultcheckedvalue = 0;
	else cos_defaultcheckedvalue = 1; end
	if ( cos_defaultvalue == nil ) then cos_defaultvalue = 1; end
	if ( cos_min == nil  ) then cos_min = 0; end
	if ( cos_max == nil  ) then cos_max = 1; end
	if ( cos_slidervalueappend == nil ) then cos_slidervalueappend = ""; end
	if ( cos_slidervaluemultiplier == nil ) then cos_slidervaluemultiplier = 1; end
	if ( cos_type == nil ) then cos_type = "nil"; end
	if ( cos_substring == nil  ) then cos_substring = ""; end
	if ( cos_sliderstep == nil ) then cos_sliderstep = .01; end
	if ( cos_handlerfunction == nil ) then cos_handlerfunction = function ( x ) return; end; end;
	if ( cos_slidervaluetexton == nil or cos_slidervaluetexton == 0) then cos_slidervaluetexton = 0;
	else cos_slidervaluetexton = 1;
	end

	if (not string.find ( cos_variable, "COS_" )) then
		Sea.io.print ( "Invalid Prefix" );
		return; -- We won't permit CVars that could mess up the game
				-- So they must be prefixed with COS_
	end
	
	-- Check the specified type
	if ( not cos_type == "CHECKBOX" and not cos_type == "SLIDER" and not cos_type == "SEPARATOR" and not cos_type == "BOTH" and not cos_type == "BUTTON" and not cos_type == "SECTION" ) then
		Sea.io.print ( "Invalid Type: "..cos_type );
		return; -- Return if it does not suit our handleable types
	end
        if ( cos_type == "SLIDER" or cos_type == "BOTH" ) then
		if ( cos_defaultvalue == nil) then cos_defaultvalue = 1; end
	end
	if ( cos_type == "SECTION" ) then
		CosmosMaster_LastSection = cos_variable;
	end
	if ( cos_type == "SEPARATOR" ) then
		CosmosMaster_LastSeparator = cos_variable;
	end

	local newRegistrant = {
		[CSM_VARIABLE] = cos_variable,
		[CSM_TYPE]	= cos_type,
		[CSM_STRING]	= cos_string,
		[CSM_DESCRIPTION] = cos_description,
		[CSM_SLIDERSTRING] = cos_substring,
		[CSM_CHECKONOFF] = cos_defaultcheckedvalue,
		[CSM_SLIDERMIN] = cos_min,
		[CSM_SLIDERMAX] = cos_max,
		[CSM_SLIDERVALUE] = cos_defaultvalue,
		[CSM_SLIDERSTEP] = cos_sliderstep,
		[CSM_SLIDERVALUETEXTTOGGLE] = cos_slidervaluetexton,
		[CSM_SLIDERVALUETEXTMULTIPLIER] = cos_slidervaluemultiplier,
		[CSM_SLIDERTEXTAPPEND] = cos_slidervalueappend,
		[CSM_HANDLER] = cos_handlerfunction,
		[CSM_SECTION] = CosmosMaster_LastSection
	}
	
	-- Check if its already been registered and flag an error
	local found = false;
	for k,v in pairs(CosmosMaster_Configurations) do
		if ( CosmosMaster_Configurations[k][CSM_VARIABLE] == cos_variable ) then
			if (((CosmosMaster_Configurations[k][CSM_TYPE] == "SECTION") and (cos_type == "SECTION")) or ((CosmosMaster_Configurations[k][CSM_TYPE] == "SEPARATOR") and (cos_type == "SEPARATOR"))) then
				return true;
			else
				--Sea.io.print ( " Cosmos Register Configuration Error: Duplicate Configuration - "..cos_variable );
				found = true;
			end
		end
	end
	if ( found == false ) then
		local index = nil;
		if (CosmosMaster_LastSection and (cos_type ~= "SECTION")) then
			local foundSection = false;
			local foundSeparator = false;
			for k in pairs(CosmosMaster_Configurations) do
				if (foundSeparator and ((CosmosMaster_Configurations[k][CSM_TYPE] == "SECTION") or (CosmosMaster_Configurations[k][CSM_TYPE] == "SEPARATOR"))) then
					break;
				end
			 	if (foundSection and CosmosMaster_LastSeparator and (CosmosMaster_Configurations[k][CSM_VARIABLE] == CosmosMaster_LastSeparator)) then
			 		foundSeparator = true;
			 	end
			 	if (foundSection and (CosmosMaster_Configurations[k][CSM_TYPE] == "SECTION")) then
					break;
				end
				if (CosmosMaster_Configurations[k][CSM_VARIABLE] == CosmosMaster_LastSection) then
			 		foundSection = true;
			 	end
			 	if (foundSection) then
			 		index = k;
			 	end
			end
		end
		-- Add the new registrant to the list
		if (index) then
			table.insert ( CosmosMaster_Configurations, index + 1, newRegistrant );
		else
			table.insert ( CosmosMaster_Configurations, newRegistrant );
		end
	end

	--if ( cos_type=="SLIDER" or  cos_type=="BOTH" ) then
		Cosmos_RegisterCVar(cos_variable, cos_defaultvalue);
	--end
	--if (  cos_type=="CHECKBOX" or  cos_type=="BOTH" ) then
		Cosmos_RegisterCVar(cos_variable.."_X", cos_defaultcheckedvalue);
	--end
			
	return true;

end

--[[
	Cosmos_UpdateValue allows you to manually update a parameter.

	I presume upon the user to use this properly.
	If you break it, tough luck. No error checking here.

	parameter list:

	cos_uniqueid - UniqueID (CVar) (technically it updates every match)
	cos_parameter - Parameter to be updated,
	cos_value - new value to be set.

	Example usage
	Cosmos_UpdateValue( "COS_MYVAR", CSM_TYPE, "BUTTON" );
	Cosmos_UpdateValue( "COS_MYVAR", CSM_SLIDERVALUETEXTMULTIPLIER, UnitHealthMax("player") );
   ]]--

function Cosmos_UpdateValue( cos_uniqueid, cos_parameter, cos_value )
	
	if( cos_parameter == CSM_CHECKONOFF ) then
		if ( cos_value+0 == 0 or cos_value+0 == 1 ) then 
		else return; end
	end
	if( cos_parameter == CSM_TYPE ) then
		if ( cos_value == "SLIDER" or cos_value == "CHECKBOX" or cos_value == "BOTH"
		or  cos_value == "BUTTON" or cos_value == "SEPARATOR" or cos_value == "SECTION" ) then
		else return; end
	end
	if( cos_parameter == CSM_SLIDERSTEP ) then
		if ( cos_value == 0 ) then cos_value = .01;
		else return; end
	end
	
	for index, value in pairs(CosmosMaster_Configurations) do
		if ( value[CSM_VARIABLE] == cos_uniqueid ) then
			value[cos_parameter] = cos_value; -- Hope the user doesnt break it.
		end
	end

end

--[[ 

     Cosmos_RegisterChatCommand 
     
     Allows you to register an in-game chat command with Cosmos's simple registration system.
     It will also cause your command to be listed with the /help or /eshelp command locally.

	usage:
	
	 Cosmos_RegisterChatCommand( GroupID, Array of commands, handler of 1 argument, chaining instructions );
	 
	example:		this.chatFrame.channelList[i] = name;
		this.chatFrame.zoneChannelList[i] = zoneChannel;
	function MyCallback (message) Sea.io.print(message); end
	 MyCommands = { "/testa", "/testb", "/testc" }
	 Cosmos_RegisterChatCommand( "TESTGROUP", MyCommands, MyCallback, "My Function", CSM_CHAINPOST);
	 
	 Will create a command that executes MyCallback, and anything else that was listed for testgroup previously.

	]]--

function Cosmos_RegisterChatCommand( cos_groupid, cos_commands, cos_handler, cos_descriptionstring, cos_chaintype )
	-- Error checking. 
	if ( cos_groupid == nil ) then 
		--Sea.io.print( " No Group ID specified." );
		return;
	end
	if ( cos_commands == nil ) then 
		Sea.io.print( cos_groupid.." No /command specified." );
		return;
	end
	if ( cos_handler == nil ) then 
		Sea.io.print( cos_groupid.." No functions specified." );
		return;
	end
	
	-- Adds the command handler to the chat global pane
	cmdlist = getglobal ( "SlashCmdList" );
	
	local cos_prevfunc = nil;
	
	-- Save the old commands if you're overwriting one
	if ( cmdlist [ cos_groupid ] ~= nil ) then
		cos_prevfunc = cmdlist [ cos_groupid ] ;
	end
	
	-- Error checking
	if ( cos_prevfunc == nil ) then 
		cos_prevfunc = function () return; end;
	end
	
	-- Set the new command
	if ( cos_chaintype == CSM_CHAINPRE ) then
		cmdlist [ cos_groupid ] 
			= function (msg)
				cos_prevfunc(msg);
				cos_handler(msg);
			end;
	end

	if ( cos_chaintype == CSM_CHAINPOST ) then
		cmdlist [ cos_groupid ] 
			= function (msg)
				cos_handler(msg);
				cos_prevfunc(msg);
			end;
	end
	if ( cos_chaintype == CSM_CHAINPREVIOUSONLY ) then
		cmdlist [ cos_groupid ] 
			= function (msg)
				cos_prevfunc(msg);
			end;
	end
	if ( cos_chaintype == nil or cos_chaintype == CSM_CHAINNONE ) then
		cmdlist [ cos_groupid ] 
			= function (msg)
				cos_handler(msg);
			end;
	end
	
	if ( cos_chaintype == CSM_CHAINNONE or cos_chaintype == nil ) then
		SlashCmdList[cos_groupid] = cos_handler;
		for i = 1, #cos_commands do
			setglobal("SLASH_"..cos_groupid..i, cos_commands[i])
		end
	end

	-- Create a new command and save it locally.
	if ( Sky ) then
		Sky.registerSlashCommand(
			{
				id=cos_groupid,
				commands=cos_commands,
				onExecute=cos_handler,
				action=cos_prevfunc,
				helpText=cos_descriptionstring
			} 
		);
	else
		newChatCommand = {};
		newChatCommand[CSM_ID] = cos_groupid;
		newChatCommand[CSM_ALIASES] = cos_commands;
		newChatCommand[CSM_HANDLER] = cos_handler;
		newChatCommand[CSM_DESCRIPTION] = cos_descriptionstring;
		newChatCommand[CSM_PREVIOUSFUNCTION] = cos_prevfunc;

		tinsert(CosmosMaster_ChatCommands, newChatCommand);
	end
end

--[[

	RegisterButton

	 Allow you to create a button of your mod in the Cosmos Features Frame.
	 
	 Usage: 
	 
		Cosmos_RegisterButton ( name, description, ToolTip_description, icon, callback, testfunction ) 
		
	 Example:
		
		Cosmos_RegisterButton ( 
			"Name", 
			"Little Text", 
			"Long Tool Tip Text", 
			"Interface\\Icons\\Spell_Holy_BlessingOfStrength", 
			function()
				if (GamesListFrame:IsVisible()) then 
					HideUIPanel(GamesListFrame); 
				else 
					ShowUIPanel(GamesListFrame); 
				end 
			end,
			function() 
				if (UnitInParty("party1")) then 
					return true; -- The button is enabled
				else
					return false; -- The button is disabled
				end
			end
			);
		
		A button will be created in the Features Frame.
		
		Description must not be more than 2 words, you should put a longer description in the tool tip.
		
	 ]]--

function Cosmos_RegisterButton ( cos_name, cos_description, cos_longdescription, cos_icon, cos_callback, cos_testfunction ) 
	if ( cos_name == nil ) then 
		Sea.io.print ( "Missing a name for the Button.");
	end
	if ( cos_icon == nil ) then 
		Sea.io.print ( "Missing an icon path for the Button.");
	end
	if ( cos_callback == nil ) then 
		Sea.io.print ( "Missing a callback for the Button.");
	end
	if ( cos_testfunction == nil ) then 
		cos_testfunction = function () return true; end;
	end

	temp = { };
	temp[CSM_NAME] = cos_name;
	temp[CSM_DESCRIPTION] = cos_description;
	temp[CSM_LONGDESCRIPTION] = cos_longdescription;
	temp[CSM_ICON] = cos_icon;
	temp[CSM_CALLBACK] = cos_callback;
	temp[CSM_TESTFUNCTION] = cos_testfunction;
	
	tinsert ( CosmosMaster_Buttons, temp );

	local loaded = LoadAddOn("CosmosMenu");
	if ( loaded ) then
		CosmosButton_UpdateButton();
	end
end

--[[

	RegisterChatWatch
	
	 Allows you to register a command to be called when a condition 
	 is met within a block of text in incoming chat messages. 
	 
	 Usage: 
	 
		Cosmos_RegisterChatWatch ( id, typearray, handlerfunction, description );
		
	 Example;
		
		Cosmos_RegisterChatWatch ( "NOYELLS", {"YELL"}, function (msg) return 0; end );
		
		This would create a function which disables yells by returning a 0 
		(indicating to halt all commands) whenever a type 'YELL' is seen.
		
		Description is entirely for debugging and observational purposes.
	 
	    You can watch the cosmos channel, just use CHANNEL_COSMOS instead of the channel type.
	    You can watch the cosmos party channel, just use CHANNEL_PARTY instead of the channel type.

	 ]]--

function Cosmos_RegisterChatWatch ( cos_id, cos_typelist, cos_handler, cos_description ) 
	
	if ( cos_id == nil ) then 
		Sea.io.print ( "Missing an ID for ChatWatch.");
	end
	if ( cos_typelist == nil or getn( cos_typelist) == 0 ) then 
		Sea.io.print ( "No chat types specified for observation");
	end
	if ( cos_handler == nil ) then 
		Sea.io.print ( "No handler function specified to apply to chat messages");
	end

	newChatWatch = {};
	newChatWatch[CSM_ID] = cos_id;
	newChatWatch[CSM_TYPELIST] = cos_typelist;
	newChatWatch[CSM_HANDLER] = cos_handler;
	newChatWatch[CSM_DESCRIPTION] = cos_description;
	
	-- Ignore this really useless line
	tinsert ( CosmosMaster_ChatWatches, newChatWatch );
	for k,v in pairs(cos_typelist) do
		if ( CosmosMaster_ChatTypeMap[v] == nil ) then CosmosMaster_ChatTypeMap[v] = {}; end
		tinsert ( CosmosMaster_ChatTypeMap[v], {handler=cos_handler;id=cos_id} );
	end
end

-- Basic Initialization of global variables
function CosmosMaster_Init()
	-- Add CVar watching
        this:RegisterEvent("VARIABLES_LOADED");
	this:RegisterEvent("PLAYER_ENTERING_WORLD");

	Sea.util.hook("ChatEdit_UpdateHeader", "Cosmos_ChatEdit_UpdateHeader", "replace");
	Sea.util.hook("ChatEdit_AddHistory", "Cosmos_ChatEdit_AddHistory", "replace");
	hooksecurefunc("ChatEdit_HandleChatType", Cosmos_ChatEdit_HandleChatType);
	hooksecurefunc("ChatEdit_ParseText", Cosmos_ChatEdit_ParseCosmosCommand);
end
  
-- Event handling
function CosmosMaster_OnEvent(event)
   if (event == "VARIABLES_LOADED") then
	-- Register Cosmos chat commands
	Cosmos_RegisterCosmosChatCommands();
   end
   if (event == "PLAYER_ENTERING_WORLD") then
	if ( not CosmosMaster_RunOnce ) then
		CosmosMaster_RunCallBack();
		local loaded = LoadAddOn("CosmosMenu");
		if ( loaded ) then
			GameMenuFrame:SetHeight(GameMenuFrame:GetHeight()+36);
		end
		CosmosMaster_RunOnce = true;
	end
   end
end

function CosmosMaster_RunCallBack()
	playername = UnitName("player");
	if (playername) then
		if ((playername ~= UKNOWNBEING) and ( playername ~= UNKNOWNOBJECT )) then
			CosmosMaster_LastPlayer = CosmosMaster_Global_CVars[COS_LAST_PLAYER];
			CosmosMaster_LastRealm = CosmosMaster_Global_CVars[COS_LAST_REALM];
			CosmosMaster_Global_CVars[COS_LAST_PLAYER] = playername;
			CosmosMaster_Global_CVars[COS_LAST_REALM] = GetCVar("realmName");
			Cosmos_RegisterMissed();
			Cosmos_SetMissed();
			CosmosMaster_LoadVariables();
			CosmosMaster_SyncVars();
			Cosmos_CallVarsLoaded();
		end
	end
end

-- Basic Saving
function CosmosMaster_Save()
	CosmosMaster_StoreVariables();
	CosmosMaster_SyncVars();
end

-- Basic Reverting
function CosmosMaster_Reset()
	CosmosMaster_LoadVariables();
	CosmosMaster_SyncVars();
end

-- Displays the commands help list  
function CosmosMaster_ChatCommandsHelpDisplay (msg) 
	Sea.io.print ( "하늘아리 도움말: " );
	for index, value in pairs(CosmosMaster_ChatCommands) do
		if ( value[CSM_DESCRIPTION] == "" ) then 
		else 
			local aliases = value[CSM_ALIASES];
			if ( aliases and value[CSM_DESCRIPTION]) then 
				local helpmsg = "- "..aliases[1].." - "..value[CSM_DESCRIPTION];
				Sea.io.printc ( {r=.7,g=.6,b=.8}, helpmsg );
			end
		end
	end
	
end

-- Shows the current version.
function CosmosMaster_ChatVersionDisplay(msg)
	 Sea.io.print ( "하늘아리 버전: "..HANULARIMASTER_VERSION.."\n".."제작: 와우메카(http://wow.gamemeca.com)" );
	 Sea.io.print ( "만든날: "..HANULARIMASTER_UPDATE );
end

-- Returns the command alias list for a registered command.
function CosmosMaster_GetCommandAliases ( cos_id ) 
	--Sea.io.print(cos_id, .5, .5, .5); -- Debug:  This will list all commands passed through

	for index, value in pairs(CosmosMaster_ChatCommands) do
		if ( value[CSM_ID] == cos_id ) then
			return value[CSM_ALIASES];
		end
	end
	-- If none was found, return nil
	return nil;
end

-- Gets the prior functions for a registered command
function CosmosMaster_GetPreviousCommand ( cos_id ) 
	for index, value in pairs(CosmosMaster_ChatCommands) do
		if ( value[CSM_ID] == cos_id ) then
			return value[CSM_PREVIOUSFUNCTION];
		end
	end
	-- If none was found, return nil
	return nil;

end

-- Default behaviour
function CosmosMaster_CheckGui( slot )
	--SendChatMessage( "Error, invalid value at slot" + slot );
end

function Cosmos_UserMessage(message)
	Sea.IO.print(message);
end

function Cosmos_DontHaveCosmos(user)
	if (strupper(CosmosMaster_LastAsked) == strupper(user)) then
		Cosmos_UserMessage(format(COSMOS_DONTHAVE,user));
	end
end

-- Hooking ChatEdit functions
function Cosmos_ChatEdit_UpdateHeader(editBox)
	local type = editBox:GetAttribute("chatType");
	if ( not type ) then
		return;
	end

	local info = ChatTypeInfo[type];
	local header = getglobal(editBox:GetName().."Header");
	if ( not header ) then
		return;
	end

	if ( type == "WHISPER" ) then
		header:SetText(format(TEXT(getglobal("CHAT_WHISPER_SEND")), editBox:GetAttribute("tellTarget")));
	elseif ( type == "EMOTE" ) then
		header:SetText(format(TEXT(getglobal("CHAT_EMOTE_SEND")), UnitName("player")));
	elseif ( type == "CHANNEL" ) then
		local channel, channelName, instanceID = GetChannelName(editBox:GetAttribute("channelTarget"));
		if ( channelName ) then
			if ( instanceID > 0 ) then
				channelName = channelName.." "..instanceID;
			end
			info = ChatTypeInfo["CHANNEL"..channel];
			editBox:SetAttribute("channelTarget", channel);
			header:SetText(format(TEXT(getglobal("CHAT_CHANNEL_SEND")), channel, channelName));
		end
	else
		header:SetText(TEXT(getglobal("CHAT_"..type.."_SEND")));
	end

	header:SetTextColor(info.r, info.g, info.b);

	editBox:SetTextInsets(15 + header:GetWidth(), 13, 0, 0);
	editBox:SetTextColor(info.r, info.g, info.b);
end

function Cosmos_ChatEdit_AddHistory(editBox)
	local text = "";
	local type = editBox:GetAttribute("chatType");
	local header = getglobal("SLASH_"..type.."1");
	
	-- ESM-ChatRegistry start
	
	if ( header == nil or header == ""  ) then 
		header = getglobal ("SLASH_"..type);
		if ( header == nil or header == ""  ) then
			-- do nothing
		else
			header = header[1];
		end
	end
	
	-- ESM-ChatRegistry end
	
	if ( header ) then
		text = header;
	end

	if ( type == "WHISPER" ) then
		text = text.." "..editBox:GetAttribute("tellTarget");
	elseif ( type == "CHANNEL" ) then
		text = "/"..editBox:GetAttribute("channelTarget");
	end

	local editBoxText = editBox:GetText();
	if ( strlen(editBoxText) > 0 ) then
		text = text.." "..editBox:GetText();
	end

	if ( strlen(text) > 0 ) then
		editBox:AddHistoryLine(text);
	end
end

function Cosmos_ChatEdit_HandleChatType(editBox, msg, command, send)

	local channel = strmatch(command, "/([0-9]+)");

	if( channel and channel >= "0" and channel <= "9" ) then
		-- BEGIN Cosmos Channel mod
		local channelNum, channelName = Cosmos_GetChannelName(channel);
		-- END Cosmos Channel mod
		if ( channelNum > 0 ) then
			editBox:SetAttribute("channelTarget", channelNum);
			editBox:SetAttribute("chatType", "CHANNEL");
			editBox:SetText(msg);
			ChatEdit_UpdateHeader(editBox);
			return true;
		end
	else
		for index, value in pairs(ChatTypeInfo) do
			local i = 1;
			local cmdString = TEXT(getglobal("SLASH_"..index..i));

			-- ESM-ChatRegistry start
					
			if ( cmdString == nil or cmdString == ""  ) then 
				cmdString = CosmosMaster_GetCommandAliases(index);
				if ( cmdString == nil or cmdString == ""  ) then
					-- do nothing
				else
					cmdString = cmdString[i];
				end
			end
			-- ESM-ChatRegistry end

			while ( cmdString ) do
				cmdString = strupper(cmdString);
				if ( cmdString == command ) then
					if ( index == "WHISPER" ) then
						ChatEdit_ExtractTellTarget(editBox, msg);
					elseif ( index == "REPLY" ) then
						local lastTell = ChatEdit_GetLastTellTarget(editBox);
						if ( strlen(lastTell) > 0 ) then
							editBox:SetAttribute("chatType", "WHISPER");
							editBox:SetAttribute("tellTarget", lastTell);
							editBox:SetText(msg);
							ChatEdit_UpdateHeader(editBox);
						else
							if ( send == 1 ) then
								ChatEdit_OnEscapePressed(editBox);
							end
						end
					elseif (index == "CHANNEL") then
						ChatEdit_ExtractChannel(editBox, msg);
					else
						editBox:SetAttribute("chatType", index);
						editBox:SetText(msg);
						ChatEdit_UpdateHeader(editBox);
					end
					return true;
				end
				i = i + 1;
				cmdString = TEXT(getglobal("SLASH_"..index..i));
				
				-- ESM-ChatRegistry start

				if ( cmdString == nil or cmdString == ""  ) then 
					cmdString = CosmosMaster_GetCommandAliases(index);
					if ( cmdString == nil or cmdString == ""  ) then
						-- do nothing
					else
						cmdString = cmdString[i];
					end
				end
				-- ESM-ChatRegistry end
				
			end
		end
	end
	return false;
end

function Cosmos_ChatEdit_ParseCosmosCommand(editBox, send)

	local text = editBox:GetText();
	if ( strlen(text) <= 0 ) then
		return;
	end

	if ( strsub(text, 1, 1) ~= "/" ) then
		return;
	end

	-- If the string is in the format "/cmd blah", command will be "cmd"
	local command = strmatch(text, "^(/[^%s]+)") or "";
	local msg = "";

	if ( command ~= text ) then
		msg = strsub(text, strlen(command) + 2);
	end

	command = strupper(command);

	if ( securecall("ChatEdit_HandleChatType", editBox, msg, command, send) ) then
		return;
	end

	if ( send == 0 ) then
		return;
	end

	for index, value in pairs(SlashCmdList) do
		local i = 1;
		local cmdString = TEXT(getglobal("SLASH_"..index..i));

		-- ESM-ChatRegistry start
		
		if ( cmdString == nil or cmdString == ""  ) then 
			cmdString = CosmosMaster_GetCommandAliases(index);
			if ( cmdString == nil or cmdString == ""  ) then
				-- do nothing
			else
				cmdString = cmdString[i];
				--send = 1;
			end
		end
		-- ESM-ChatRegistry end

		while ( cmdString ) do
			cmdString = strupper(cmdString);
			if ( cmdString == command ) then
				value(strtrim(msg));
				editBox:AddHistoryLine(text);
				ChatEdit_OnEscapePressed(editBox);
				return;
			end
			i = i + 1;
			cmdString = TEXT(getglobal("SLASH_"..index..i));

			-- ESM-ChatRegistry start
			
			if ( cmdString == nil or cmdString == ""  ) then 
				cmdString = CosmosMaster_GetCommandAliases(index);
				if ( cmdString == nil or cmdString == ""  ) then
					-- do nothing
				else
					cmdString = cmdString[i];
				end
			end
			-- ESM-ChatRegistry end
		end
	end
end

function Cosmos_ChatEdit_ExtractChannel(editBox, msg)
	local target = strmatch(msg, "%s*([^%s]+)");
	if ( not target ) then
		return;
	end
	
	-- BEGIN Cosmos Channel mod
	local channelNum, channelName = Cosmos_GetChannelName(target);
	DEFAULT_CHAT_FRAME:AddMessage(channelNum);
	-- END Cosmos Channel mod
	if ( channelNum <= 0 ) then
		return;
	end

	msg = strsub(msg, strlen(target) + 2);

	editBox.channelTarget = channelNum;
	editBox.chatType = "CHANNEL";
	editBox:SetText(msg);
	ChatEdit_UpdateHeader(editBox);
end

function Cosmos_GetChannelName(channel)
	local channelNum = 0
	local channelName = "0";
	if ( channel ) then
		channelNum, channelName = GetChannelName(channel);
	end
	return channelNum, channelName;
end

-- Synchronizes all global variables
function CosmosMaster_SyncVars()
	for index, value in pairs(CosmosMaster_Configurations) do
		if ( value[CSM_TYPE] == "SEPARATOR" or value[CSM_TYPE] == "BUTTON" or value[CSM_TYPE] == "SECTION" ) then
		else 
			setglobal(value[CSM_VARIABLE], value[CSM_SLIDERVALUE] );
			setglobal(value[CSM_VARIABLE]..'_X',value[CSM_CHECKONOFF]);
			local callback = value[CSM_HANDLER];
			local slidervalue =  value[CSM_SLIDERVALUE];
			callback(value[CSM_CHECKONOFF],slidervalue);
		end
	end
end

-- Load all variables from CVars
function CosmosMaster_LoadVariables()

	for index, value in pairs(CosmosMaster_Configurations) do
		-- Ignore separators
		if ( value[CSM_TYPE] == "SEPARATOR" or value[CSM_TYPE] == "BUTTON" or value[CSM_TYPE] == "SECTION" ) then
		else 
			if ( value[CSM_TYPE]=="SLIDER" or value[CSM_TYPE]=="BOTH" ) then
				val = Cosmos_GetCVar(value[CSM_VARIABLE]);
				if ( val ~= nil ) then 
					value[CSM_SLIDERVALUE] = val + 0;
				else
                                       Cosmos_SetCVar(value[CSM_VARIABLE], value[CSM_SLIDERVALUE]);
				end
			end
			if ( value[CSM_TYPE]=="CHECKBOX" or value[CSM_TYPE]=="BOTH" ) then
				val = Cosmos_GetCVar(value[CSM_VARIABLE].."_X");
				if ( val ~= nil ) then 
					value[CSM_CHECKONOFF] = val + 0;
				else
                               		Cosmos_SetCVar(value[CSM_VARIABLE], value[CSM_CHECKONOFF]);
				end
			end
		end
	end
end

-- Store all variables to CVars
function CosmosMaster_StoreVariables()
	for index, value in pairs(CosmosMaster_Configurations) do
		if (not ( value[CSM_TYPE]=="SEPARATOR") and not (value[CSM_TYPE] == "BUTTON") and not (value[CSM_TYPE] == "SECTION") ) then
			if ( value[CSM_TYPE]=="SLIDER" or value[CSM_TYPE]=="BOTH" ) then
				local slidervalue =  value[CSM_SLIDERVALUE] ;
				
				Cosmos_SetCVar(value[CSM_VARIABLE], slidervalue);
			end
			if ( value[CSM_TYPE]=="CHECKBOX" or value[CSM_TYPE]=="BOTH" ) then				
				Cosmos_SetCVar(value[CSM_VARIABLE].."_X", value[CSM_CHECKONOFF]);
			end
		end
	end

end

-- Notify all stored methods
function CosmosMaster_NotifyAll()
	for index, value in pairs(CosmosMaster_Configurations) do
		if (not ( value[CSM_TYPE] == "SEPARATOR") and not (value[CSM_TYPE] == "BUTTON") and not (value[CSM_TYPE] == "SECTION") ) then
			f = value[CSM_HANDLER];
			local slidervalue =  value[CSM_SLIDERVALUE];
			
			f(value[CSM_CHECKONOFF],slidervalue);
		end
	end
end

-- Watch CVars
function CosmosMaster_HandleCVarUpdate(cvar, val, checked) 
end

-- Helper function to get Offset
function CosmosMaster_GetOffsetValue(offset)
	local curList = {};
	local curIndex = 1;
	for dex, curVal in pairs(CosmosMaster_Configurations) do
		if (curVal and (curVal[CSM_TYPE] ~= "SECTION") and (curVal[CSM_SECTION] == CosmosMaster_Section)) then
			table.insert(curList, curVal);
		end
	end	
	return curList[offset];
end

-- Helper function to get the number of configurations for the current header
function CosmosMaster_GetConfCount()
	confCount = 0;
	for dex, curVal in pairs(CosmosMaster_Configurations) do
		if (curVal and (curVal[CSM_TYPE] ~= "SECTION") and (curVal[CSM_SECTION] == CosmosMaster_Section)) then
			confCount = confCount + 1;
		end
	end
	return confCount;
end

-- Helper function to sort the list of the sections
function CosmosMaster_SectionComparator(section1, section2)
	if ( ( section1 ) and ( section2 ) ) then
		if ( ( section1[CSM_STRING] ) and ( section2[CSM_STRING] ) ) then
			if ( section1[CSM_STRING] == TEXT(COSMOS_CONFIG_SEP) ) then
				return true;
			elseif ( section2[CSM_STRING] == TEXT(COSMOS_CONFIG_SEP) ) then
				return false;
			else
				return (section1[CSM_STRING] < section2[CSM_STRING]);
			end
		elseif ( section1[CSM_STRING] ) then
			return false;
		elseif ( section2[CSM_STRING] ) then
			return true;
		end
	elseif ( section1 ) then
		return false;
	elseif ( section2 ) then
		return true;
	end
end

-- Helper function to get a list of the sections
function CosmosMaster_GenerateSectionList()
	local curList = {};
	for dex, curVal in pairs(CosmosMaster_Configurations) do
		if (curVal and (curVal[CSM_TYPE] == "SECTION")) then
			table.insert(curList, curVal);
		end
	end
	table.sort(curList, CosmosMaster_SectionComparator);
	return curList;
end

-- Helper function to get Offset
function CosmosMaster_GetOffsetSection(offset)
	local curList = CosmosMaster_GenerateSectionList();
	return curList[offset];
end

-- Helper function to get the number of sections
function CosmosMaster_GetSectCount()
	sectCount = 0;
	for dex, curVal in pairs(CosmosMaster_Configurations) do
		if (curVal[CSM_TYPE] == "SECTION") then
			sectCount = sectCount + 1;
		end
	end
	return sectCount;
end

-------------------------------------------------------------------------------
-- Example Code
-------------------------------------------------------------------------------

--[[
  An Example of how this could be used :

-- Example Callback Functions
function CombatCaller_HealthEnabledCallback (state, onoff)
	if( onoff == 1) then
		SendChatMessage ( state );
	end
end
function CombatCaller_ManaEnabledCallback (state, onoff)
	if ( onoff == 1) then
		SendChatMessage ( state );
	end
end

 All you have to do is call:

	Cosmos_RegisterConfiguration("COS_MYMOD_MYVALUE", "BOTH", "Abc ShortDescription", "Abc Long description\n line12",CombatCaller_HealthEnabledCallback, 0, .5, 0, 1, "BarName", 1, 1, "%");

 And I'll call CombatCaller_HealthEnabledCallback(value); every time the value changes!

 If you need to read the value regularly, just check the variable COSMOS_MYMOD_MYVALUE.

	Example arrays stored:
	(You dont have to create these, I make them in Register)

Cosmos_CombatCallerEnableHealthConfig = {
	[CSM_VARIABLE]="COS_COMBATCALLER_HEALTHENABLED", -- The CVar and global value that is updated
	[CSM_TYPE]="BOTH", -- CHECKBOX, SLIDER or BOTH
	[CSM_STRING]="Enable Auto Low Health Shout", -- The Text string
	[CSM_DESCRIPTION]="Long description here\n moretext";
	[CSM_CHECKONOFF]=1,	-- Starts off checked = true (1)
	[CSM_HANDLER]=CombatCaller_HealthEnabledCallback, -- The Function called when the value changes.
	[CSM_SLIDERMIN]=0,		-- Min value on slider
	[CSM_SLIDERMAX]=1,		-- Max value on slider
	[CSM_SLIDERVALUE]=.5,	-- Default value on slider
	[CSM_SLIDERSTEP]=.01,	-- Increments on slider
	[CSM_SLIDERSTRING]="Health Limit",	-- Slider Text (optional)
	[CSM_SLIDERVALUETEXTTOGGLE]=1,				-- Slider Text On/Off toggle
	[CSM_SLIDERTEXTAPPEND]="\%",
	[CSM_SLIDERVALUETEXTMULTIPLIER]=100
}
CosmosMaster_CombatCallerThankYouConfig = {
	[CSM_VARIABLE]="COS_COMBATCALLER_THANKYOUENABLED",
	[CSM_TYPE]="CHECKBOX",
	[CSM_STRING]="Enable Auto Thank You Shout",
	[CSM_DESCRIPTION]="",
	[CSM_CHECKONOFF]=1,	-- Default on?
	[CSM_HANDLER]=CombatCaller_HealthEnabledCallback,
	[CSM_SLIDERMIN]=0,	-- Everything below is useless for checkbox only.
	[CSM_SLIDERMAX]=1,
	[CSM_SLIDERVALUE]=1,
	[CSM_SLIDERSTEP]=.01, --Unimportant for checkboxes
	[CSM_SLIDERSTRING]="",
	[CSM_SLIDERVALUETEXTTOGGLE]=0 -- 0 for off!
}

	Please use this wisely.

	-Alex

	------------------------------------------------------------------

	How to register a chat command:
	
	--Create a function for your command:
	function myfunc () return 4; end
	
	--Create a list of /commands you want applied. 
	mycommands = { "/mycommand", "/mycommands" };
	
	--Create a help description 
	local mydesc = "This is my command!";
	
	--Pick a name or an action you are overwriting.
	local myfuncname = "CUSTOMMINE";
	
	--Choose how to handle a command if you overwrite it. (usually CSM_CHAINNONE)
	local mychain = CSM_CHAINNONE; 
	
	--Register it
	CosmosMaster_RegisterChatCommand( myfuncname, mycommands, myfunc, mydesc, mychain );
	
	-- Thats it!

]]--


-- Compatability stuffs
local Cosmos_Schedule = Chronos.schedule;
local Cosmos_ScheduleByName = Chronos.scheduleByName;
local Cosmos_AfterInit = Chronos.afterInit;
