--[[
	Tell Track

	By AnduinLothar, sarf & Lash

	This mod allows you to keep track of people you have had whisper conversations with.

	Thanks goes to Lash for the idea, support and the development of the XML file 
	(as well as providing acceleration to the rear end of sarf).
	Remember, only Lash prevented your CPU from suffering from loops galore!
	
	CosmosUI URL:
	http://www.cosmosui.org/
	
	Change Log:
	v1.1 (2/11/05) - Taken Over by AnduinLothar:
		-The newest whisper appears on the bottom and TellTrack auto-scrolls when updated.
		-Also added a "Delete All" option to the right-click menu.
		-Now monitors incomming whispers and adds their sender to the list.
		-If they were the last one to whisper their name is red. If you whispered last their name is green.
		-Added /re or /retell slash commands to whisper the last person you whispered.
		-Made retell bindable too. (I use shift-r)
		-Works w/o cosmos now.
	v1.2 (2/15/05) 
		-TellTrack is now resizable! Drag the bottom right corner. Remade button graphics to stretch correctly.
		-Added ability to extend up to 20 or down to 2 visible names.
		-List is now invertable "/telltrackinvert" or use cosmos options or right-click menu (cosmos only).
		-Bugfix for variable saving for non-cosmos users.
	v1.21 (2/15/05) 
		-Updated the toc version number to 4211.
		-Updated the right-click menu height
	v1.22 (2/15/05) 
		-Compressed images.
		-Made image alpha layers compatible with UI transparency (TransNUI)
	v1.23 (2/19/05)
		-Fixed name length trunkating.
		-Fixed right-click indexing and menu problems.
		-Added resize tooltip.
		-Optimized the arrow buttons code.
		-Fixed a rare bug where if you closed TellTrack while resizing or moving you couldn't click on anything.
		-Tooltip now scales correctly (set parent to UIParent)
	v1.24 (2/21/05) 
		-Fixed too many buttons showing onload.
		-Fixed unhooking issue with Sky.
		-Fixed reseting offset on Cosmos options cancil and login/reload.
	v1.25 (2/21/05)
		-Fixed Resize bug with too many buttons showing
	v1.3 (3/25/05)
		-Updated TOC to 1300
		-Fixed RMB Menu to use Earth instead of the obsolete Cosmos Menu
		-French Localization by Elzix and Sasmira
		-Fixed Wrong Faction Detected
	v1.4 (7/25/05)
		-Updated TOC to 1600
		-Added Khaos Configuration Support
		-Added Visibility Options Support
		-Added Sky Slash command Support
		-Fixed TellTrack not showing onload.
	v1.5 (8/2/05)
		-Added Shift-Click 'Who'
		-Added Alt-Click group invitation
		-Added Control-Click add to friends list
		-Added (Optional) Whisper Conversation Issolation
		(first click shows conversation, second click whispers)
		-Fixed inverted auto-scrolls on button update
		-Added list auto-scrolls for outgoing whispers.
		-If whisper frame is visible, 'enter' initiates a whisper
		-If there is not current conversation issolation defaults to off
		-When whisper frame is not visible a number on the TellTrack frame indicates the number of unread whipers from each person
		-Added option to not store list between login
		-Added VisibilityOptions for TellTrack Border/ScrollButtons
		-Auto WhisperChatFrame Creation
		-WhisperChatFrame saved for each realm/character
		-Added option to hide all other whispers
		-Added optional whisper frame time stamping
		-Compatibility with ChatTimeStamp seperator and date options.
		-Will utilize Clock value for time stamps if availible.
	v1.51 (9/18/05)
		-Fixed TellTrackTooltip nil bug.
	v1.52 (10/05/05)
		-Fixed a bug whispering players whose name started with a special character.
	v1.6 (10/12/05)
		-TOC updated to 1800
		-updated to use chatFrame:Clear()
		-Added 'Whisper First' option to open whisper on first click rather than view the chat log.
		-Fix for capitalization bug which caused whisper frame to not show received messages under certain circumstances
	v1.7 (11/13/05)
		-German localization updated.
		-Fixed the percentage issue
		-Renamed TellTrack_AddWhipser to TellTrack_AddWhisper.
		-Guilded and Sky whispers should now be hidden.
	v1.71 (11/16/05)
		-GuildAds should now be hidden.
		-Fix: <GM> whispers will not be hidden
	v1.8 (12/3/05)
		-SlashCommands issolated all to the namespace: "/telltrack"
		SubCommands: enabled, invert, dontsavelist, autowhisperframe, hidewhispers, timestamps, whisperfirst, clearall
		-Added Limited Chinese support
		-DropDownMenu no longer requires Earth
		-Dragging will now be more snappy with a new OnDragStart technique.
	v1.81 (1/2/06)
		-Removed debug that was causing error w/o Sea
	v1.9 (3/20/06)
		-Fixed RegisterForSave bug
		-Updated TOC to 11000
	v1.91 (4/18/06)
		-Fixed ancient resizing bug that showed the wrong number of buttons on new sessions.
	v1.92 (8/22/06)
		-Added Telepathy whisper ignores
		-Updated toc to 11200
    v1.93 (12/4/06)
    	-Updated toc to 20000
        -Works with TasteTheNaimbow
        -Fixed up ignoring addon whispers
    	-Added alt-shift-click deleteing
    	-Offline players' names now appear grey
    
    ToDo: reset location option, standalone border hiding, save conversations and remove msgs older than X days
    
   ]]--

-- Variables

TellTrack_ButtonCount = 1;
TellTrack_TooltipSetId = 0;
TellTrack_Array = {};
TellTrack_ID = 0;
TellTrack_LastTell = nil;
TellTrack_ArrayMaxSize = 20;
TellTrack_ArrayOffset = 1;

TellTrack_WhisperMessages = {};
TellTrack_WhisperChatFrame = {};
TellTrack_CurrentConversation = {name="",showAll=true};
TellTrack_OfflinePlayers = {};

TellTrack_Cosmos_Registered = nil;
TellTrack_Khaos_Registered = nil;
TellTrack_Satellite_Registered = nil;

local SavedSendChatMessage = nil;
local SavedFCF_SelectDockFrame = nil;
local SavedChatFrame_OpenChat = nil;
local SavedFCF_Close = nil;
local SavedFCF_OpenNewWindow = nil;

SELECTED_CHAT_FRAME = DEFAULT_CHAT_FRAME;
ERR_CHAT_PLAYER_NOT_FOUND_S_REGEX = gsub(ERR_CHAT_PLAYER_NOT_FOUND_S, "%%s", "(.*)")
ERR_FRIEND_OFFLINE_S_REGEX = gsub(ERR_FRIEND_OFFLINE_S, "%%s", "(.*)")
ERR_FRIEND_ONLINE_SS_REGEX = gsub(ERR_FRIEND_ONLINE_SS, "%%s", "(.*)")

-- executed on load, calls general set-up functions
function TellTrack_OnLoad()
	TellTrack_Register();
	TellTrack_UpdateTellTrackButtonsText();
	
	if (Sea and Sea.util and Sea.util.hook) then
		Sea.util.hook("SendChatMessage", "TellTrack_SendChatMessage", "before");
		Sea.util.hook("FCF_SelectDockFrame", "TellTrack_FCF_SelectDockFrame", "after");
		Sea.util.hook("ChatFrame_OpenChat", "TellTrack_ChatFrame_OpenChat", "replace");
		Sea.util.hook("FCF_Close", "TellTrack_FCF_Close", "before");
		Sea.util.hook("FCF_OpenNewWindow", "TellTrack_FCF_OpenNewWindow", "replace");
	else
		-- Hook the chat event handler 'before'
		if (SendChatMessage ~= SavedSendChatMessage) then
			SavedSendChatMessage = SendChatMessage;
			SendChatMessage = TellTrack_SendChatMessage;
		end
		
		-- Hook dock frame selection 'after'
		if (FCF_SelectDockFrame ~= TellTrack_FCF_SelectDockFrame) then
			SavedFCF_SelectDockFrame = FCF_SelectDockFrame;
			FCF_SelectDockFrame = TellTrack_FCF_SelectDockFrame;
		end
		
		-- Hook Open Chat to Open as whisper
		if (ChatFrame_OpenChat ~= TellTrack_ChatFrame_OpenChat) then
			SavedChatFrame_OpenChat = ChatFrame_OpenChat;
			ChatFrame_OpenChat = TellTrack_ChatFrame_OpenChat;
		end
		
		-- Hook Open Chat to Open as whisper
		if (FCF_Close ~= TellTrack_FCF_Close) then
			SavedFCF_Close = FCF_Close;
			FCF_Close = TellTrack_FCF_Close;
		end
		
		-- Hook Open Chat to Open as whisper
		if (FCF_OpenNewWindow ~= TellTrack_FCF_OpenNewWindow) then
			SavedFCF_OpenNewWindow = FCF_OpenNewWindow;
			FCF_OpenNewWindow = TellTrack_FCF_OpenNewWindow;
		end
	end
	
	TellTrack_SlashSubCommandList = {
		enabled = {func=function(msg) TellTrack_ChatCommandToggler(msg, TellTrack_Toggle_Enabled) end, help=TELLTRACK_CHAT_COMMAND_ENABLE_INFO};
		invert = {func=function(msg) TellTrack_ChatCommandToggler(msg, TellTrack_Toggle_Inverted) end, help= TELLTRACK_CHAT_COMMAND_INVERT_INFO};
		dontsavelist = {func=function(msg) TellTrack_ChatCommandToggler(msg, TellTrack_Toggle_DontSaveList) end, help= TELLTRACK_CHAT_COMMAND_DONTSAVELIST_INFO};
		autowhisperframe = {func=function(msg) TellTrack_ChatCommandToggler(msg, TellTrack_Toggle_AutoWhisperFrame) end, help= TELLTRACK_CHAT_COMMAND_AUTOWHISPERFRAME_INFO};
		hidewhispers = {func=function(msg) TellTrack_ChatCommandToggler(msg, TellTrack_Toggle_HideOtherWhispers) end, help= TELLTRACK_CHAT_COMMAND_HIDEOTHERWHISPERS_INFO};
		timestamps = {func=function(msg) TellTrack_ChatCommandToggler(msg, TellTrack_Toggle_TimeStamps) end, help=TELLTRACK_CHAT_COMMAND_TIMESTAMPS_INFO};
		whisperfirst = {func=function(msg) TellTrack_ChatCommandToggler(msg, TellTrack_Toggle_WhisperFirst) end, help=TELLTRACK_CHAT_COMMAND_WHISPERFIRST_INFO};
		clearall = {func=TellTrack_Menu_DeleteAll, help=TELLTRACK_CHAT_COMMAND_CLEARALL_INFO};
	};
	
end

function TellTrack_Register_Khaos()
	local optionSet = {
		id="TellTrack";
		text=TELLTRACK_CONFIG_HEADER;
		helptext=TELLTRACK_CONFIG_HEADER_INFO;
		difficulty=1;
		options={
			{
				id="Header";
				text=TELLTRACK_CONFIG_HEADER;
				helptext=TELLTRACK_CONFIG_HEADER_INFO;
				type=K_HEADER;
				difficulty=1;
			};
			{
				id="TellTrackEnable";
				type=K_TEXT;
				text=TELLTRACK_ENABLED;
				helptext=TELLTRACK_ENABLED_INFO;
				callback=function(state) if (state.checked) then TellTrack_Toggle_Enabled(1) else TellTrack_Toggle_Enabled(0) end end;
				feedback=function(state) if (state.checked) then return TELLTRACK_CHAT_ENABLED; else return TELLTRACK_CHAT_DISABLED; end end;
				check=true;
				--default={checked=TellTrack_Enabled};
				default={checked=true};
				disabled={checked=false};
			};
			{
				id="TellTrackInvert";
				type=K_TEXT;
				text=TELLTRACK_INVERTED;
				helptext=TELLTRACK_INVERTED_INFO;
				callback=function(state) if (state.checked) then TellTrack_Toggle_Inverted(1) else TellTrack_Toggle_Inverted(0) end end;
				feedback=function(state) if (state.checked) then return TELLTRACK_CHAT_INVERTED; else return TELLTRACK_CHAT_NORMALIZED; end end;
				check=true;
				--default={checked=TellTrack_InvertedList};
				default={checked=false};
				disabled={checked=false};
			};
			{
				id="TellTrackDontSaveList";
				type=K_TEXT;
				text=TELLTRACK_DONTSAVELIST;
				helptext=TELLTRACK_DONTSAVELIST_INFO;
				callback=function(state) if (state.checked) then TellTrack_Toggle_DontSaveList(1) else TellTrack_Toggle_DontSaveList(0) end end;
				feedback=function(state) if (state.checked) then return TELLTRACK_CHAT_DONTSAVELIST; else return TELLTRACK_CHAT_SAVELIST; end end;
				check=true;
				--default={checked=TellTrack_DontSaveList};
				default={checked=false};
				disabled={checked=false};
			};
			{
				id="TellTrackAutoWhisperFrame";
				type=K_TEXT;
				text=TELLTRACK_AUTOWHISPERFRAME;
				helptext=TELLTRACK_AUTOWHISPERFRAME_INFO;
				callback=function(state) if (state.checked) then TellTrack_Toggle_AutoWhisperFrame(1) else TellTrack_Toggle_AutoWhisperFrame(0) end end;
				feedback=function(state) if (state.checked) then return TELLTRACK_CHAT_AUTOWHISPERFRAME; else return TELLTRACK_CHAT_MANUALWHISPERFRAME; end end;
				check=true;
				--default={checked=TellTrack_AutoWhisperFrame};
				default={checked=true};
				disabled={checked=false};
			};
			{
				id="TellTrackHideOtherWhispers";
				type=K_TEXT;
				text=TELLTRACK_HIDEOTHERWHISPERS;
				helptext=TELLTRACK_HIDEOTHERWHISPERS_INFO;
				callback=function(state) if (state.checked) then TellTrack_Toggle_HideOtherWhispers(1) else TellTrack_Toggle_HideOtherWhispers(0) end end;
				feedback=function(state) if (state.checked) then return TELLTRACK_CHAT_HIDEOTHERWHISPERS; else return TELLTRACK_CHAT_SHOWOTHERWHISPERS; end end;
				check=true;
				--default={checked=TellTrack_HideOtherWhispers};
				default={checked=false};
				disabled={checked=false};
			};
			{
				id="TellTrackTimeStamps";
				type=K_TEXT;
				text=TELLTRACK_TIMESTAMPS;
				helptext=TELLTRACK_TIMESTAMPS_INFO;
				callback=function(state) if (state.checked) then TellTrack_Toggle_TimeStamps(1) else TellTrack_Toggle_TimeStamps(0) end end;
				feedback=function(state) if (state.checked) then return TELLTRACK_CHAT_TIMESTAMPS; else return TELLTRACK_CHAT_NOTIMESTAMPS; end end;
				check=true;
				--default={checked=TellTrack_TimeStamps};
				default={checked=false};
				disabled={checked=false};
			};
			{
				id="TellTrackWhisperFirst";
				type=K_TEXT;
				text=TELLTRACK_WHISPERFIRST;
				helptext=TELLTRACK_WHISPERFIRST_INFO;
				callback=function(state) if (state.checked) then TellTrack_Toggle_WhisperFirst(1) else TellTrack_Toggle_WhisperFirst(0) end end;
				feedback=function(state) if (state.checked) then return TELLTRACK_CHAT_WHISPERFIRST; else return TELLTRACK_CHAT_LOGFIRST; end end;
				check=true;
				--default={checked=TellTrack_TimeStamps};
				default={checked=false};
				disabled={checked=false};
			};
		};
	};
	Khaos.registerOptionSet(
		"chat",
		optionSet
	);
	TellTrack_Khaos_Registered = 1;
end


-- registers the mod with Cosmos
function TellTrack_Register_Cosmos()
	Cosmos_RegisterConfiguration(
		"COS_TELLTRACK",
		"SECTION",
		TELLTRACK_CONFIG_HEADER,
		TELLTRACK_CONFIG_HEADER_INFO
	);
	Cosmos_RegisterConfiguration(
		"COS_TELLTRACK_HEADER",
		"SEPARATOR",
		TELLTRACK_CONFIG_HEADER,
		TELLTRACK_CONFIG_HEADER_INFO
	);
	Cosmos_RegisterConfiguration(
		"COS_TELLTRACK_ENABLED",
		"CHECKBOX",
		TELLTRACK_ENABLED,
		TELLTRACK_ENABLED_INFO,
		TellTrack_Toggle_Enabled,
		TellTrack_Enabled
	);
	Cosmos_RegisterConfiguration(
		"COS_TELLTRACK_INVERTED",
		"CHECKBOX",
		TELLTRACK_INVERTED,
		TELLTRACK_INVERTED_INFO,
		TellTrack_Toggle_Inverted,
		TellTrack_InvertedList
	);
	Cosmos_RegisterConfiguration(
		"COS_TELLTRACK_DONTSAVELIST",
		"CHECKBOX",
		TELLTRACK_DONTSAVELIST,
		TELLTRACK_DONTSAVELIST_INFO,
		TellTrack_Toggle_DontSaveList,
		TellTrack_DontSaveList
	);
	Cosmos_RegisterConfiguration(
		"COS_TELLTRACK_AUTOWHISPERFRAME",
		"CHECKBOX",
		TELLTRACK_AUTOWHISPERFRAME,
		TELLTRACK_AUTOWHISPERFRAME_INFO,
		TellTrack_Toggle_AutoWhisperFrame,
		TellTrack_AutoWhisperFrame
	);
	Cosmos_RegisterConfiguration(
		"COS_TELLTRACK_TIMESTAMPS",
		"CHECKBOX",
		TELLTRACK_TIMESTAMPS,
		TELLTRACK_TIMESTAMPS_INFO,
		TellTrack_Toggle_TimeStamps,
		TellTrack_TimeStamps
	);
	Cosmos_RegisterConfiguration(
		"COS_TELLTRACK_WHISPERFIRST",
		"CHECKBOX",
		TELLTRACK_WHISPERFIRST,
		TELLTRACK_WHISPERFIRST_INFO,
		TellTrack_Toggle_WhisperFirst,
		TellTrack_WhisperFirst
	);
	
	TellTrack_Cosmos_Registered = 1;
end

function TellTrack_Register_Satellite()
	Satellite.registerSlashCommand(
		{
			id="TellTrack";
			commands = {"/telltrack","/ttrack"};
			onExecute = TellTrack_ChatCommandHandler;
			helpText = TELLTRACK_CHAT_COMMAND_INFO;
		}
	);
	Satellite.registerSlashCommand(
		{
			id="Retell";
			commands = {"/re", "/retell"};
			onExecute = TellTrack_WhisperToPreviousTarget;
			helpText = BINDING_NAME_TELLTRACK_RETELL;
		}
	);
	TellTrack_Satellite_Registered = 1;
end

-- registers the mod with the system, integrating it with slash commands and "master" AddOns
function TellTrack_Register()
	if (Khaos) then
		if (not TellTrack_Khaos_Registered) then
			TellTrack_Register_Khaos();
		end
	elseif (Cosmos_RegisterConfiguration) then
		if (not TellTrack_Cosmos_Registered) then
			TellTrack_Register_Cosmos();
		end
	end
		

	if (Satellite) then
		if (not TellTrack_Satellite_Registered) then
			TellTrack_Register_Satellite();
		end
	else
		SlashCmdList["TELLTRACK"] = TellTrack_ChatCommandHandler;
		SLASH_TELLTRACK1 = "/telltrack";
		SLASH_TELLTRACK2 = "/ttrack";
		SlashCmdList["TELLTRACKRETELL"] = TellTrack_WhisperToPreviousTarget;
		SLASH_TELLTRACKRETELL1 = "/re";
		SLASH_TELLTRACKRETELL2 = "/retell";
	end
        
	this:RegisterEvent("VARIABLES_LOADED");
	this:RegisterEvent("PLAYER_ENTERING_WORLD");
	this:RegisterEvent("CHAT_MSG_WHISPER");
	this:RegisterEvent("CHAT_MSG_WHISPER_INFORM");
	this:RegisterEvent("CHAT_MSG_SYSTEM");

	if ( Eclipse ) then
		--Set the ui section to be TellTrack if we have Khaos
		local uisec = "TellTrack";
		--If we don't have Khaos, then use the Cosmos section
		if (not Khaos) then
			uisec = "COS_TELLTRACK";
		end
		--Register with VisibilityOptions
		Eclipse.registerForVisibility( {
			name = "TellTrackFrame";	--The name of the config, in this case also the name of the frame
			nototal = true;	--This addon has an option to hide the frame already, so we don't need to register with total
			uisec = uisec;	--This puts the options, in the TellTrack section, it is not neccisary but helps to keep VisOpts section cleaner
			uiname = TELLTRACK_CONFIG_HEADER;	--This is the base name of this reg to display in the description and ui
			slashcom = { "telltrack", "tt" };	--These are the slash commands
		}	);
		Eclipse.registerForVisibility( {
			name = "TellTrackBorder";	--The name of the config
			frames = {"TellTrackFrameArrowUpButton", "TellTrackFrameArrowDownButton", "TellTrackFrameBackground"};
			uisec = uisec;	--This puts the options, in the TellTrack section, it is not neccisary but helps to keep VisOpts section cleaner
			uiname = TELLTRACK_BORDER_CONFIG_HEADER;	--This is the base name of this reg to display in the description and ui
			slashcom = { "telltrackborder", "ttborder" };	--These are the slash commands
			state = { show = true };	--Because the frame is not visible when registered, we need to let VisOpts know it should be visible
		}	);
	else
		if ( TransNUI_RegisterUI ) then
			TransNUI_RegisterUI("TellTrackFrame", { "telltrack" }, TELLTRACK_CONFIG_TRANSNUI, TELLTRACK_CONFIG_TRANSNUI_INFO, 0);
		end
		if ( PopNUI_RegisterUI ) then
			PopNUI_RegisterUI("TellTrackFrame", { "telltrack" }, {"TellTrack_Enabled", 1, true}, TELLTRACK_CONFIG_POPNUI, TELLTRACK_CONFIG_POPNUI_INFO);
		end
	end
end

function TellTrack_ChatCommandToggler(msg, func)
	if msg == nil then
		msg = "";
	end
	msg = string.lower(msg);
	
	-- Toggle appropriately
	if ( (string.find(msg, 'on')) or ((string.find(msg, '1')) and (not string.find(msg, '-1')) ) ) then
		func(1);
	else
		if ( (string.find(msg, 'off')) or (string.find(msg, '0')) ) then
			func(0);
		else
			func(-1);
		end
	end
end

--Master Chat Command Handler
function TellTrack_ChatCommandHandler(msg)
	local _, _, subcom, toggle = string.find(msg, "[ ]*(%w+)[ ]*(.*)");
	if (subcom) then
		subcom = strlower(subcom);
		--Sea.io.print("TellTrack - ", subcom, " ", toggle);
	else
		TellTrack_ChatCommand_Help();
		return;
	end
	if (TellTrack_SlashSubCommandList[subcom]) then
		TellTrack_SlashSubCommandList[subcom].func(toggle);
	else
		TellTrack_ChatCommand_Help();
	end
end

function TellTrack_ChatCommand_Help()
	TellTrack_Print (TELLTRACK_CHAT_QUESTION_MARK_INFO,1.0,1.0,0);
	TellTrack_Print (TELLTRACK_CHAT_USE_INFO,1.0,1.0,0);
	for subcom, info in pairs(TellTrack_SlashSubCommandList) do
		TellTrack_Print(subcom.." - ".. info.help,1.0,1.0,0);
	end
end

-- Does things with the hooked function
function TellTrack_SendChatMessage(text, type, language, target)
	-- saves target for 'Retell' regardless of TellTrack_Enabled
	target = TellTrack_Capitalize(target);
	if ( type == "WHISPER" ) then
		TellTrack_LastTell = target;
	end
	if ( TellTrack_Enabled == 1 ) then
		if ( type == "WHISPER" ) then 
			-- prevent data message transfers from being used
			if ( strsub(text, 1, 1) ~= "<" ) then
				TellTrack_HandleMessageSentOrRecieved(target, false);
			end
		end
	end
	if (SavedSendChatMessage) then
		SavedSendChatMessage(text, type, language, target);
	end
end

function TellTrack_Capitalize(text)
	if (not text) then
		return;
	end
	if( string.find(text, "^[a-zA-Z].*") ) then
		text = strupper(strsub(text,1,1))..strlower(strsub(text,2));
	else
		text = strupper(strsub(text,1,2))..strlower(strsub(text,3));
	end
	return text;
end

function TellTrack_startsWith(s, prefix)
	local isAtBeginning = false;
	if (type(s) == "string") and (type(prefix) == "string") then
		if (s == prefix) then
			isAtBeginning = true;
		elseif ( string.len(s) > string.len(prefix) ) then
			if( string.sub(s, 1, string.len(prefix)) == prefix ) then
				isAtBeginning = true;
			end
		end
	end
	return isAtBeginning;
end


-- Handles events
function TellTrack_OnEvent(event)
	if ( event == "VARIABLES_LOADED" ) then
		local value = TellTrack_Enabled;
		if ( TellTrack_Cosmos_Registered ) then
			value = getglobal("COS_TELLTRACK_ENABLED_X");
			TellTrack_InvertedList = getglobal("COS_TELLTRACK_INVERTED_X");
		end
		if (value == nil) then
			-- defaults to off
			value = 0;
		end
		if (TellTrack_InvertedList == nil) then
			-- defaults to off
			TellTrack_InvertedList = 0;
		end
		if (not TellTrack_WhisperChatFrame[GetRealmName()]) then
			TellTrack_WhisperChatFrame[GetRealmName()] = {};
		end
		if (not TellTrack_Khaos_Registered) then
			TellTrack_Toggle_Enabled(value);
		end
		if ( TellTrack_DontSaveList == 1 ) then
			TellTrack_SavedList = {};
		end
		TellTrack_LoadNames();
		--Initialize Menu
		UIDropDownMenu_Initialize(TellTrackDropDown, TellTrackTextButton_InitializeMenuArray, "MENU");
	elseif ( event == "PLAYER_ENTERING_WORLD" ) then
		TellTrack_ModifyButtonCount(TellTrackFrame:GetHeight());
	elseif ( event == "CHAT_MSG_WHISPER" ) or ( event == "CHAT_MSG_WHISPER_INFORM" ) then
		--For some reason TellTrack_HookAllAddMessages doesn't work on VARIABLES_LOADED...
		TellTrack_HookAllAddMessages();
		
		--Special Hidden Whisper Ignores
        if ( IsAddOnLoaded("Guilded") ) and (string.len(arg1) > 2) and (string.sub(arg1,1,2) == "<G") and ( (string.len(arg1) ~= 4) or (string.sub(arg1,1,4) ~= "<GM>") ) then
			--Hide Guilded messages
			return;
		elseif ( IsAddOnLoaded("GuildAds") ) and (string.len(arg1) > 3) and (string.sub(arg1,1,3) == "<GA") then
			--Hide GuildAds messages
			return;
		elseif (string.len(arg1) > 3) and (string.sub(arg1,1,3) == "<T>") then
			--Hide Telepathy messages
			return;
        elseif (string.len(arg1) > 1) and (string.sub(arg1,1,1) == "/") then
            --Hide AceComm messages
            return;
        elseif (string.len(arg1) > 7) and (string.sub(arg1,1,7) == "SKP1019") then
            --Hide SKP messages
            return;
		end 
		
		local type = string.sub(event,10);
		if ( event == "CHAT_MSG_WHISPER" ) then
			TellTrack_HandleMessageSentOrRecieved(arg2, true, arg1);
		end
		local time;
		if (Clock_GetTimeText) then
			time = Clock_GetTimeText();
		else
			local hour, minute = GetGameTime();
			time = {h=hour,m=minute};
		end
		local message = gsub(arg1, "%%", "%%%%");
		table.insert(TellTrack_WhisperMessages, {type=type,name=arg2,msg=message,time=time, date=date()});
	elseif ( event == "CHAT_MSG_SYSTEM" ) then
		local _, _, name = strfind(arg1, ERR_CHAT_PLAYER_NOT_FOUND_S_REGEX);
		if (name) then
			TellTrack_OfflinePlayers[name] = true;
			TellTrack_UpdateTellTrackButtonsText();
			return;
		end
		_, _, name = strfind(arg1, ERR_FRIEND_OFFLINE_S_REGEX);
		if (name) then
			TellTrack_OfflinePlayers[name] = true;
			TellTrack_UpdateTellTrackButtonsText();
			return;
		end
		_, _, name = strfind(arg1, ERR_FRIEND_ONLINE_SS_REGEX);
		if (name) then
			TellTrack_OfflinePlayers[name] = nil;
			TellTrack_UpdateTellTrackButtonsText();
			return;
		end
	end
end

function TellTrack_AddWhisper(chatFrame, text, name, type, time)
	local info = ChatTypeInfo[type];
    if TasteTheNaimbow_Loaded then
    local namecolor = TasteTheNaimbowExternalColor(name)
	chatFrame:AddMessage(format(TEXT(getglobal("CHAT_"..type.."_GET"))..text, "|cff"..namecolor.."|Hplayer:"..name.."|h".."["..name.."]".."|h|r"), info.r, info.g, info.b, info.id, time);
    else
    chatFrame:AddMessage(format(TEXT(getglobal("CHAT_"..type.."_GET"))..text, "|Hplayer:"..name.."|h".."["..name.."]".."|h"), info.r, info.g, info.b, info.id, time);
    end
end

function TellTrack_GetTimeStamp(time)
	local separator = ">";
	if (ChatTimeStamps_TimeStamp) and (not ChatTimeStamps_SEPARATOR) then
		separator = "";
	end
	if (not time) then
		if (ChatTimeStamps_LOCALTIME) then
			time = date();
		elseif (Clock_GetTimeText) then
			time = Clock_GetTimeText();
		else
			local hour, minute = GetGameTime();
			time = {h=hour,m=minute};
		end
	end
	if (type(time) == "string") then
		return time..separator.." ";
	end
	
	if (time.h < 10) then 
		time.h = "0"..time.h; 
	end

	if (time.m < 10) then 
		time.m = "0"..time.m; 
	end
	return time.h..":"..time.m..separator.." ";
end

function TellTrack_ClearChatFrame(chatFrame)
	chatFrame:Clear();
end

function TellTrack_ShowConversationWith(name, showAll)
	if (not TellTrack_WhisperChatFrame[GetRealmName()][UnitName("player")]) then
		if (TellTrack_AutoWhisperFrame) and (FCF_GetNumActiveChatFrames() < NUM_CHAT_WINDOWS ) then
			TellTrack_CreateWhisperChatFrame();
		else
			return;
		end
	end
	local WhisperFrame = getglobal(TellTrack_WhisperChatFrame[GetRealmName()][UnitName("player")]);
	TellTrack_CurrentConversation = {name=name,showAll=true};
	TellTrack_ClearChatFrame(WhisperFrame);
	TellTrack_CurrentConversation.showAll = showAll;
	local found = 0;
	--Display only the valid whispers.
	for i, data in ipairs(TellTrack_WhisperMessages) do
		if (showAll) or (data.name == name) then
			if (ChatTimeStamps_LOCALTIME) then
				TellTrack_AddWhisper(WhisperFrame, data.msg, data.name, data.type, data.date);
			else
				TellTrack_AddWhisper(WhisperFrame, data.msg, data.name, data.type, data.time);
			end
			found = found + 1;
		end
	end
	
	if (found == 0) then
		TellTrack_CurrentConversation.showAll = true;
		local timeStamp = TellTrack_TimeStamps;
		TellTrack_TimeStamps = 0;
		WhisperFrame:AddMessage(format(TELLTRACK_NO_CURRENT_CONVERSATION, name));
		TellTrack_TimeStamps = timeStamp;
		FCF_SetWindowName(WhisperFrame, TELLTRACK_WHISPER_FRAME_TITLE);
	else
		FCF_SetWindowName(WhisperFrame, name);
	end
	
	-- Show the frame and tab
    if (WhisperFrame.isDocked) then
	FCF_SelectDockFrame(WhisperFrame);
    end
end

function TellTrack_ShowAllWhispers()
	TellTrack_ShowConversationWith(TellTrack_CurrentConversation.name, true);
end

function TellTrack_WhisperChatFrame_AddMessage(this, text, r, g, b, id, time)
	if (this:GetName() ~= TellTrack_WhisperChatFrame[GetRealmName()][UnitName("player")]) then
		-- incorrect whisper frame
		SavedWhisperChatFrame_AddMessages[this:GetName()](this, text, r, g, b, id);
		return;
	end
	local stamps = ChatTimeStamps_ENABLED;
	ChatTimeStamps_ENABLED = 0;
	if (strlen(text) < 5) then
		SavedWhisperChatFrame_AddMessages[this:GetName()](this, text, r, g, b, id);
		ChatTimeStamps_ENABLED = stamps;
		return;
	end
	local timedText = text;
	if (TellTrack_TimeStamps == 1) then
		timedText = TellTrack_GetTimeStamp(time)..text;
	end
	if (not TellTrack_CurrentConversation) or (TellTrack_CurrentConversation.showAll) then
		SavedWhisperChatFrame_AddMessages[this:GetName()](this, timedText, r, g, b, id);
		ChatTimeStamps_ENABLED = stamps;
		return;
	end
	local name = TellTrack_CurrentConversation.name;
    local namecolor = "ffffff"
    if (TasteTheNaimbow_Loaded) then
        namecolor = TasteTheNaimbowExternalColor(name)
    end
	if  (TellTrack_startsWith(text, format(TEXT(getglobal("CHAT_WHISPER_GET")), "|Hplayer:"..name.."|h".."["..name.."]".."|h"))) or
		(TellTrack_startsWith(text, format(TEXT(getglobal("CHAT_WHISPER_INFORM_GET")), "|Hplayer:"..name.."|h".."["..name.."]".."|h"))) or
        (TellTrack_startsWith(text, format(TEXT(getglobal("CHAT_WHISPER_GET")), "|cff"..namecolor.."|Hplayer:"..name.."|h".."["..name.."]".."|h|r"))) or
        (TellTrack_startsWith(text, format(TEXT(getglobal("CHAT_WHISPER_INFORM_GET")), "|cff"..namecolor.."|Hplayer:"..name.."|h".."["..name.."]".."|h|r"))) then
		SavedWhisperChatFrame_AddMessages[this:GetName()](this, timedText, r, g, b, id);
		ChatTimeStamps_ENABLED = stamps;
		return;
	end
end

function TellTrack_RedrawWhisperFrame()
	if ( not GetRealmName() or not UnitName("player") ) then
		return;
	end;
	if (not TellTrack_WhisperChatFrame[GetRealmName()] ) then
		TellTrack_WhisperChatFrame[GetRealmName()] = {};
	end
	if (not TellTrack_WhisperChatFrame[GetRealmName()][UnitName("player")]) then
		return;
	end
	local WhisperFrame = getglobal(TellTrack_WhisperChatFrame[GetRealmName()][UnitName("player")]);
	local scrollOffset = WhisperFrame:GetCurrentScroll();
	TellTrack_ClearChatFrame(WhisperFrame);
	for i, data in pairs(TellTrack_WhisperMessages) do
		if (TellTrack_CurrentConversation.showAll) or (data.name == TellTrack_CurrentConversation.name) then
			TellTrack_AddWhisper(WhisperFrame, data.msg, data.name, data.type, data.time, "|cff"..TasteTheNaimbowExternalColor(data.name)..data.name.."|r");
		end
	end
	WhisperFrame:SetScrollFromBottom(scrollOffset)
end

function TellTrack_HookAllAddMessages()
	if (SavedWhisperChatFrame_AddMessages) then
		--Sea.io.print("Failed to set up TellTrack_WhisperChatFrame_AddMessage hooks.");
		return;
	end
	SavedWhisperChatFrame_AddMessages = {};
	local frameName = ""
	for i=1, NUM_CHAT_WINDOWS do
		frameName = "ChatFrame"..i;
		if (getglobal(frameName).AddMessage ~= TellTrack_WhisperChatFrame_AddMessage) then
			SavedWhisperChatFrame_AddMessages[frameName] = getglobal(frameName).AddMessage;
			getglobal(frameName).AddMessage = TellTrack_WhisperChatFrame_AddMessage;
		end
	end
	--Sea.io.print("Set up TellTrack_WhisperChatFrame_AddMessage hooks.");
end

function TellTrack_ChatFrame_OpenChat(text, chatFrame)
	if (text == "") and (SELECTED_CHAT_FRAME:GetName() == TellTrack_WhisperChatFrame[GetRealmName()][UnitName("player")]) and (TellTrack_CurrentConversation) and (TellTrack_CurrentConversation.name) and (TellTrack_CurrentConversation.name ~= "") then
		TellTrack_InitiateWhisperToTarget(TellTrack_CurrentConversation.name);
	else
		if (SavedChatFrame_OpenChat) then
			SavedChatFrame_OpenChat(text, chatFrame);
		else
			return true;
		end
	end
end

function TellTrack_FCF_OpenNewWindow(name)
	if (TellTrack_HideOtherWhispers == 1) then
		local temp, shown;
		local newChatFrame = nil;
		for i=1, NUM_CHAT_WINDOWS do
			temp, temp, temp, temp, temp, temp, shown, temp = GetChatWindowInfo(i);
			chatFrame = getglobal("ChatFrame"..i);
			if ( (not shown and not chatFrame.isDocked) or (count == NUM_CHAT_WINDOWS) ) then
				newChatFrame = chatFrame;
				break;
			end
		end
		if (SavedFCF_OpenNewWindow) then
			SavedFCF_OpenNewWindow(name);
		else
			Sea.util.Hooks.FCF_OpenNewWindow.orig(name);
		end
		ChatFrame_RemoveMessageGroup(newChatFrame, "WHISPER");
	else
		if (SavedFCF_OpenNewWindow) then
			SavedFCF_OpenNewWindow(name);
		else
			return true;
		end
	end
end

function TellTrack_FCF_Close(chatFrame)
	if ( not chatFrame ) then
		chatFrame = FCF_GetCurrentChatFrame();
	end
	if (chatFrame:GetName() == TellTrack_WhisperChatFrame[GetRealmName()][UnitName("player")]) then
		TellTrack_WhisperChatFrame[GetRealmName()][UnitName("player")] = nil;
	end
	if (SavedFCF_Close) then
		SavedFCF_Close(chatFrame);
	else
		return true, chatFrame;
	end
end

function TellTrack_FCF_SelectDockFrame(frame)
	if (SavedFCF_SelectDockFrame ) then
		SavedFCF_SelectDockFrame(frame);
	end
	if (frame:GetName() == TellTrack_WhisperChatFrame[GetRealmName()][UnitName("player")] ) then
		if (TellTrack_CurrentConversation.showAll) then
			for i = 1, TellTrack_ArrayMaxSize do
				if (TellTrack_Array[i]) then
					TellTrack_Array[i].unread = 0;
				end
			end
		elseif (TellTrack_CurrentConversation.name ~= "") then
			for i = 1, TellTrack_ArrayMaxSize do
				if (TellTrack_Array[i]) and (TellTrack_Array[i].name == TellTrack_CurrentConversation.name) then
					TellTrack_Array[i].unread = 0;
				end
			end
		end
		TellTrack_UpdateTellTrackButtonsText();
	end
	SELECTED_CHAT_FRAME = frame;
end

function TellTrack_CreateWhisperChatFrame(name)
	if (TellTrack_WhisperChatFrame[GetRealmName()][UnitName("player")]) then
		--check for not valid?
		return;
	end
	local temp, shown;
	local count = 1;
	local chatFrame;
	
	for i=1, NUM_CHAT_WINDOWS do
		temp, temp, temp, temp, temp, temp, shown, temp = GetChatWindowInfo(i);
		chatFrame = getglobal("ChatFrame"..i);
		chatTab = getglobal("ChatFrame"..i.."Tab");
		if ( (not shown and not chatFrame.isDocked) or (count == NUM_CHAT_WINDOWS) ) then
			if (type(name) ~= "string" or name == "") then
				name = "TellTrack";
			end
			
			-- initialize the frame
			FCF_SetWindowName(chatFrame, name);
			FCF_SetWindowColor(chatFrame, DEFAULT_CHATFRAME_COLOR.r, DEFAULT_CHATFRAME_COLOR.g, DEFAULT_CHATFRAME_COLOR.b);
			FCF_SetWindowAlpha(chatFrame, DEFAULT_CHATFRAME_ALPHA);
			SetChatWindowLocked(i, nil);

			-- Listen to the standard messages
			ChatFrame_RemoveAllMessageGroups(chatFrame);
			ChatFrame_AddMessageGroup(chatFrame, "WHISPER");

			-- Show the frame and tab
			chatFrame:Show();
			chatTab:Show();
			SetChatWindowShown(i, 1);
			
			-- Dock the frame by default
			FCF_DockFrame(chatFrame);
			
			TellTrack_WhisperChatFrame[GetRealmName()][UnitName("player")] = chatFrame:GetName();
			break;
		end
		count = count + 1;
	end
end

function TellTrack_HideWhispersInOtherFrames()
	if (not TellTrack_WhisperChatFrame[GetRealmName()][UnitName("player")]) then
		--check for not valid?
		return;
	end
	local chatFrameName;
	for i=1, NUM_CHAT_WINDOWS do
		chatFrameName = "ChatFrame"..i;
		if (chatFrameName ~= TellTrack_WhisperChatFrame[GetRealmName()][UnitName("player")]) then
			ChatFrame_RemoveMessageGroup(getglobal(chatFrameName), "WHISPER");
		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 TellTrack_Generic_Toggle(toggle, variableName, CVarName, enableMessage, disableMessage, CosmosVarName, KhaosVarName)
	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 ) and ( not TellTrack_Cosmos_Registered ) and ( not TellTrack_Khaos_Registered ) ) then
		if ( newvalue == 1 ) then
			TellTrack_Print(TEXT(getglobal(enableMessage)));
		else
			TellTrack_Print(TEXT(getglobal(disableMessage)));
		end
	end
	-- TellTrack_Register_Cosmos();
	if (TellTrack_Khaos_Registered) then
		if (Khaos.getSetKey("TellTrack", KhaosVarName)) then
			Khaos.setSetKeyParameter("TellTrack",KhaosVarName, "checked", (newvalue == 1));
		end
	elseif (TellTrack_Cosmos_Registered) then
		if ( CosmosVarName ) then
			Cosmos_UpdateValue(CosmosVarName, CSM_CHECKONOFF, newvalue);
			Cosmos_SetCVar(CosmosVarName, newvalue);
		end
	else
		-- No more RegisterForSave
	end
	return newvalue;
end

-- Toggles the enabled/disabled state of the TellTrack
--  if toggle is 1, it's enabled
--  if toggle is 0, it's disabled
--   otherwise, it's toggled
function TellTrack_Toggle_Enabled(toggle)
	local oldvalue = TellTrack_Enabled;
	local newvalue = TellTrack_Generic_Toggle(toggle, "TellTrack_Enabled", "COS_TELLTRACK_ENABLED", "TELLTRACK_CHAT_ENABLED", "TELLTRACK_CHAT_DISABLED", "COS_TELLTRACK_ENABLED", "TellTrackEnable");
	if ( TellTrack_Enabled == 0 ) then
		TellTrackFrame:Hide();
		if (TellTrackTooltip) then
			TellTrackTooltip:Hide();
		end
	else
		TellTrackFrame:Show();
	end
end

-- Toggles the inverted/normalized state of the TellTrack list
--  if toggle is 1, it's enabled
--  if toggle is 0, it's disabled
--   otherwise, it's toggled
function TellTrack_Toggle_Inverted(toggle)
	local oldvalue = TellTrack_InvertedList;
	local newvalue = TellTrack_Generic_Toggle(toggle, "TellTrack_InvertedList", "COS_TELLTRACK_INVERTED", "TELLTRACK_CHAT_INVERTED", "TELLTRACK_CHAT_NORMALIZED", "COS_TELLTRACK_INVERTED", "TellTrackInvert");
	--only change (reset offset) telltrack if TellTrack_InvertedList was changed
	if ( oldvalue ~= newvalue ) then
		if ( newvalue == 1 ) then
			TellTrack_ChangeArrayOffset(TellTrack_ArrayMaxSize);
		else
			TellTrack_ChangeArrayOffset(1);
		end
		TellTrack_UpdateTellTrackButtonsText();
	end
end

-- Toggles the saved state of the TellTrack list accross sessions
--  if toggle is 1, it's enabled
--  if toggle is 0, it's disabled
--   otherwise, it's toggled
function TellTrack_Toggle_DontSaveList(toggle)
	local oldvalue = TellTrack_DontSaveList;
	local newvalue = TellTrack_Generic_Toggle(toggle, "TellTrack_DontSaveList", "COS_TELLTRACK_DONTSAVELIST", "TELLTRACK_CHAT_DONTSAVELIST", "TELLTRACK_CHAT_SAVELIST", "COS_TELLTRACK_DONTSAVELIST", "TellTrackDontSaveList");
end

-- Toggles the auto creation of TellTrack whisper frame
--  if toggle is 1, it's enabled
--  if toggle is 0, it's disabled
--   otherwise, it's toggled
function TellTrack_Toggle_AutoWhisperFrame(toggle)
	local oldvalue = TellTrack_AutoWhisperFrame;
	local newvalue = TellTrack_Generic_Toggle(toggle, "TellTrack_AutoWhisperFrame", "COS_TELLTRACK_AUTOWHISPERFRAME", "TELLTRACK_CHAT_AUTOWHISPERFRAME", "TELLTRACK_CHAT_MANUALWHISPERFRAME", "COS_TELLTRACK_AUTOWHISPERFRAME", "TellTrackAutoWhisperFrame");
end

-- Toggles Hiding Whispers outside the whisper frame
--  if toggle is 1, it's enabled
--  if toggle is 0, it's disabled
--   otherwise, it's toggled
function TellTrack_Toggle_HideOtherWhispers(toggle)
	local oldvalue = TellTrack_HideOtherWhispers;
	local newvalue = TellTrack_Generic_Toggle(toggle, "TellTrack_HideOtherWhispers", "COS_TELLTRACK_HIDEOTHERWHISPERS", "TELLTRACK_CHAT_HIDEOTHERWHISPERS", "TELLTRACK_CHAT_SHOWOTHERWHISPERS", "COS_TELLTRACK_HIDEOTHERWHISPERS", "TellTrackHideOtherWhispers");
	if ( newvalue == 1 ) then
		TellTrack_HideWhispersInOtherFrames();
	end
end

-- Toggles Whisper chat stamping
--  if toggle is 1, it's enabled
--  if toggle is 0, it's disabled
--   otherwise, it's toggled
function TellTrack_Toggle_TimeStamps(toggle)
	local oldvalue = TellTrack_TimeStamps;
	local newvalue = TellTrack_Generic_Toggle(toggle, "TellTrack_TimeStamps", "COS_TELLTRACK_TIMESTAMPS", "TELLTRACK_CHAT_TIMESTAMPS", "TELLTRACK_CHAT_NOTIMESTAMPS", "COS_TELLTRACK_TIMESTAMPS", "TellTrackTimeStamps");
	TellTrack_RedrawWhisperFrame();
end

-- Toggles the whisper first option
--  if toggle is 1, it's enabled
--  if toggle is 0, it's disabled
--   otherwise, it's toggled
function TellTrack_Toggle_WhisperFirst(toggle)
	local oldvalue = TellTrack_WhisperFirst;
	local newvalue = TellTrack_Generic_Toggle(toggle, "TellTrack_WhisperFirst", "COS_TELLTRACK_WHISPERFIRST", "TELLTRACK_CHAT_WHISPERFIRST", "TELLTRACK_CHAT_LOGFIRST", "COS_TELLTRACK_WHISPERFIRST", "TellTrackWhisperFirst");
end

-- Prints out text to a chat box.
function TellTrack_Print(msg,r,g,b,frame,id,unknown4th)
	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 = 1.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

function TellTrack_ArrowButton_OnLoad(frame)
	frame:RegisterForClicks("LeftButtonUp", "RightButtonUp");
end

function TellTrack_ArrowUpButton_OnClick(button)
	local id = this:GetID();
	if ( not id ) then
		return
	end
	if ( button == "RightButton" ) then
		TellTrack_ChangeArrayOffset(1);
	elseif ( button == "LeftButton" ) then
		TellTrack_PageUp();
	end
end

function TellTrack_ArrowDownButton_OnClick(button)
	local id = this:GetID();
	if ( not id ) then
		return;
	end
	if ( button == "RightButton" ) then
		TellTrack_ChangeArrayOffset(TellTrack_ArrayMaxSize);
	elseif ( button == "LeftButton" ) then
		TellTrack_PageDown();
	end
end

function TellTrack_QButton_OnLoad()
	this:RegisterForClicks("LeftButtonUp", "RightButtonUp");
end

function TellTrack_QButton_OnClick(button)
	local id = this:GetID();
	if ( not id ) then
		return
	end
	TellTrack_ChatCommand_Help();
	--TellTrack_Print(TELLTRACK_CHAT_QUESTION_MARK_INFO,1.0,1.0,0);
end

function TellTrackTextButton_OnEnter()
	local id = this:GetID();
	if ( id ) then
		TellTrackTooltip:SetOwner(TellTrackFrame, "ANCHOR_TOPLEFT");
		TellTrackSetTooltip(id);
	end
end

function TellTrackTextButton_OnLeave()
	if ( TellTrackTooltip:IsOwned(TellTrackFrame) ) then
		TellTrackTooltip:Hide();
	end
end

function TellTrackTextButton_OnLoad()
	this:RegisterForClicks("LeftButtonUp", "RightButtonUp");
end

function TellTrack_InitiateWhisperByID(id)
	local name = TellTrackGetName(id);
	TellTrack_InitiateWhisperToTarget(name);
end

-- 1/20/05 'Retell' added by AnduinLothar
function TellTrack_InitiateWhisperToPreviousTarget()
	if ( not TellTrack_LastTell ) then
		return;
	end
	TellTrack_InitiateWhisperToTarget(TellTrack_LastTell);
end

function TellTrack_WhisperToPreviousTarget(msg)
	if ( not TellTrack_LastTell ) then
		return;
	end
	SendChatMessage(msg, "WHISPER", this.language, TellTrack_LastTell);
end

function TellTrack_InitiateWhisperToTarget(name)
	if ( not name ) then
		return;
	end
	local chatFrame = DEFAULT_CHAT_FRAME;
	chatFrame.editBox.chatType = "WHISPER";
	chatFrame.editBox.tellTarget = name;
	ChatEdit_UpdateHeader(chatFrame.editBox);
	if ( not chatFrame.editBox:IsVisible() ) then
		if (SavedChatFrame_OpenChat) then
			SavedChatFrame_OpenChat("", chatFrame);
		else
			Sea.util.Hooks.ChatFrame_OpenChat.orig("", chatFrame);
		end
	end
end

function TellTrack_Menu_Whisper()
	TellTrack_InitiateWhisperByID(TellTrack_ID);
end

function TellTrack_Menu_Show_Conversation()
	TellTrack_ShowConversationWith(TellTrackGetName(TellTrack_ID), false);
end

function TellTrack_Menu_Who()
	local name = TellTrackGetName(TellTrack_ID);
	SendWho("n-\""..name.."\"");
end

function TellTrack_Menu_GroupeInvite()
	local name = TellTrackGetName(TellTrack_ID);
	InviteByName(name);
end

function TellTrack_Menu_AddToFriend()
	local name = TellTrackGetName(TellTrack_ID);
	AddFriend(name);
end

function TellTrack_Menu_Delete()
	TellTrack_EraseByID(TellTrack_ID);
end

-- Function by AnduinLothar used in right click menu

function TellTrack_Menu_DeleteAll()
	--Print("TT: Reseting Tell List");
	TellTrack_Array = {};
	--Print("TT: Updating Tell Track Button");
	TellTrack_UpdateTellTrackButtonsText();
	-- saves the names to the list
	TellTrack_SaveNames();
end

-- Modified by AnduinLothar to add "Delete All", and "Invert List"

function TellTrackTextButton_InitializeMenuArray()
	local info = { }
	table.insert(info, { text = "   "..BINDING_HEADER_TELLTRACKHEADER, isTitle = 1 });
	table.insert(info, { text = TELLTRACK_WHISPER, func = TellTrack_Menu_Whisper });
	table.insert(info, { text = TELLTRACK_SHOW_CONVERSATION, func = TellTrack_Menu_Show_Conversation });
	table.insert(info, { text = TELLTRACK_WHO, func = TellTrack_Menu_Who });
	table.insert(info, { text = TELLTRACK_GRPINV, func = TellTrack_Menu_GroupeInvite });
	table.insert(info, { text = TELLTRACK_ADDFRIEND, func = TellTrack_Menu_AddToFriend });
	table.insert(info, { text = TELLTRACK_DELETE, func = TellTrack_Menu_Delete });
	table.insert(info, { text = "|cFFCCCCCC--------------|r", disabled = 1, notClickable = 1 });
	table.insert(info, { text = TELLTRACK_DELETE_ALL, func = TellTrack_Menu_DeleteAll });
	table.insert(info, { text = TELLTRACK_INVERT, func = TellTrack_Toggle_Inverted });
	table.insert(info, { text = TELLTRACK_SHOW_ALL_WHISPERS, func = TellTrack_ShowAllWhispers });
	table.insert(info, { text = TELLTRACK_CREATE_CHATFRAME, func = TellTrack_CreateWhisperChatFrame });
	table.insert(info, { text = "|cFFCCCCCC--------------|r", disabled = 1, notClickable = 1 });
	table.insert(info, { text = TELLTRACK_CANCEL, func = function () end });
	for index, menuLine in pairs(info) do
		UIDropDownMenu_AddButton(menuLine);
	end
end

function TellTrackTextButton_GetMenuLevels(menu)
	if ( not menu ) then
		return 0;
	end
	local menuLevels = 1;
	for i = 1, #(menu) do
		if ( menu[i][1] ) then
			menuLevels = menuLevels + TellTrackTextButton_GetMenuLevels(menu[i]);
			break;
		end
	end
	return menuLevels;
end

function TellTrackTextButton_ShowMenu()
	ToggleDropDownMenu(1, nil, TellTrackDropDown, "cursor", 0, 0);
	return true;
end

function TellTrackTextButton_OnClick(button)
	local id = this:GetID();
	if ( not id ) then
		return;
	end
	if ( TellTrack_Array[TellTrack_GetArrayId(id)] == nil ) then
		return;
	end
	TellTrack_ID = id;
	if ( button == "RightButton" ) then
		if ( not TellTrackTextButton_ShowMenu() ) then
			TellTrack_EraseByID(id);
		end
	elseif ( button == "LeftButton" ) then
		if (IsShiftKeyDown() and IsAltKeyDown()) then
			TellTrack_Menu_Delete();
		elseif (IsShiftKeyDown()) then
			TellTrack_Menu_Who();
		elseif (IsAltKeyDown()) then
			TellTrack_Menu_GroupeInvite();
		elseif (IsControlKeyDown()) then
			TellTrack_Menu_AddToFriend();
		else
			local name = TellTrackGetName(id);
			if (TellTrack_WhisperFirst == 1) then
				if (SELECTED_DOCK_FRAME.editBox:IsVisible()) and (SELECTED_DOCK_FRAME.editBox.chatType == "WHISPER") and (SELECTED_DOCK_FRAME.editBox. tellTarget == name) then
					TellTrack_ShowConversationWith(name, false);
				else
					TellTrack_InitiateWhisperToTarget(name);
				end
			else
				if (SELECTED_DOCK_FRAME == getglobal(TellTrack_WhisperChatFrame[GetRealmName()][UnitName("player")])) and (TellTrack_CurrentConversation.name == name) then
					TellTrack_InitiateWhisperToTarget(name);
					--TellTrack_InitiateWhisperByID(id);
				else
					TellTrack_ShowConversationWith(name, false);
				end
			end
		end
	end
end

function TellTrack_GetArrayId(id)
	local newID
	if ( TellTrack_InvertedList == 1 ) then
		newID = TellTrack_ArrayMaxSize - (id + TellTrack_ArrayOffset) + 2;
	else
		newID = (id + TellTrack_ArrayOffset) - 1;
	end
	return newID;
end

function TellTrack_EraseByID(id)
	if ( id ) then
		id = TellTrack_GetArrayId(id);
		if ( TellTrack_Array ) and ( TellTrack_Array[id] ) then
			if ( ( TellTrackTooltip:IsVisible() ) and ( TellTrack_TooltipSetId == id ) ) then
				TellTrackTooltip:Hide();
			end
			TellTrack_Array[id] = nil;
		end
		TellTrack_CompressList();
		TellTrack_UpdateTellTrackButtonsText();
	end
end

function TellTrackGetName(id)
	if ( id ) then
		id = TellTrack_GetArrayId(id);
		if ( ( TellTrack_Array ) and ( TellTrack_Array[id] ) and ( TellTrack_Array[id].name ) ) then
			return TellTrack_Array[id].name;
		end
	end
	return nil;
end

function TellTrackSetTooltip(id)
	local name = TellTrackGetName(id);
	if ( name ) then
		TellTrackTooltip:SetText(name);
		TellTrack_TooltipSetId = id;
	end
end

-- Yet another function from George Warner, modified a bit to fit my own nefarious purposes. 
-- It can now accept r, g and b specifications, too (leaving out a), as well as handle 255 255 255
-- Source : http://www.cosmosui.org/cgi-bin/bugzilla/show_bug.cgi?id=159
function TellTrack_GetColorFormatString(a, r, g, b)
	local percent = false;
	if ( ( ( not b ) or ( b <= 1 ) ) and ( a <= 1 ) and ( r <= 1 ) and ( g <= 1) ) then percent = true; end
	if ( ( not b ) and ( a ) and ( r ) and ( g ) ) then b = g; g = r; r = a; if ( percent ) then a = 1; else a = 255; end end
	if ( percent ) then a = a * 255; r = r * 255; g = g * 255; b = b * 255; end
	a = TellTrack_GetByteValue(a); r = TellTrack_GetByteValue(r); g = TellTrack_GetByteValue(g); b = TellTrack_GetByteValue(b);
	
	--return format("[c%02X%02X%02X%02X%%s]r", a, r, g, b);
	return format("|c%02X%02X%02X%02X%%s|r", a, r, g, b);
end

function TellTrack_UpdateTellTrackButtonsText()
	local lastSentToNameFormatStr = TellTrack_GetColorFormatString(0.2, 1.0, 0.2);
	local lastRecievedFromNameFormatStr = TellTrack_GetColorFormatString(1.0, 0.2, 0.2);
	local noNameFormatStr = TellTrack_GetColorFormatString(0.4, 0.4, 0.4);
	local id = 0;
	for i = 1, TellTrack_ButtonCount do
		local buttonText = getglobal("TellTrack"..i.."Text");
		local formatStr, valueStr;
		id = TellTrack_GetArrayId(i);
		if ( ( TellTrack_Array[id] ) and ( TellTrack_Array[id].name ) ) then
			valueStr = TellTrack_Array[id].name;
			if (TellTrack_OfflinePlayers[valueStr]) then
				formatStr = noNameFormatStr;
			elseif( TellTrack_Array[id].sentTo ) then
				formatStr = lastSentToNameFormatStr;
			else
				formatStr = lastRecievedFromNameFormatStr;
			end
			if (TellTrack_Array[id].unread) and (TellTrack_Array[id].unread > 0) then
				valueStr = "("..TellTrack_Array[id].unread..") "..valueStr;
			end
		else
			formatStr = noNameFormatStr;
			valueStr = "Empty";
		end
		if ( buttonText ) then
			buttonText:SetText(format(formatStr, valueStr));
			buttonText:Show();
		end
	end
end

--[[ The following function was edited by AnduinLothar (1/15/05)
		to add the following features:

	 The last person whispered is automaticly moved to the bottom
	of the list and everytime you whisper it autoscrolls to the 
	bottom of the list.  Also if the list is full the top name is
	deleted and the new name is appended to the bottom.
        
        1/17/05
	Added recieved/sent boolean for color on button reload.
	
]]--
function TellTrack_HandleMessageSentOrRecieved(target, recieved, msg)
	local tempName = strlower(target);
	local firstEmptySlotIndex = nil;
	local previousNameInstance = nil;
	for i = 1, TellTrack_ArrayMaxSize do
		if ( ( not firstEmptySlotIndex ) and ( not TellTrack_Array[i] )) then
			--Print("TT: First empty slot "..i);
			firstEmptySlotIndex = i;
		elseif( not firstEmptySlotIndex ) then
			if( TellTrack_Array[i].compareName == tempName ) then
				--Print("TT: Found in array");
				previousNameInstance = i;
			end
		end
	end
	if( (not firstEmptySlotIndex) and (not previousNameInstance) ) then
		--Print("TT: No empty slots found, deleting first name");
		TellTrack_Array[1] = nil;
		TellTrack_CompressList();
		firstEmptySlotIndex = TellTrack_ArrayMaxSize;
	elseif( (not firstEmptySlotIndex) and previousNameInstance) then
		firstEmptySlotIndex = TellTrack_ArrayMaxSize+1;
	end
	for i = 1, TellTrack_ArrayMaxSize do
		if ( previousNameInstance == i and firstEmptySlotIndex == i+1) then
			--Print("TT: Name found is last name sent "..i);
			TellTrack_Array[i].sentTo = not recieved;
			if (not TellTrack_Array[i].unread) then
				TellTrack_Array[i].unread = 0;
			end
			if (recieved) and (TellTrack_WhisperChatFrame[GetRealmName()][UnitName("player")]) then
				if (not getglobal(TellTrack_WhisperChatFrame[GetRealmName()][UnitName("player")]):IsVisible()) or (TellTrack_CurrentConversation.name ~= target and not TellTrack_CurrentConversation.showAll) then
					TellTrack_Array[i].unread = TellTrack_Array[i].unread + 1;
				end
			end
			firstEmptySlotIndex = firstEmptySlotIndex-1
			if (TellTrack_InvertedList == 1) then
				if( i < TellTrack_ArrayMaxSize-TellTrack_ButtonCount and (TellTrack_ArrayMaxSize-i+1) ~= TellTrack_ArrayOffset ) then
					TellTrack_ChangeArrayOffset(TellTrack_ArrayMaxSize-i+1);
				end
			else
				if( i > TellTrack_ButtonCount and (i-TellTrack_ButtonCount+1) ~= TellTrack_ArrayOffset ) then
					TellTrack_ChangeArrayOffset(i-TellTrack_ButtonCount+1);
				end
			end
		elseif ( previousNameInstance == i and firstEmptySlotIndex ~= i+1) then
			--Print("TT: Removing old instance at "..i);
			TellTrack_Array[i] = nil;
			TellTrack_CompressList()
			firstEmptySlotIndex = firstEmptySlotIndex-1;
		elseif ( firstEmptySlotIndex == i ) then
			--Print("TT: Adding to array "..i);
			TellTrack_Array[i] = {};
			TellTrack_Array[i].name = target;
			TellTrack_Array[i].compareName = tempName;
			TellTrack_Array[i].sentTo = not recieved;
			if (not TellTrack_Array[i].unread) then
				TellTrack_Array[i].unread = 0;
			end
			if (recieved) and (TellTrack_WhisperChatFrame[GetRealmName()][UnitName("player")]) then
				if (not getglobal(TellTrack_WhisperChatFrame[GetRealmName()][UnitName("player")]):IsVisible()) or (TellTrack_CurrentConversation.name ~= target and not TellTrack_CurrentConversation.showAll) then
					TellTrack_Array[i].unread = TellTrack_Array[i].unread + 1;
				end
			end
			if (TellTrack_InvertedList == 1) then
				if( i < TellTrack_ArrayMaxSize-TellTrack_ButtonCount and (TellTrack_ArrayMaxSize-i+1) ~= TellTrack_ArrayOffset ) then
					TellTrack_ChangeArrayOffset(TellTrack_ArrayMaxSize-i+1);
				end
			else
				if( i > TellTrack_ButtonCount and (i-TellTrack_ButtonCount+1) ~= TellTrack_ArrayOffset ) then
					TellTrack_ChangeArrayOffset(i-TellTrack_ButtonCount+1);
				end
			end
		end
	end
	--Print("TT: Updating Tell Track Button");
	TellTrack_UpdateTellTrackButtonsText();
	-- saves the names to the list
	TellTrack_SaveNames();
end

-- thanks again to Lash for the idea and the pushing of this function :)
function TellTrack_CompressList()
	local index;
	local otherArray = {};
	index = 1;
	if ( TellTrack_Array ) then
		for k, v in pairs(TellTrack_Array) do
			otherArray[index] = v;
			index = index + 1;
		end
	end
	TellTrack_Array = otherArray;
	TellTrack_UpdateTellTrackButtonsText();
end


function TellTrack_ChangeArrayOffset(offset)
	local capSize = TellTrack_ArrayMaxSize - TellTrack_ButtonCount+1;
	if ( offset <= 0 ) then
		offset = 1;
	end
	if ( offset > capSize ) then
		offset = capSize;
	end
	TellTrack_ArrayOffset = offset;
	TellTrack_UpdateTellTrackButtonsText();
end

function TellTrack_PageDown()
	TellTrack_ChangeArrayOffset(TellTrack_ArrayOffset + TellTrack_ButtonCount);
end

function TellTrack_PageUp()
	TellTrack_ChangeArrayOffset(TellTrack_ArrayOffset - TellTrack_ButtonCount);
end

function TellTrack_OnMouseWheel(value)
	if ( value > 0 ) then
		TellTrack_ChangeArrayOffset(TellTrack_ArrayOffset - 1);
	elseif ( value < 0 ) then
		TellTrack_ChangeArrayOffset(TellTrack_ArrayOffset + 1);
	end
end


function TellTrack_QButton_OnEnter()
	TellTrackTooltip:SetOwner(TellTrackFrame, "ANCHOR_TOPLEFT");
	TellTrackTooltip:SetText(TELLTRACK_QUESTION_MARK_TOOLTIP);
end

function TellTrack_QButton_OnLeave()
	if ( TellTrackTooltip:IsOwned(TellTrackFrame) ) then
		TellTrackTooltip:Hide();
	end
end

function TellTrack_ResizeButton_OnEnter()
	TellTrackTooltip:SetOwner(TellTrackFrame, "ANCHOR_TOPLEFT");
	TellTrackTooltip:SetText(TELLTRACK_RESIZE_TOOLTIP);
end

function TellTrack_ResizeButton_OnLeave()
	if ( TellTrackTooltip:IsOwned(TellTrackFrame) ) then
		TellTrackTooltip:Hide();
	end
end

function TellTrack_IsClassHorde(class)
	local hordeClasses = { "Orc", "Tauren", "Troll", "Undead" };
	for k, v in pairs(hordeClasses) do
		if ( class == v ) then
			return true;
		end
	end
	return false;
end

-- returns the index name for the load/save funcs
function TellTrack_GetListIndex()
	local firstString = GetCVar("realmName");
	local secondString = UnitRace("player");
	--local secondString = UnitName("player");
	if ( not firstString ) or ( not secondString ) then
		return nil;
	end
	if ( TellTrack_IsClassHorde(secondString) ) then
		secondString = "Horde";
	else
		secondString = "Alliance";
	end
	return format("%s_%s", firstString, secondString);
end

function TellTrack_LoadNames()
	local index = TellTrack_GetListIndex();
	if ( not index ) then
		if ( Cosmos_ScheduleByName ) then
			Cosmos_ScheduleByName("TELLTRACK_LOADNAMES", 0.5, TellTrack_LoadNames);
		end
		return;
	end
	if ( not TellTrack_SavedList ) then
		return;
	end;
	local list = TellTrack_SavedList[index];
	--1/20/05 now aditionally loads sentTo instead of just names. This allows loading of 'recieved/sent' status.
	if ( list ) then
		for k, v in pairs(list) do
			if(type(v) == "table") then
				if(v.name and v.sentTo ~= nil) then
					TellTrack_HandleMessageSentOrRecieved(v.name, not v.sentTo);
				elseif(v.name) then
					TellTrack_HandleMessageSentOrRecieved(v.name, false);
				end
			elseif(type(v) == "string") then
				TellTrack_HandleMessageSentOrRecieved(v, false); --Support for previous TellTrack Saved Lists (all are marked as sent)
			end
			TellTrack_Array[k].unread = 0;
		end
	end
end

function TellTrack_SaveNames()
	local index = TellTrack_GetListIndex();
	if ( not index ) then
		if ( Cosmos_ScheduleByName ) then
			Cosmos_ScheduleByName("TELLTRACK_SAVENAMES", 0.5, TellTrack_SaveNames);
		end
		return;
	end
	--[[
	local list = {};
	for k, v in pairs(TellTrack_Array) do
		if (v.name) then
			table.insert(list, v.name);
		end
	end
	]]--
	 --1/20/05 now saves whole array instead of just names. This allows saving of 'recieved/sent' status.
	list = TellTrack_Array;
        
	if ( not TellTrack_SavedList ) then
		TellTrack_SavedList = {};
	end
	TellTrack_SavedList[index] = list;
end

-- helper function - returns value as a byte
function TellTrack_GetByteValue(pValue)
	local value = tonumber(pValue);
	if ( value <= 0 ) then return 0; end
	if ( value >= 255 ) then return 255; end
	return value;
end


function TellTrack_ModifyButtonCount(height)
	height = height - 44;   --insets and arrow buttons
	local newCount = floor((height)/20);
	if ( newCount ~= TellTrack_ButtonCount ) then
		TellTrack_ButtonCount = newCount;
		for i=1, TellTrack_ArrayMaxSize do
			local thisButton = getglobal("TellTrack"..i);
			if not thisButton:IsVisible() and i <= TellTrack_ButtonCount then
				thisButton:Show();
			elseif thisButton:IsVisible() and i > TellTrack_ButtonCount then
				thisButton:Hide();
			end
		end
	end
	local offset = floor((height-TellTrack_ButtonCount*20+20)/2);
	TellTrackFrameArrowUpButton:SetHeight(offset);
	TellTrackFrameArrowDownButton:SetHeight(offset);
end

function TellTrackFrame_OnSizeChanged()
	local width = TellTrackFrame:GetWidth()-24;
	TellTrackFrameArrowUpButton:SetWidth(width);
	for i=1, TellTrack_ArrayMaxSize do
		getglobal("TellTrack"..i):SetWidth(width);
	end
	TellTrackFrameArrowDownButton:SetWidth(width);
	TellTrack_ModifyButtonCount(TellTrackFrame:GetHeight());
	TellTrack_ChangeArrayOffset(TellTrack_ArrayOffset);
	TellTrack_UpdateTellTrackButtonsText();
end

function TellTrack_ArrowButton_OnSizeChanged(frame)
	local height = frame:GetHeight();
	getglobal(frame:GetName().."RightBG"):SetHeight(height);
	getglobal(frame:GetName().."LeftBG"):SetHeight(height);
	getglobal(frame:GetName().."Arrow"):SetHeight(height);
end


