-- CHAT PROTOTYPE STUFF
SELECTED_DOCK_FRAME = nil;
DOCKED_CHAT_FRAMES = {};
DOCK_COPY = {};

MOVING_CHATFRAME = nil;

CHAT_TAB_SHOW_DELAY = 0.2;
CHAT_TAB_HIDE_DELAY = 1;
CHAT_FRAME_FADE_TIME = 0.15;
CHAT_FRAME_FADE_OUT_TIME = 2.0;
CHAT_FRAME_BUTTON_FRAME_MIN_ALPHA = 0.2;

CHAT_FRAME_TAB_SELECTED_MOUSEOVER_ALPHA = 1.0;
CHAT_FRAME_TAB_SELECTED_NOMOUSE_ALPHA = 0.4;
CHAT_FRAME_TAB_ALERTING_MOUSEOVER_ALPHA = 1.0;
CHAT_FRAME_TAB_ALERTING_NOMOUSE_ALPHA = 1.0;
CHAT_FRAME_TAB_NORMAL_MOUSEOVER_ALPHA = 0.6;
CHAT_FRAME_TAB_NORMAL_NOMOUSE_ALPHA = 0.2;

DEFAULT_CHATFRAME_ALPHA = 0.25;
DEFAULT_CHATFRAME_COLOR = {r = 0, g = 0, b = 0};

CHAT_FRAME_NORMAL_MIN_HEIGHT = 120;
CHAT_FRAME_BIGGER_MIN_HEIGHT = 147;
CHAT_FRAME_MIN_WIDTH = 296;

CURRENT_CHAT_FRAME_ID = nil;

CHAT_FRAME_TEXTURES = {
	"Background",
	"TopLeftTexture",
	"BottomLeftTexture",
	"TopRightTexture",
	"BottomRightTexture",
	"LeftTexture",
	"RightTexture",
	"BottomTexture",
	"TopTexture",
	--"ResizeButton",
	
	"ButtonFrameBackground",
	"ButtonFrameTopLeftTexture",
	"ButtonFrameBottomLeftTexture",
	"ButtonFrameTopRightTexture",
	"ButtonFrameBottomRightTexture",
	"ButtonFrameLeftTexture",
	"ButtonFrameRightTexture",
	"ButtonFrameBottomTexture",
	"ButtonFrameTopTexture",
}

CHAT_FRAMES = {};

function FloatingChatFrame_OnLoad(self)
	--IMPORTANT NOTE: This function isn't run by ChatFrame1.
	tinsert(CHAT_FRAMES, self:GetName());
	
	FCF_SetTabPosition(self, 0);
	FloatingChatFrame_Update(self:GetID());
	
	FCFTab_UpdateColors(_G[self:GetName().."Tab"], true);
	self:SetClampRectInsets(-35, 35, 26, -50);
	
	local chatTab = _G[self:GetName().."Tab"];
	chatTab.mouseOverAlpha = CHAT_FRAME_TAB_SELECTED_MOUSEOVER_ALPHA;
	chatTab.noMouseAlpha = CHAT_FRAME_TAB_SELECTED_NOMOUSE_ALPHA;

	FRAMELOCK_STATES.PETBATTLEOPENING[self:GetName()] = "hidden";
	FRAMELOCK_STATES.PETBATTLEOPENING[chatTab:GetName()] = "hidden";
	UpdateFrameLock(self);
	UpdateFrameLock(chatTab);
end

function FloatingChatFrame_OnEvent(self, event, ...)
	if ( (event == "UPDATE_CHAT_WINDOWS") or (event == "UPDATE_FLOATING_CHAT_WINDOWS") ) then
		FloatingChatFrame_Update(self:GetID(), 1);
		self.isInitialized = 1;
	elseif ( event == "UPDATE_CHAT_COLOR" ) then
		local chatType, r, g, b = ...;
		if ( self.isTemporary and self.chatType == chatType ) then
			local tab = _G[self:GetName().."Tab"];
			tab.selectedColorTable.r, tab.selectedColorTable.g, tab.selectedColorTable.b = r, g, b;
			FCFTab_UpdateColors(tab, not self.isDocked or self == FCFDock_GetSelectedWindow(GENERAL_CHAT_DOCK));
		end
	end
end

function FloatingChatFrame_OnMouseScroll(self, delta)
	if ( delta > 0 ) then
		self:ScrollUp();
	else
		self:ScrollDown();
	end
end

function FCF_GetChatWindowInfo(id)
	if ( id > NUM_CHAT_WINDOWS ) then
		local frame = _G["ChatFrame"..id];
		local tab = _G["ChatFrame"..id.."Tab"];
		local background = _G["ChatFrame"..id.."Background"];
		
		if ( frame and tab and background ) then
			local r, g, b, a = background:GetVertexColor();
			
			return tab:GetText(), select(2, frame:GetFont()), r, g, b, a, frame:IsShown(), frame.isLocked, frame.isDocked, frame.isUninteractable;
			--This is a temporary chat window. Pass this to whatever handles those options.
		end
	else
		return GetChatWindowInfo(id);
	end
end

function FCF_CopyChatSettings(copyTo, copyFrom)
	local name, fontSize, r, g, b, a, shown, locked, docked, uninteractable = FCF_GetChatWindowInfo(copyFrom:GetID());
	FCF_SetWindowColor(copyTo, r, g, b, 1);
	FCF_SetWindowAlpha(copyTo, a, 1);
	--If we're copying to a docked window, we don't want to copy locked.
	if ( not copyTo.isDocked ) then
		FCF_SetLocked(copyTo, locked);
	end
	FCF_SetUninteractable(copyTo, uninteractable);
	FCF_SetChatWindowFontSize(nil, copyTo, fontSize);
end

function FloatingChatFrame_Update(id, onUpdateEvent)
	local chatFrame = _G["ChatFrame"..id];
	local chatTab = _G["ChatFrame"..id.."Tab"];
	
	local name, fontSize, r, g, b, a, shown, locked, docked, uninteractable = FCF_GetChatWindowInfo(id);
	
	-- Set Tab Name
	FCF_SetWindowName(chatFrame, name, 1)

	if ( onUpdateEvent ) then
		-- Set Frame Color and Alpha
		FCF_SetWindowColor(chatFrame, r, g, b, 1);
		FCF_SetWindowAlpha(chatFrame, a, 1);
		FCF_SetLocked(chatFrame, locked);
		FCF_SetUninteractable(chatFrame, uninteractable);
	end

	if ( shown ) then
		if ( not chatFrame.minimized ) then
			chatFrame:Show();
		end
		FCF_SetTabPosition(chatFrame, 0);
	else
		if ( not chatFrame.isDocked ) then
			chatFrame:Hide();
			chatTab:Hide();
		end
	end
	
	if ( docked ) then
		FCF_DockFrame(chatFrame, docked, (id == 1));
	else
		if ( shown ) then
			FCF_UnDockFrame(chatFrame);
			if ( not chatFrame.minimized ) then
				chatTab:Show();
			end
		elseif ( not chatFrame.isTemporary ) then
			FCF_Close(chatFrame);
		end
	end
	
	if ( not chatFrame.isTemporary and (chatFrame == DEFAULT_CHAT_FRAME or not chatFrame.isDocked)) then
		FCF_RestorePositionAndDimensions(chatFrame);
	end

	FCF_UpdateButtonSide(chatFrame);
end

-- Channel Dropdown
function FCFOptionsDropDown_OnLoad(self)
	CURRENT_CHAT_FRAME_ID = self:GetParent():GetID();
	UIDropDownMenu_Initialize(self, FCFOptionsDropDown_Initialize, "MENU");
	UIDropDownMenu_SetButtonWidth(self, 50);
	UIDropDownMenu_SetWidth(self, 50);
end

function FCFOptionsDropDown_Initialize(dropDown)
	-- Window preferences
	local name, fontSize, r, g, b, a, shown = FCF_GetChatWindowInfo(FCF_GetCurrentChatFrameID());
	local info;

	local chatFrame = FCF_GetCurrentChatFrame();
	local isTemporary = chatFrame and chatFrame.isTemporary;
	
	-- If level 2
	if ( UIDROPDOWNMENU_MENU_LEVEL == 2 ) then
		-- If this is the font size menu then create dropdown
		if ( UIDROPDOWNMENU_MENU_VALUE == FONT_SIZE ) then
			-- Add the font heights from the font height table
			local value;
			for i=1, #CHAT_FONT_HEIGHTS do
				value = CHAT_FONT_HEIGHTS[i];
				info = UIDropDownMenu_CreateInfo();
				info.text = format(FONT_SIZE_TEMPLATE, value);
				info.value = value;
				info.func = FCF_SetChatWindowFontSize;

				local fontFile, fontHeight, fontFlags = FCF_GetCurrentChatFrame():GetFont();
				if ( value == floor(fontHeight+0.5) ) then
					info.checked = 1;
				end

				UIDropDownMenu_AddButton(info, UIDROPDOWNMENU_MENU_LEVEL);
			end
			return;
		end
		return;
	end
	-- Window options
	info = UIDropDownMenu_CreateInfo();
	local dropDownChatFrame = FCF_GetCurrentChatFrame(dropDown);
	if ( dropDownChatFrame and dropDownChatFrame ~= DEFAULT_CHAT_FRAME and dropDownChatFrame.isDocked ) then
		info.text = UNDOCK_WINDOW;
	elseif ( dropDownChatFrame and dropDownChatFrame.isLocked ) then
		info.text = UNLOCK_WINDOW;
	else
		info.text = LOCK_WINDOW;
	end
	info.func = FCF_ToggleLock;
	info.notCheckable = 1;
	UIDropDownMenu_AddButton(info);

	--Add Uninteractable button
	info = UIDropDownMenu_CreateInfo();
	if ( FCF_GetCurrentChatFrame(dropDown) and FCF_GetCurrentChatFrame(dropDown).isUninteractable) then
		info.text = MAKE_INTERACTABLE;
	else
		info.text = MAKE_UNINTERACTABLE;
	end
	info.func = FCF_ToggleUninteractable;
	info.notCheckable = 1;
	UIDropDownMenu_AddButton(info);
	
	if ( not isTemporary ) then
		-- Add name button
		info = UIDropDownMenu_CreateInfo();
		info.text = RENAME_CHAT_WINDOW;
		info.func = FCF_RenameChatWindow_Popup;
		info.notCheckable = 1;
		UIDropDownMenu_AddButton(info);
	end
	
	if ( chatFrame == DEFAULT_CHAT_FRAME ) then
		-- Create new chat window
		info = UIDropDownMenu_CreateInfo();
		info.text = NEW_CHAT_WINDOW;
		info.func = FCF_NewChatWindow;
		info.notCheckable = 1;
		if (FCF_GetNumActiveChatFrames() == NUM_CHAT_WINDOWS ) then
			info.disabled = 1;
		end
		UIDropDownMenu_AddButton(info);

		-- Reset Chat windows to default
		info = UIDropDownMenu_CreateInfo();
		info.text = RESET_ALL_WINDOWS;
		info.func = FCF_ResetAllWindows;
		info.notCheckable = 1;
		UIDropDownMenu_AddButton(info);
	end

	-- Close current chat window
	if ( chatFrame and (chatFrame ~= DEFAULT_CHAT_FRAME and not IsCombatLog(chatFrame)) ) then
		if ( not chatFrame.isTemporary ) then
			info = UIDropDownMenu_CreateInfo();
			info.text = CLOSE_CHAT_WINDOW;
			info.func = FCF_PopInWindow;
			info.arg1 = FCF_GetCurrentChatFrame(dropDown);
			info.notCheckable = 1;
			UIDropDownMenu_AddButton(info);
		elseif ( chatFrame.isTemporary and (chatFrame.chatType == "WHISPER" or chatFrame.chatType == "BN_WHISPER") ) then
			info = UIDropDownMenu_CreateInfo();
			info.text = CLOSE_CHAT_WHISPER_WINDOW;
			info.func = FCF_PopInWindow;
			info.arg1 = FCF_GetCurrentChatFrame(dropDown);
			info.notCheckable = 1;
			UIDropDownMenu_AddButton(info);
		elseif ( chatFrame.isTemporary and (chatFrame.chatType == "BN_CONVERSATION" ) ) then
			if ( GetCVar("conversationMode") == "popout" or GetCVar("conversationMode") == "popout_and_inline" ) then
				info = UIDropDownMenu_CreateInfo();
				info.text = CLOSE_AND_LEAVE_CHAT_CONVERSATION_WINDOW;
				info.func = FCF_LeaveConversation;
				info.arg1 = FCF_GetCurrentChatFrame(dropDown);
				info.notCheckable = 1;
				UIDropDownMenu_AddButton(info);
			else
				info = UIDropDownMenu_CreateInfo();
				info.text = CLOSE_CHAT_CONVERSATION_WINDOW;
				info.func = FCF_PopInWindow;
				info.arg1 = FCF_GetCurrentChatFrame(dropDown);
				info.notCheckable = 1;
				UIDropDownMenu_AddButton(info);
			end
		elseif ( chatFrame.isTemporary ) then
			info = UIDropDownMenu_CreateInfo();
			info.text = CLOSE_CHAT_WINDOW;
			info.func = FCF_Close;
			info.arg1 = FCF_GetCurrentChatFrame(dropDown);
			info.notCheckable = 1;
			UIDropDownMenu_AddButton(info);
		else
			error(format("Unhandled temporary window type. chatType: %s, chatTarget %s", tostring(chatFrame.chatType), tostring(chatFrame.chatTarget)));
		end
	end

	-- Display header
	info = UIDropDownMenu_CreateInfo();
	info.text = DISPLAY;
	info.notClickable = 1;
	info.isTitle = 1;
	info.notCheckable = 1;
	UIDropDownMenu_AddButton(info);

	-- Font size
	info = UIDropDownMenu_CreateInfo();
	info.text = FONT_SIZE;
	--info.notClickable = 1;
	info.hasArrow = 1;
	info.func = nil;
	info.notCheckable = 1;
	UIDropDownMenu_AddButton(info);

	-- Set Background color
	info = UIDropDownMenu_CreateInfo();
	info.text = BACKGROUND;
	info.hasColorSwatch = 1;
	info.notCheckable = 1;
	info.r = r;
	info.g = g;
	info.b = b;
	-- Done because the slider is reversed
	if ( a ) then
		a = 1- a;
	end
	info.opacity = a;
	info.swatchFunc = FCF_SetChatWindowBackGroundColor;
	info.func = UIDropDownMenuButton_OpenColorPicker;
	--info.notCheckable = 1;
	info.hasOpacity = 1;
	info.opacityFunc = FCF_SetChatWindowOpacity;
	info.cancelFunc = FCF_CancelWindowColorSettings;
	UIDropDownMenu_AddButton(info);

	if ( not isTemporary ) then
		-- Filter header
		info = UIDropDownMenu_CreateInfo();
		info.text = FILTERS;
		--info.notClickable = 1;
		info.isTitle = 1;
		info.notCheckable = 1;
		UIDropDownMenu_AddButton(info);

		-- Configure settings
		info = UIDropDownMenu_CreateInfo();
		info.text = CHAT_CONFIGURATION;
		info.func = function() ShowUIPanel(ChatConfigFrame); end;
		info.notCheckable = 1;
		UIDropDownMenu_AddButton(info);
	end
end
--[[
function FCFDropDown_LoadServerChannels(...)
	local checked;
	local channelList = FCF_GetCurrentChatFrame().channelList;
	local zoneChannelList = FCF_GetCurrentChatFrame().zoneChannelList;
	local info, channel;

	-- Server Channels header
	info = UIDropDownMenu_CreateInfo();
	info.text = SERVER_CHANNELS;
	info.notClickable = 1;
	info.isTitle = 1;
	UIDropDownMenu_AddButton(info, UIDROPDOWNMENU_MENU_LEVEL);

	info = UIDropDownMenu_CreateInfo();
	for i=1, select("#", ...) do
		checked = nil;
		channel = select(i, ...);
		if ( channelList ) then
			for index, value in pairs(channelList) do
				if ( value == channel ) then
					checked = 1;
				end
			end
		end
		if ( zoneChannelList ) then
			for index, value in pairs(zoneChannelList) do
				if ( value == channel ) then
					checked = 1;
				end
			end
		end

		info.text = channel;
		info.value = channel;
		info.func = FCFServerChannelsDropDown_OnClick;
		info.checked = checked;
		info.keepShownOnClick = 1;
		UIDropDownMenu_AddButton(info, UIDROPDOWNMENU_MENU_LEVEL);
	end
end

function FCFServerChannelsDropDown_OnClick()
	if ( UIDropDownMenuButton_GetChecked() ) then
		ChatFrame_RemoveChannel(FCF_GetCurrentChatFrame(), UIDropDownMenuButton_GetName());
	else
		JoinPermanentChannel(UIDropDownMenuButton_GetName(), nil, FCF_GetCurrentChatFrameID(), 1);
		ChatFrame_AddChannel(FCF_GetCurrentChatFrame(), UIDropDownMenuButton_GetName());
	end
end
]]
function FCFDropDown_LoadChannels(...)
	local checked;
	local channelList = FCF_GetCurrentChatFrame().channelList;
	local zoneChannelList = FCF_GetCurrentChatFrame().zoneChannelList;
	local info = UIDropDownMenu_CreateInfo();
	local channel, tag;
	for i=1, select("#", ...), 2 do
		checked = nil;
		tag = "CHANNEL"..select(i, ...);
		channel = select(i+1, ...);
		if ( channelList ) then
			for index, value in pairs(channelList) do
				if ( value == channel ) then
					checked = 1;
				end
			end
		end
		if ( zoneChannelList ) then
			for index, value in pairs(zoneChannelList) do
				if ( value == channel ) then
					checked = 1;
				end
			end
		end
		info.text = channel;
		info.value = tag;
		info.func = FCFChannelDropDown_OnClick;
		info.checked = checked;
		info.keepShownOnClick = 1;
		-- Color the chat channel
		local color = ChatTypeInfo[tag];
		info.hasColorSwatch = 1;
		info.r = color.r;
		info.g = color.g;
		info.b = color.b;
		-- Set the function the color picker calls
		info.swatchFunc = FCF_SetChatTypeColor;
		info.cancelFunc = FCF_CancelFontColorSettings;
		UIDropDownMenu_AddButton(info, UIDROPDOWNMENU_MENU_LEVEL);
	end
end

function FCFChannelDropDown_OnClick()
	if ( UIDropDownMenuButton_GetChecked() ) then
		ChatFrame_RemoveChannel(FCF_GetCurrentChatFrame(), UIDropDownMenuButton_GetName());
	else
		ChatFrame_AddChannel(FCF_GetCurrentChatFrame(), UIDropDownMenuButton_GetName());
	end
end

-- Used to display chattypegroups
function FCFDropDown_LoadChatTypes(menuChatTypeGroups)
	local checked, chatTypeInfo;
	local messageTypeList = FCF_GetCurrentChatFrame().messageTypeList;
	local info, group;
	for index, value in pairs(menuChatTypeGroups) do
		checked = nil;
		if ( messageTypeList ) then
			for joinedIndex, joinedValue in pairs(messageTypeList) do
				if ( value == joinedValue ) then
					checked = 1;
				end
			end
		end
		info = UIDropDownMenu_CreateInfo();
		info.value = value;
		info.func = FCFMessageTypeDropDown_OnClick;
		info.checked = checked;
		-- Set to keep shown on button click
		info.keepShownOnClick = 1;
		
		-- If more than one message type in a Chat Type Group need to show an expand arrow
		group = ChatTypeGroup[value];
		if ( getn(group) > 1 ) then
			info.text = _G[value];
			info.hasArrow = 1;
			UIDropDownMenu_AddButton(info, UIDROPDOWNMENU_MENU_LEVEL);
		else
			info.text = _G[group[1]];
			chatTypeInfo = ChatTypeInfo[FCF_StripChatMsg(group[1])];
			-- If no chatTypeInfo then don't display
			if ( chatTypeInfo ) then
				-- Set the function to be called when a color is set
				info.swatchFunc = FCF_SetChatTypeColor;
				-- Set the swatch color info
				info.hasColorSwatch = 1;
				info.r = chatTypeInfo.r;
				info.g = chatTypeInfo.g;
				info.b = chatTypeInfo.b;
				info.cancelFunc = FCF_CancelFontColorSettings;	
				UIDropDownMenu_AddButton(info, UIDROPDOWNMENU_MENU_LEVEL);
			end
		end
	end
end

--  Used to display chatsubtypes
function FCF_LoadChatSubTypes(chatGroup)
	if ( chatGroup ) then
		chatGroup = ChatTypeGroup[chatGroup];
	else
		chatGroup = ChatTypeGroup[UIDROPDOWNMENU_MENU_VALUE];
	end
	if ( chatGroup ) then
		local info = UIDropDownMenu_CreateInfo();
		local chatTypeInfo
		for index, value in pairs(chatGroup) do
			chatTypeInfo = ChatTypeInfo[FCF_StripChatMsg(value)];
			if ( chatTypeInfo ) then
				info.text = _G[value];
				info.value = FCF_StripChatMsg(value);
				-- Disable the button and color the text white
				info.notClickable = 1;
				-- Set to be notcheckable
				info.notCheckable = 1;
				-- Set the function to be called when a color is set
				info.swatchFunc = FCF_SetChatTypeColor;
				-- Set the swatch color info
				info.hasColorSwatch = 1;
				info.r = chatTypeInfo.r;
				info.g = chatTypeInfo.g;
				info.b = chatTypeInfo.b;
				-- Set function called when cancel is clicked in the colorpicker
				info.cancelFunc = FCF_CancelFontColorSettings;
				UIDropDownMenu_AddButton(info, UIDROPDOWNMENU_MENU_LEVEL);
			end
		end
	end
end

function FCFMessageTypeDropDown_OnClick(self)
	if ( UIDropDownMenuButton_GetChecked() ) then
		ChatFrame_RemoveMessageGroup(FCF_GetCurrentChatFrame(), self.value);
	else
		ChatFrame_AddMessageGroup(FCF_GetCurrentChatFrame(), self.value);
	end
end

function FCF_OpenNewWindow(name)
	local count = 1;
	local chatFrame, chatTab;
	
	for i=1, NUM_CHAT_WINDOWS do
		local _, _, _, _, _, _, shown = FCF_GetChatWindowInfo(i);
		chatFrame = _G["ChatFrame"..i];
		chatTab = _G["ChatFrame"..i.."Tab"];
		if ( (not shown and not chatFrame.isDocked) or (count == NUM_CHAT_WINDOWS) ) then
			if ( not name or name == "" ) then
				name = format(CHAT_NAME_TEMPLATE, i);			
			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);

			-- clear stale messages
			chatFrame:Clear();

			-- Listen to the standard messages
			ChatFrame_RemoveAllMessageGroups(chatFrame);
			ChatFrame_RemoveAllChannels(chatFrame);
			ChatFrame_ReceiveAllPrivateMessages(chatFrame);
			ChatFrame_ReceiveAllBNConversations(chatFrame);
			
			ChatFrame_AddMessageGroup(chatFrame, "SAY");
			ChatFrame_AddMessageGroup(chatFrame, "YELL");
			ChatFrame_AddMessageGroup(chatFrame, "GUILD");
			ChatFrame_AddMessageGroup(chatFrame, "WHISPER");
			ChatFrame_AddMessageGroup(chatFrame, "BN_WHISPER");
			ChatFrame_AddMessageGroup(chatFrame, "PARTY");
			ChatFrame_AddMessageGroup(chatFrame, "PARTY_LEADER");
			ChatFrame_AddMessageGroup(chatFrame, "CHANNEL");

			--Clear the edit box history.
			chatFrame.editBox:ClearHistory();
			
			-- Show the frame and tab
			chatFrame:Show();
			chatTab:Show();
			SetChatWindowShown(i, 1);
			
			-- Dock the frame by default
			FCF_DockFrame(chatFrame, (#FCFDock_GetChatFrames(GENERAL_CHAT_DOCK)+1), true);
			FCF_FadeInChatFrame(FCFDock_GetSelectedWindow(GENERAL_CHAT_DOCK));
			return chatFrame;
		end
		count = count + 1;
	end
end

function FCF_SetTemporaryWindowType(chatFrame, chatType, chatTarget)
	local chatTab = _G[chatFrame:GetName().."Tab"];
	--If the frame was already registered, unregister it.
	if ( chatFrame.isRegistered ) then
		FCFManager_UnregisterDedicatedFrame(chatFrame, chatFrame.chatType, chatFrame.chatTarget);
		chatFrame.isRegistered = false;
	end
	
	--Set the title text
	local name;
	if ( chatType == "WHISPER" or chatType == "BN_WHISPER" ) then
		name = chatTarget;
	elseif ( chatType == "BN_CONVERSATION" ) then
		name = format(CONVERSATION_NAME, tonumber(chatTarget) + MAX_WOW_CHAT_CHANNELS);
	elseif ( chatType == "PET_BATTLE_COMBAT_LOG" ) then
		name = PET_BATTLE_COMBAT_LOG;
	end
	FCF_SetWindowName(chatFrame, name);
	
	
	--Set up the window to receive the message types we want.
	chatFrame.chatType = chatType;
	chatFrame.chatTarget = chatTarget;
	
	ChatFrame_RemoveAllMessageGroups(chatFrame);
	ChatFrame_RemoveAllChannels(chatFrame);
	ChatFrame_ReceiveAllPrivateMessages(chatFrame);
	ChatFrame_ReceiveAllBNConversations(chatFrame);
	
	ChatFrame_AddMessageGroup(chatFrame, chatType);
	
	-- This is to display "friend is online"/"friend is offline" messages
	if ( chatType == "BN_WHISPER" ) then
		ChatFrame_AddSingleMessageType(chatFrame, "CHAT_MSG_BN_INLINE_TOAST_ALERT");
		ChatFrame_AddSingleMessageType(chatFrame, "CHAT_MSG_BN_WHISPER_PLAYER_OFFLINE");
	elseif ( chatType == "WHISPER" ) then
		ChatFrame_AddSingleMessageType(chatFrame, "CHAT_MSG_SYSTEM");
	elseif ( chatType == "PET_BATTLE_COMBAT_LOG" ) then
		ChatFrame_AddMessageGroup(chatFrame, "PET_BATTLE_INFO");
	end
	
	chatFrame.editBox:SetAttribute("chatType", chatType);
	chatFrame.editBox:SetAttribute("stickyType", chatType);
	
	if ( chatType == "WHISPER" or chatType == "BN_WHISPER" ) then
		chatFrame.editBox:SetAttribute("tellTarget", chatTarget);
		ChatFrame_AddPrivateMessageTarget(chatFrame, chatTarget);
	elseif ( chatType == "BN_CONVERSATION" ) then
		chatFrame.editBox:SetAttribute("channelTarget", chatTarget);
		ChatFrame_AddBNConversationTarget(chatFrame, chatTarget);
	elseif ( chatType == "PET_BATTLE_COMBAT_LOG" ) then
		chatFrame.editBox:SetAttribute("chatType", "SAY");
		chatFrame.editBox:SetAttribute("stickyType", "SAY");
	end
	
	-- Set up the colors
	local info = ChatTypeInfo[chatType];
	chatTab.selectedColorTable = { r = info.r, g = info.g, b = info.b };
	FCFTab_UpdateColors(chatTab, not chatFrame.isDocked or chatFrame == FCFDock_GetSelectedWindow(GENERAL_CHAT_DOCK));
	
	--If it's a conversation, create the conversation button
	if ( chatType == "BN_CONVERSATION" or chatType == "BN_WHISPER" ) then
		if ( chatFrame.conversationButton ) then
			BNConversationButton_UpdateTarget(chatFrame.conversationButton);
			chatFrame.conversationButton:Show();
		else
			CreateFrame("Button", chatFrame:GetName().."ConversationButton", chatFrame.buttonFrame, "BNConversationRosterButtonTemplate", chatFrame:GetID());
		end
		if ( chatFrame:GetHeight() < CHAT_FRAME_BIGGER_MIN_HEIGHT ) then
			chatFrame:SetHeight(CHAT_FRAME_BIGGER_MIN_HEIGHT);
		end
		chatFrame:SetMinResize(CHAT_FRAME_MIN_WIDTH, CHAT_FRAME_BIGGER_MIN_HEIGHT);
	else
		if ( chatFrame.conversationButton ) then
			chatFrame.conversationButton:Hide();
		end
		chatFrame:SetMinResize(CHAT_FRAME_MIN_WIDTH, CHAT_FRAME_NORMAL_MIN_HEIGHT);
	end
	
	--If it's a conversation, get it ready to convert to a whisper if needed.
	if ( chatType == "BN_CONVERSATION" ) then
		chatFrame:RegisterEvent("BN_CHAT_CHANNEL_CLOSED");
	else
		chatFrame:UnregisterEvent("BN_CHAT_CHANNEL_CLOSED");
	end
	
	--Set the icon
	local conversationIcon;
	if ( chatType == "WHISPER" or chatType == "BN_WHISPER" ) then
		conversationIcon = "Interface\\ChatFrame\\UI-ChatWhisperIcon";
	elseif ( chatType == "PET_BATTLE_COMBAT_LOG" ) then
		conversationIcon = "Interface\\Icons\\Tracking_WildPet";
	else
		conversationIcon = "Interface\\ChatFrame\\UI-ChatConversationIcon";
	end
	
	chatTab.conversationIcon:SetTexture(conversationIcon);
	if ( chatFrame.minFrame ) then
		chatFrame.minFrame.conversationIcon:SetTexture(conversationIcon);
	end
	
	--Register this frame
	FCFManager_RegisterDedicatedFrame(chatFrame, chatType, chatTarget);
	chatFrame.isRegistered = true;
	
	--The window name may have been updated, so update the dock and tabs.
	FCF_DockUpdate();
end

local maxTempIndex = NUM_CHAT_WINDOWS + 1;
function FCF_OpenTemporaryWindow(chatType, chatTarget, sourceChatFrame, selectWindow)
	local chatFrame, chatTab, conversationIcon;
	for _, chatFrameName in pairs(CHAT_FRAMES) do
		local frame = _G[chatFrameName];
		if ( frame.isTemporary ) then
			if ( not frame.inUse and not frame.isDocked ) then
				chatFrame = frame;
				chatTab = _G[chatFrame:GetName().."Tab"];
				break;
			end
		end
	end
	
	if ( not chatFrame ) then
		chatTab = CreateFrame("Button", "ChatFrame"..maxTempIndex.."Tab", UIParent, "ChatTabTemplate", maxTempIndex);
		
		conversationIcon = chatTab:CreateTexture(chatTab:GetName().."ConversationIcon", "ARTWORK", "ChatTabConversationIconTemplate");
		conversationIcon:ClearAllPoints();
		conversationIcon:SetPoint("RIGHT", chatTab:GetFontString(), "LEFT", 0, -2);
		chatTab.conversationIcon = conversationIcon;
		
		local tabText = _G[chatTab:GetName().."Text"];
		tabText:SetPoint("LEFT", chatTab.leftTexture, "RIGHT", 10, -6);
		tabText:SetJustifyH("LEFT");
		chatTab.sizePadding = 10;
		
		chatFrame = CreateFrame("ScrollingMessageFrame", "ChatFrame"..maxTempIndex, UIParent, "FloatingChatFrameTemplate", maxTempIndex);
		
		if ( GetCVarBool("chatMouseScroll") ) then
			chatFrame:SetScript("OnMouseWheel", FloatingChatFrame_OnMouseScroll);
			chatFrame:EnableMouseWheel(true);
		end

		maxTempIndex = maxTempIndex + 1;		
	end
	
	--Copy chat settings from the source frame.
	FCF_CopyChatSettings(chatFrame, sourceChatFrame or DEFAULT_CHAT_FRAME);

	-- clear stale messages
	chatFrame:Clear();
	chatFrame.inUse = true;
	chatFrame.isTemporary = true;
	
	FCF_SetTemporaryWindowType(chatFrame, chatType, chatTarget);
	
	--Clear the edit box history.
	chatFrame.editBox:ClearHistory();
	
	if ( sourceChatFrame ) then
		--Copy over messages
		local accessID = ChatHistory_GetAccessID(chatType, chatTarget);
		for i = 1, sourceChatFrame:GetNumMessages(accessID) do
			local text, accessID, lineID, extraData = sourceChatFrame:GetMessageInfo(i, accessID);
			local cType, cTarget = ChatHistory_GetChatType(extraData);

			local info = ChatTypeInfo[cType];
			chatFrame:AddMessage(text, info.r, info.g, info.b, lineID, false, accessID, extraData);
		end

		--Stop displaying this type of chat in the old chat frame.
		--Remove the messages from the old frame.
		if (not (chatType == "WHISPER" and GetCVar("whisperMode") == "popout_and_inline")
			and not (chatType == "BN_WHISPER" and GetCVar("bnWhisperMode") == "popout_and_inline")
			and not (chatType == "BN_CONVERSATION" and GetCVar("conversationMode") == "popout_and_inline") ) then

			if ( chatType == "WHISPER" or chatType == "BN_WHISPER" ) then
				ChatFrame_ExcludePrivateMessageTarget(sourceChatFrame, chatTarget);
			elseif ( chatType == "BN_CONVERSATION" ) then
				ChatFrame_ExcludeBNConversationTarget(sourceChatFrame, chatTarget);
			end

			sourceChatFrame:RemoveMessagesByAccessID(accessID);
		end
	end
	
	--Close the Editbox
	ChatEdit_DeactivateChat(chatFrame.editBox);
	
	-- Show the frame and tab
	chatFrame:Show();
	chatTab:Show();
	
	-- Dock the frame by default
	FCF_DockFrame(chatFrame, (#FCFDock_GetChatFrames(GENERAL_CHAT_DOCK)+1), selectWindow);
	return chatFrame;
end

function FCF_RemoveAllMessagesFromChanSender(chatFrame, chanSender)
	local ids = ChatHistory_GetAllAccessIDsByChanSender(chanSender);
	for i=1, #ids do
		chatFrame:RemoveMessagesByExtraData(ids[i]);
	end
end

function FCF_GetNumActiveChatFrames()
	local count = 0;
	local chatFrame;
	for i=1, NUM_CHAT_WINDOWS do
		local _, _, _, _, _, _, shown = FCF_GetChatWindowInfo(i);
		chatFrame = _G["ChatFrame"..i];
		if ( chatFrame ) then
			if ( shown or chatFrame.isDocked ) then
				count = count + 1;
			end
		end
	end
	return count;
end

function FCF_RenameChatWindow_Popup()
	local dialog = StaticPopup_Show("NAME_CHAT");
	dialog.data = FCF_GetCurrentChatFrameID();
end

function FCF_NewChatWindow()
	StaticPopup_Show("NAME_CHAT");
end

function FCF_ResetAllWindows()
	StaticPopup_Show("RESET_CHAT");
end

--[[function FCF_ChatChannels()
	ToggleFriendsFrame(3);
end]]--

function FCF_SetWindowName(frame, name, doNotSave)
	if ( not name or name == "") then
		-- Hack to initialize the chat window names, since globalstrings are not available on init
		if ( frame:GetID() == 1 ) then
			name = GENERAL;
			doNotSave = nil;
		elseif ( frame:GetID() == 2 ) then
			name = COMBAT_LOG;
			doNotSave = nil;
		else
			name = format(CHAT_NAME_TEMPLATE, frame:GetID());
		end
	else
		FCFDock_SetDirty(GENERAL_CHAT_DOCK);
	end
	frame.name = name;
	local tab = _G[frame:GetName().."Tab"];
	tab:SetText(name);
	PanelTemplates_TabResize(tab, tab.sizePadding or 0);
	-- Save this off so we know how big the tab should always be, even if it gets shrunken on the dock.
	tab.textWidth = _G[tab:GetName().."Text"]:GetWidth();
	if ( not doNotSave ) then
		SetChatWindowName(frame:GetID(), name);
	end
	if ( frame.minFrame ) then
		frame.minFrame:SetText(name);
	end
end

function FCF_SetWindowColor(frame, r, g, b, doNotSave)
	local name = frame:GetName();
	for index, value in pairs(CHAT_FRAME_TEXTURES) do
		--NOTE - If this is changed, please change the equivalent code in GMChatFrame_OnLoad.
		local object = _G[name..value];
		local objectType = object:GetObjectType();
		if ( objectType == "Button" ) then
			object:GetNormalTexture():SetVertexColor(r, g, b);
			object:GetHighlightTexture():SetVertexColor(r, g, b);
			object:GetPushedTexture():SetVertexColor(r, g, b);
		elseif ( objectType == "Texture" ) then
			_G[name..value]:SetVertexColor(r,g,b);
		else
			--error("Unhandled frame type...");
		end
	end
	if ( not doNotSave ) then
		SetChatWindowColor(frame:GetID(), r, g, b);
	end
end

function FCF_SetWindowAlpha(frame, alpha, doNotSave)
	local name = frame:GetName();
	for index, value in pairs(CHAT_FRAME_TEXTURES) do
		_G[name..value]:SetAlpha(alpha);
	end
	if ( not doNotSave ) then
		SetChatWindowAlpha(frame:GetID(), alpha);
	end
	-- Remember the alpha
	frame.oldAlpha = alpha;
end

function FCF_GetCurrentChatFrameID()
	return CURRENT_CHAT_FRAME_ID;
end

function FCF_GetCurrentChatFrame(child)
	local currentChatFrame = nil;
	if ( CURRENT_CHAT_FRAME_ID ) then
		currentChatFrame = _G["ChatFrame"..CURRENT_CHAT_FRAME_ID];
	end
	if ( not currentChatFrame and child ) then
		currentChatFrame = _G["ChatFrame"..child:GetParent():GetID()];
	end
	return currentChatFrame;
end

function FCF_SetChatTypeColor()
	local r,g,b = ColorPickerFrame:GetColorRGB();
	ChangeChatColor(UIDROPDOWNMENU_MENU_VALUE, r, g, b);
end

function FCF_SetChatWindowBackGroundColor()
	local r,g,b = ColorPickerFrame:GetColorRGB();
	FCF_SetWindowColor(FCF_GetCurrentChatFrame(), r, g, b)
	SetChatWindowColor(FCF_GetCurrentChatFrameID(), r, g, b);
end

function FCF_SetChatWindowOpacity()
	local alpha = 1.0 - OpacitySliderFrame:GetValue();
	FCF_SetWindowAlpha(FCF_GetCurrentChatFrame(), alpha);
end

function FCF_SetChatWindowFontSize(self, chatFrame, fontSize)
	if ( not chatFrame ) then
		chatFrame = FCF_GetCurrentChatFrame();
	end
	if ( not fontSize ) then
		fontSize = self.value;
	end
	local fontFile, unused, fontFlags = chatFrame:GetFont();
	chatFrame:SetFont(fontFile, fontSize, fontFlags);
	if ( GMChatFrame and chatFrame == DEFAULT_CHAT_FRAME ) then
		GMChatFrame:SetFont(fontFile, fontSize, fontFlags);
	end
	SetChatWindowSize(chatFrame:GetID(), fontSize);
end

function FCF_CancelFontColorSettings(previousValues)
	if ( previousValues.r ) then
		ChangeChatColor(UIDROPDOWNMENU_MENU_VALUE, previousValues.r, previousValues.g, previousValues.b);
	end
end

function FCF_CancelWindowColorSettings(previousValues)
	if ( previousValues.r ) then
		FCF_SetWindowColor(FCF_GetCurrentChatFrame(), previousValues.r, previousValues.g, previousValues.b)
		SetChatWindowColor(FCF_GetCurrentChatFrameID(), previousValues.r, previousValues.g, previousValues.b);
	end
	if ( previousValues.opacity ) then
		FCF_SetWindowAlpha(FCF_GetCurrentChatFrame(), 1 - previousValues.opacity);
	end
end

function FCF_StripChatMsg(string)
	if ( strsub(string,1,8) == "CHAT_MSG" ) then
		return strsub(string,10);
	else
		return string;
	end
end

function FCF_ToggleLock()
	local chatFrame = FCF_GetCurrentChatFrame();
	if ( chatFrame.isLocked ) then
		-- If unlocking a docked frame then undock it and center it on the screen
		if ( chatFrame.isDocked and chatFrame ~= DEFAULT_CHAT_FRAME ) then
			FCF_UnDockFrame(chatFrame);
			chatFrame:ClearAllPoints();
			chatFrame:SetPoint("CENTER", "UIParent", "CENTER", 0, 0);
			FCF_SetTabPosition(chatFrame, 0);
			chatFrame:Show();
		end
		FCF_SetLocked(chatFrame, nil);
	else
		FCF_SetLocked(chatFrame, 1);
	end
end

function FCF_SetLocked(chatFrame, isLocked)
	chatFrame.isLocked = isLocked;
	if ( chatFrame.isUninteractable or isLocked ) then
		chatFrame.resizeButton:Hide();
	else
		chatFrame.resizeButton:Show();
		--chatFrame.resizeButton:SetAlpha(_G[chatFrame:GetName().."Background"]:GetAlpha());
	end
	SetChatWindowLocked(chatFrame:GetID(), isLocked);
end

function FCF_ToggleUninteractable()
	local chatFrame = FCF_GetCurrentChatFrame();
	if ( chatFrame.isUninteractable ) then
		FCF_SetExpandedUninteractable(chatFrame, false)
	else
		FCF_SetExpandedUninteractable(chatFrame, true)
	end
end

function FCF_SetExpandedUninteractable(chatFrame, isUninteractable)
	if ( chatFrame.isDocked ) then
		for _, frame in pairs(GENERAL_CHAT_DOCK.DOCKED_CHAT_FRAMES) do
			FCF_SetUninteractable(frame, isUninteractable);
		end
	else
		FCF_SetUninteractable(chatFrame, isUninteractable);
	end
end

function FCF_SetUninteractable(chatFrame, isUninteractable)	--No, uninteractable is not really a word.
	chatFrame.isUninteractable = isUninteractable;
	SetChatWindowUninteractable(chatFrame:GetID(), isUninteractable);
	if ( not chatFrame.overrideHyperlinksEnabled ) then
		chatFrame:SetHyperlinksEnabled(not isUninteractable);
	end
	local chatFrameName = chatFrame:GetName();
	if ( isUninteractable or chatFrame.isLocked ) then
		_G[chatFrameName.."ResizeButton"]:Hide();
	else
		_G[chatFrameName.."ResizeButton"]:Show();
	end
end

function FCF_FadeInChatFrame(chatFrame)
	local frameName = chatFrame:GetName();
	chatFrame.hasBeenFaded = true;
	for index, value in pairs(CHAT_FRAME_TEXTURES) do
		local object = _G[frameName..value];
		if ( object:IsShown() ) then
			UIFrameFadeIn(object, CHAT_FRAME_FADE_TIME, object:GetAlpha(), max(chatFrame.oldAlpha, DEFAULT_CHATFRAME_ALPHA));
		end
	end
	if ( chatFrame == FCFDock_GetSelectedWindow(GENERAL_CHAT_DOCK) ) then
		for _, frame in pairs(FCFDock_GetChatFrames(GENERAL_CHAT_DOCK)) do
			if ( frame ~= chatFrame ) then
				FCF_FadeInChatFrame(frame);
			end
		end
		if ( GENERAL_CHAT_DOCK.overflowButton:IsShown() ) then
			UIFrameFadeIn(GENERAL_CHAT_DOCK.overflowButton, CHAT_FRAME_FADE_TIME, GENERAL_CHAT_DOCK.overflowButton:GetAlpha(), CHAT_FRAME_TAB_SELECTED_MOUSEOVER_ALPHA);
		end
	end
	
	local chatTab = _G[frameName.."Tab"];
	UIFrameFadeIn(chatTab, CHAT_FRAME_FADE_TIME, chatTab:GetAlpha(), chatTab.mouseOverAlpha);
	
	--Fade in the button frame
	if ( not chatFrame.isDocked ) then
		UIFrameFadeIn(chatFrame.buttonFrame, CHAT_FRAME_FADE_TIME, chatFrame.buttonFrame:GetAlpha(), 1);
	end
end

function FCF_FadeOutChatFrame(chatFrame)
	local frameName = chatFrame:GetName();
	chatFrame.hasBeenFaded = nil;
	for index, value in pairs(CHAT_FRAME_TEXTURES) do
		-- Fade out chat frame
		local object = _G[frameName..value];
		if ( object:IsShown() ) then
			UIFrameFadeOut(object, CHAT_FRAME_FADE_OUT_TIME, max(object:GetAlpha(), chatFrame.oldAlpha), chatFrame.oldAlpha);
		end
	end
	if ( chatFrame == FCFDock_GetSelectedWindow(GENERAL_CHAT_DOCK) ) then
		for _, frame in pairs(FCFDock_GetChatFrames(GENERAL_CHAT_DOCK)) do
			if ( frame ~= chatFrame ) then
				FCF_FadeOutChatFrame(frame);
			end
		end
		if ( GENERAL_CHAT_DOCK.overflowButton:IsShown() ) then
			UIFrameFadeOut(GENERAL_CHAT_DOCK.overflowButton, CHAT_FRAME_FADE_OUT_TIME, GENERAL_CHAT_DOCK.overflowButton:GetAlpha(), CHAT_FRAME_TAB_SELECTED_NOMOUSE_ALPHA);
		end
	end
	
	local chatTab = _G[frameName.."Tab"];
	UIFrameFadeOut(chatTab, CHAT_FRAME_FADE_OUT_TIME, chatTab:GetAlpha(), chatTab.noMouseAlpha);
	
	--Fade out the ButtonFrame
	if ( not chatFrame.isDocked ) then
		UIFrameFadeOut(chatFrame.buttonFrame, CHAT_FRAME_FADE_OUT_TIME, chatFrame.buttonFrame:GetAlpha(), CHAT_FRAME_BUTTON_FRAME_MIN_ALPHA);
	end
end
	
local LAST_CURSOR_X, LAST_CURSOR_Y;
function FCF_OnUpdate(elapsed)
	local cursorX, cursorY = GetCursorPosition();
	
	local overSomething = false;
	for _, frameName in pairs(CHAT_FRAMES) do
		local chatFrame = _G[frameName];
		if ( chatFrame:IsShown() ) then
			local topOffset = 28;
			if ( IsCombatLog(chatFrame) ) then
				topOffset = topOffset + CombatLogQuickButtonFrame_Custom:GetHeight();
			end
			--Items that will always cause the frame to fade in.
			if ( MOVING_CHATFRAME or chatFrame.resizeButton:GetButtonState() == "PUSHED" or (chatFrame.isDocked and GENERAL_CHAT_DOCK.overflowButton.list:IsShown())) then	
				overSomething = true;
				chatFrame.mouseOutTime = 0;
				if ( not chatFrame.hasBeenFaded ) then
					overSomething = true;
					FCF_FadeInChatFrame(chatFrame);
				end
			--Things that will cause the frame to fade in if the mouse is stationary.
			elseif ( chatFrame:IsMouseOver(topOffset, -2, -2, 2) or	--This should be slightly larger than the hit rect insets to give us some wiggle room.
				(chatFrame.isDocked and FriendsMicroButton:IsMouseOver()) or
				(chatFrame.buttonFrame:IsMouseOver())) then
				overSomething = true;
				chatFrame.mouseOutTime = 0;
				if ( cursorX == LAST_CURSOR_X and cursorY == LAST_CURSOR_Y and not chatFrame.hasBeenFaded ) then
					chatFrame.mouseInTime = (chatFrame.mouseInTime or 0) + elapsed;
					if ( chatFrame.mouseInTime > CHAT_TAB_SHOW_DELAY ) then
						FCF_FadeInChatFrame(chatFrame);
					end
				else
					chatFrame.mouseInTime = 0;
				end
			elseif ( chatFrame:IsShown() and chatFrame.hasBeenFaded ) then
				chatFrame.mouseInTime = 0;
				chatFrame.mouseOutTime = (chatFrame.mouseOutTime or 0) + elapsed;
				if ( chatFrame.mouseOutTime > CHAT_TAB_HIDE_DELAY ) then
					FCF_FadeOutChatFrame(chatFrame);
				end
			end
		end
	end
	
	LAST_CURSOR_X, LAST_CURSOR_Y = cursorX, cursorY;
end

function FCF_SavePositionAndDimensions(chatFrame)
	local centerX = chatFrame:GetLeft() + chatFrame:GetWidth() / 2;
	local centerY = chatFrame:GetBottom() + chatFrame:GetHeight() / 2;
	
	local horizPoint, vertPoint;
	local screenWidth, screenHeight = GetScreenWidth(), GetScreenHeight();
	local xOffset, yOffset;
	if ( centerX > screenWidth / 2 ) then
		horizPoint = "RIGHT";
		xOffset = (chatFrame:GetRight() - screenWidth)/screenWidth;
	else
		horizPoint = "LEFT";
		xOffset = chatFrame:GetLeft()/screenWidth;
	end
	
	if ( centerY > screenHeight / 2 ) then
		vertPoint = "TOP";
		yOffset = (chatFrame:GetTop() - screenHeight)/screenHeight;
	else
		vertPoint = "BOTTOM";
		yOffset = chatFrame:GetBottom()/screenHeight;
	end
	
	SetChatWindowSavedPosition(chatFrame:GetID(), vertPoint..horizPoint, xOffset, yOffset);
	SetChatWindowSavedDimensions(chatFrame:GetID(), chatFrame:GetWidth(), chatFrame:GetHeight());
end

function FCF_RestorePositionAndDimensions(chatFrame)
	local width, height = GetChatWindowSavedDimensions(chatFrame:GetID());
	if ( width and height ) then
		chatFrame:SetSize(width, height);
	end
	
	local point, xOffset, yOffset = GetChatWindowSavedPosition(chatFrame:GetID());
	if ( point ) then
		chatFrame:ClearAllPoints();
		chatFrame:SetPoint(point, xOffset * GetScreenWidth(), yOffset * GetScreenHeight());
		chatFrame:SetUserPlaced(true);
	else
		chatFrame:SetUserPlaced(false);
	end
end

-- Docking handling functions
function FCF_StopDragging(chatFrame)
	chatFrame:StopMovingOrSizing();

	_G[chatFrame:GetName().."Tab"]:UnlockHighlight();
	
	FCFDock_HideInsertHighlight(GENERAL_CHAT_DOCK);
	
	if ( GENERAL_CHAT_DOCK:IsMouseOver(10, -10, 0, 10) ) then
		local mouseX, mouseY = GetCursorPosition();
		mouseX, mouseY = mouseX / UIParent:GetScale(), mouseY / UIParent:GetScale();
		FCF_DockFrame(chatFrame, FCFDock_GetInsertIndex(GENERAL_CHAT_DOCK, chatFrame, mouseX, mouseY), true);
	else
		FCF_SetTabPosition(chatFrame, 0);
	end
	
	FCF_SavePositionAndDimensions(chatFrame);

	MOVING_CHATFRAME = nil;
end

function FCFTab_OnUpdate(self, elapsed)
	local cursorX, cursorY = GetCursorPosition();
	cursorX, cursorY = cursorX / UIParent:GetScale(), cursorY / UIParent:GetScale();
	local chatFrame = _G["ChatFrame"..self:GetID()];
	if ( chatFrame ~= GENERAL_CHAT_DOCK.primary and GENERAL_CHAT_DOCK:IsMouseOver(10, -10, 0, 10) ) then
		FCFDock_PlaceInsertHighlight(GENERAL_CHAT_DOCK, chatFrame, cursorX, cursorY);
	else
		FCFDock_HideInsertHighlight(GENERAL_CHAT_DOCK);
	end
	
	FCF_UpdateButtonSide(chatFrame);
	if ( chatFrame == GENERAL_CHAT_DOCK.primary ) then
		for _, frame in pairs(FCFDock_GetChatFrames(GENERAL_CHAT_DOCK)) do
			FCF_SetButtonSide(frame, FCF_GetButtonSide(GENERAL_CHAT_DOCK.primary));
		end
	end
	
	if ( not IsMouseButtonDown(self.dragButton) ) then
		FCFTab_OnDragStop(self, self.dragButton);
		self.dragButton = nil;
		self:SetScript("OnUpdate", nil);
	end

	if ( BNToastFrame and BNToastFrame:IsShown() ) then
		BNToastFrame_UpdateAnchor();
	end	
end

function FCFTab_OnDragStop(self, button)
	FCF_StopDragging(_G["ChatFrame"..self:GetID()]);
end

DEFAULT_TAB_SELECTED_COLOR_TABLE = { r = 1, g = 0.5, b = 0.25 };

function FCFTab_UpdateColors(self, selected)
	if ( selected ) then
		self.leftSelectedTexture:Show();
		self.middleSelectedTexture:Show();
		self.rightSelectedTexture:Show();
	else
		self.leftSelectedTexture:Hide();
		self.middleSelectedTexture:Hide();
		self.rightSelectedTexture:Hide();
	end
	
	local colorTable = self.selectedColorTable or DEFAULT_TAB_SELECTED_COLOR_TABLE;
	
	if ( self.selectedColorTable ) then
		self:GetFontString():SetTextColor(colorTable.r, colorTable.g, colorTable.b);
	else
		self:GetFontString():SetTextColor(NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b);
	end
	
	self.leftSelectedTexture:SetVertexColor(colorTable.r, colorTable.g, colorTable.b);
	self.middleSelectedTexture:SetVertexColor(colorTable.r, colorTable.g, colorTable.b);
	self.rightSelectedTexture:SetVertexColor(colorTable.r, colorTable.g, colorTable.b);
	
	self.leftHighlightTexture:SetVertexColor(colorTable.r, colorTable.g, colorTable.b);
	self.middleHighlightTexture:SetVertexColor(colorTable.r, colorTable.g, colorTable.b);
	self.rightHighlightTexture:SetVertexColor(colorTable.r, colorTable.g, colorTable.b);
	self.glow:SetVertexColor(colorTable.r, colorTable.g, colorTable.b);
	
	if ( self.conversationIcon ) then
		self.conversationIcon:SetVertexColor(colorTable.r, colorTable.g, colorTable.b);
	end
	
	local minimizedFrame = _G["ChatFrame"..self:GetID().."Minimized"];
	if ( minimizedFrame ) then
		minimizedFrame.selectedColorTable = self.selectedColorTable;
		FCFMin_UpdateColors(minimizedFrame);
	end
end

function FCFTab_UpdateAlpha(chatFrame)
	local chatTab = _G[chatFrame:GetName().."Tab"];
	if ( not chatFrame.isDocked or chatFrame == FCFDock_GetSelectedWindow(GENERAL_CHAT_DOCK) ) then
		chatTab.mouseOverAlpha = CHAT_FRAME_TAB_SELECTED_MOUSEOVER_ALPHA;
		chatTab.noMouseAlpha = CHAT_FRAME_TAB_SELECTED_NOMOUSE_ALPHA;
	else
		if ( chatTab.alerting ) then
			chatTab.mouseOverAlpha = CHAT_FRAME_TAB_ALERTING_MOUSEOVER_ALPHA;
			chatTab.noMouseAlpha = CHAT_FRAME_TAB_ALERTING_NOMOUSE_ALPHA;
		else
			chatTab.mouseOverAlpha = CHAT_FRAME_TAB_NORMAL_MOUSEOVER_ALPHA;
			chatTab.noMouseAlpha = CHAT_FRAME_TAB_NORMAL_NOMOUSE_ALPHA;
		end
	end
	
	-- If this is in the middle of fading, stop it, since we're about to set the alpha
	UIFrameFadeRemoveFrame(chatTab);
	
	if ( chatFrame.hasBeenFaded ) then
		chatTab:SetAlpha(chatTab.mouseOverAlpha);
	else
		chatTab:SetAlpha(chatTab.noMouseAlpha);
	end
end

function FCF_HideOnFadeFinished(frame)
	frame:Hide();
end

function FCF_IsValidChatFrame(chatFrame)
	-- Break out all the cases individually because the logic gets convoluted
	if ( chatFrame == MOVING_CHATFRAME ) then
		return nil;
	end

	if ( not chatFrame:IsShown() and not chatFrame.isDocked ) then
		return nil;
	end
	
	return 1;
end

function FCF_UpdateButtonSide(chatFrame)
	local leftDist =  chatFrame:GetLeft();
	local rightDist = GetScreenWidth() - chatFrame:GetRight();
	local changed = nil;
	if (( leftDist > 0 and leftDist <= rightDist ) or rightDist < 0 ) then
		if ( chatFrame.buttonSide ~= "left" ) then
			FCF_SetButtonSide(chatFrame, "left");
			changed = 1;
		end
	else
		if ( chatFrame.buttonSide ~= "right" or leftDist < 0 ) then
			FCF_SetButtonSide(chatFrame, "right");
			changed = 1;
		end
	end
	return changed;
end

function FCF_SetButtonSide(chatFrame, buttonSide, forceUpdate)
	if ( not forceUpdate and chatFrame.buttonSide == buttonSide  ) then
		return;
	end
	chatFrame.buttonFrame:ClearAllPoints();
	
	local topY = 0;
	if ( IsCombatLog(chatFrame) ) then
		topY = topY + CombatLogQuickButtonFrame_Custom:GetHeight();
	end

	if ( buttonSide == "left" ) then
		chatFrame.buttonFrame:SetPoint("TOPRIGHT", chatFrame, "TOPLEFT", -4, topY);
		chatFrame.buttonFrame:SetPoint("BOTTOMRIGHT", chatFrame, "BOTTOMLEFT", -4, 0);
	elseif ( buttonSide == "right" ) then
		chatFrame.buttonFrame:SetPoint("TOPLEFT", chatFrame, "TOPRIGHT", 4, topY);
		chatFrame.buttonFrame:SetPoint("BOTTOMLEFT", chatFrame, "BOTTOMRIGHT", 4, 0);
	end
	chatFrame.buttonSide = buttonSide;
	
	if ( chatFrame == DEFAULT_CHAT_FRAME ) then
		ChatFrameMenu_UpdateAnchorPoint();
	end
end

function FCF_StartAlertFlash(chatFrame)
	if ( chatFrame.minFrame ) then
		UIFrameFlash(chatFrame.minFrame.glow, 1.0, 1.0, -1, false, 0, 0, "chat");
		
		chatFrame.minFrame.alerting = true;
	end
	
	local chatTab = _G[chatFrame:GetName().."Tab"];
	UIFrameFlash(chatTab.glow, 1.0, 1.0, -1, false, 0, 0, "chat");
	
	chatTab.alerting = true;
	
	FCFTab_UpdateAlpha(chatFrame);
	
	FCFDockOverflowButton_UpdatePulseState(GENERAL_CHAT_DOCK.overflowButton);
end

function FCF_StopAlertFlash(chatFrame)
	if ( chatFrame.minFrame ) then
		UIFrameFlashStop(chatFrame.minFrame.glow);
		
		chatFrame.minFrame.alerting = false;
	end
	
	local chatTab = _G[chatFrame:GetName().."Tab"];
	UIFrameFlashStop(chatTab.glow);
	
	chatTab.alerting = false;
	
	FCFTab_UpdateAlpha(chatFrame);

	FCFDockOverflowButton_UpdatePulseState(GENERAL_CHAT_DOCK.overflowButton);
end

function FCF_GetButtonSide(chatFrame)
	return chatFrame.buttonSide;
end

function FCF_DockUpdate()
	FCFDock_UpdateTabs(GENERAL_CHAT_DOCK, true);
end
--[[
	local numDockedFrames = getn(DOCKED_CHAT_FRAMES);
	local dockRegion, chatTab, previousDockedFrame;
	local dockWidth = 0;
	local previousDockRegion;
	local name;
	for index, value in pairs(DOCKED_CHAT_FRAMES) do
		-- If not the initial chatframe then anchor the frame to the base chatframe
		name = value:GetName();
		if ( index ~= 1 ) then
			value:ClearAllPoints();
			value:SetPoint("TOPLEFT", DEFAULT_CHAT_FRAME, "TOPLEFT", 0, 0);
			value:SetPoint("BOTTOMLEFT", DEFAULT_CHAT_FRAME, "BOTTOMLEFT", 0, 0);
			value:SetPoint("BOTTOMRIGHT", DEFAULT_CHAT_FRAME, "BOTTOMRIGHT", 0, 0);
		end
		
		-- Select or deselect the frame
		chatTab = _G[value:GetName().."Tab"];
		-- chatTab.textWidth is the original width of the text name of the tab
		-- We need to use this as an absolute measure of the text's width is altered when the chat dock gets too small
		-- If the text is shrunken the original width is lost, unless we save it and use it in the following manner
		-- This is a fix for Bug ID: 71180
		PanelTemplates_TabResize(chatTab, 5, nil, nil, nil, chatTab.textWidth);
		if ( value == SELECTED_DOCK_FRAME ) then
			value:Show();
			if ( chatTab:IsShown() ) then
				chatTab:SetAlpha(1.0);
			end
		else
			value:Hide();
			if ( chatTab:IsShown() ) then
				chatTab:SetAlpha(0.5);
			end
		end
		
		-- If there was a frame before this frame then anchor the tab
		
		if ( previousDockedFrame ) then
			chatTab:ClearAllPoints();
			FCF_SetTabPosition(value, dockWidth);
			_G[previousDockedFrame:GetName().."TabDockRegion"]:SetPoint("RIGHT", value:GetName().."Tab", "CENTER", 0, 0);
		end

		-- If this is the last frame in the dock then extend the dockRegion, otherwise shrink it to the default width
		dockRegion = _G[chatTab:GetName().."DockRegion"];
		dockRegion:SetPoint("LEFT", chatTab, "CENTER", 0 , 0);
		if ( numDockedFrames == index ) then
			dockRegion:SetPoint("RIGHT", "ChatFrame"..chatTab:GetID(), "RIGHT", 0, 0);
		end
		dockRegion:Hide();
		
		-- Keep track of the width of the dock for anchoring purposes
		dockWidth = dockWidth + chatTab:GetWidth();
		previousDockedFrame = value;
	end
	
	-- Intelligently resize the chat tabs if dockwidth is greater than the window width
	if ( dockWidth > DEFAULT_CHAT_FRAME:GetWidth() ) then
		DOCK_COPY = {};
		-- Copy the array
		for index, value in pairs(DOCKED_CHAT_FRAMES) do
			DOCK_COPY[index] = DOCKED_CHAT_FRAMES[index];
		end
		sort(DOCK_COPY, FCF_TabCompare);
		local totalWidth = DEFAULT_CHAT_FRAME:GetWidth();
		local avgWidth = totalWidth / numDockedFrames;
		local chatTabWidth;
		-- Resize the tabs
		for index, value in pairs(DOCK_COPY) do
			chatTab = _G[value:GetName().."Tab"];
			chatTabWidth = chatTab:GetWidth();
			if ( chatTabWidth < avgWidth ) then
				-- If tab is smaller than the average then remove it from the list and recalc the average
				totalWidth = totalWidth - chatTabWidth;
				numDockedFrames = numDockedFrames - 1;
				avgWidth = totalWidth / numDockedFrames;
			else
				-- Set the tab to the average width
				PanelTemplates_TabResize(chatTab, 0, avgWidth);
			end
		end

		-- Reanchor the tabs
		previousDockedFrame = nil;
		dockWidth = 0;
		for index, value in pairs(DOCKED_CHAT_FRAMES) do
			-- If there was a frame before this frame then anchor the tab
			if ( previousDockedFrame ) then
				FCF_SetTabPosition(value, dockWidth);
			end
			chatTab = _G[value:GetName().."Tab"];
			dockWidth = dockWidth + chatTab:GetWidth();
			previousDockedFrame = value;
		end
	end
end]]

function FCF_TabCompare(chatFrame1, chatFrame2)
	local tab1 = _G[chatFrame1:GetName().."Tab"];
	local tab2 = _G[chatFrame2:GetName().."Tab"];
	return tab1:GetWidth() < tab2:GetWidth();
end

function FCF_DockFrame(frame, index, selected)
	-- Return if already docked
	if ( frame.isDocked ) then
		return;
	end

	FCFDock_AddChatFrame(GENERAL_CHAT_DOCK, frame, index);
	
	-- Save docked state
	FCF_SaveDock();
	if ( selected ) then
		--FCF_SelectDockFrame(frame);
		FCFDock_SelectWindow(GENERAL_CHAT_DOCK, frame);
	end

	-- Set scroll button side
	if ( frame == DEFAULT_CHAT_FRAME ) then
		FCF_UpdateButtonSide(frame);
	else
		FCF_SetButtonSide(frame, FCF_GetButtonSide(DEFAULT_CHAT_FRAME));
	end
	
	-- Lock frame
	FCF_SetLocked(frame, 1);
	
	--If the frame that is being docked and the frame it is docking to have different interactable settings, make them both interactable.
	if ( frame.isUninteractable ~= DEFAULT_CHAT_FRAME.isUninteractable ) then
		FCF_SetExpandedUninteractable(frame, false)
	end
	
	if ( frame == COMBATLOG ) then
		Blizzard_CombatLog_Update_QuickButtons();
	end
	
	FCF_DockUpdate();
end

function FCF_UnDockFrame(frame)
	if ( frame == DEFAULT_CHAT_FRAME or not frame.isDocked ) then
		return;
	end
	-- Undock frame regardless of whether its docked or not
	SetChatWindowDocked(frame:GetID(), nil);
	FCFDock_RemoveChatFrame(GENERAL_CHAT_DOCK, frame);

	FCF_SaveDock();
	
	-- Set tab to full alpha
	local chatTab = _G[frame:GetName().."Tab"];
	chatTab:SetAlpha(1.0);
end

function FCF_SelectDockFrame(frame)
	SELECTED_DOCK_FRAME = frame;
	-- Stop tab flashing
	local tabFlash;
	if ( frame ) then
		tabFlash = _G["ChatFrame"..frame:GetID().."TabFlash"];
	end
	
	if ( tabFlash ) then
		UIFrameFlashStop(tabFlash);
		tabFlash:Hide();
	end
	FCFDock_SelectWindow(GENERAL_CHAT_DOCK, frame);
	FCF_DockUpdate();
end

function FCF_Tab_OnClick(self, button)
	local chatFrame = _G["ChatFrame"..self:GetID()];
	-- If Rightclick bring up the options menu
	if ( button == "RightButton" ) then
		chatFrame:StopMovingOrSizing();
		CURRENT_CHAT_FRAME_ID = self:GetID();
		ToggleDropDownMenu(1, nil, _G[self:GetName().."DropDown"], self:GetName(), 0, 0);
		return;
	end
	
	if (button == "MiddleButton") then
		if ( chatFrame and (chatFrame ~= DEFAULT_CHAT_FRAME and not IsCombatLog(chatFrame)) ) then
			if ( not chatFrame.isTemporary ) then
				FCF_PopInWindow(self, chatFrame);
				return;
			elseif ( chatFrame.isTemporary and (chatFrame.chatType == "WHISPER" or chatFrame.chatType == "BN_WHISPER") ) then
				FCF_PopInWindow(self, chatFrame);
				return;
			elseif ( chatFrame.isTemporary and ( chatFrame.chatType == "BN_CONVERSATION" ) ) then
				if ( GetCVar("conversationMode") == "popout" or GetCVar("conversationMode") == "popout_and_inline" ) then
					FCF_LeaveConversation(self, chatFrame);
					return;
				else
					FCF_PopInWindow(self, chatFrame);
					return;
				end
			elseif ( chatFrame.isTemporary and ( chatFrame.chatType == "PET_BATTLE_COMBAT_LOG" ) ) then
				FCF_Close(chatFrame);
			else
				GMError(format("Unhandled temporary window type. chatType: %s, chatTarget %s", tostring(chatFrame.chatType), tostring(chatFrame.chatTarget)));
			end
		end
		return;
	end

	-- Close all dropdowns
	CloseDropDownMenus();

	-- If frame is docked assume that a click is to select a chat window, not drag it
	SELECTED_CHAT_FRAME = chatFrame;
	if ( chatFrame.isDocked and FCFDock_GetSelectedWindow(GENERAL_CHAT_DOCK) ~= chatFrame ) then
		FCF_SelectDockFrame(chatFrame);
		FCF_FadeInChatFrame(chatFrame);
		return;
	else
		if ( GetCVar("chatStyle") ~= "classic" ) then
			ChatEdit_SetLastActiveWindow(chatFrame.editBox);
		end
		FCF_FadeInChatFrame(chatFrame);
	end
	
end

function FCF_SetTabPosition(chatFrame, x)
	local chatTab = _G[chatFrame:GetName().."Tab"];
	chatTab:ClearAllPoints();
	chatTab:SetPoint("BOTTOMLEFT", chatFrame:GetName().."Background", "TOPLEFT", x+2, 0);
end

function FCF_SaveDock()
	for index, value in pairs(FCFDock_GetChatFrames(GENERAL_CHAT_DOCK)) do
		SetChatWindowDocked(value:GetID(), index);
	end
end

function FCF_LeaveConversation(frame, fallback)
	if ( fallback ) then
		frame=fallback
	end
	if ( not frame ) then
		frame = FCF_GetCurrentChatFrame();
	end
	
	assert(frame.chatType == "BN_CONVERSATION");
	BNLeaveConversation(tonumber(frame.chatTarget));
	
	FCF_Close(frame);
end

function FCF_PopInWindow(frame, fallback)
	if ( fallback ) then
        frame=fallback
    end
	if ( not frame ) then
		frame = FCF_GetCurrentChatFrame();
	end
	if ( frame == DEFAULT_CHAT_FRAME ) then
		return;
	end
	
	--Restore any chats this frame had to the DEFAULT_CHAT_FRAME
	FCF_RestoreChatsToFrame(DEFAULT_CHAT_FRAME, frame);
	
	FCF_Close(frame);
end

function FCF_Close(frame, fallback)
    if ( fallback ) then
        frame=fallback
    end
	if ( not frame ) then
		frame = FCF_GetCurrentChatFrame();
	end
	if ( frame == DEFAULT_CHAT_FRAME ) then
		return;
	end
	FCF_UnDockFrame(frame);
	HideUIPanel(frame);
	_G[frame:GetName().."Tab"]:Hide();
	FCF_FlagMinimizedPositionReset(frame);
	if ( frame.minFrame and frame.minFrame:IsShown() ) then
		frame.minFrame:Hide();
	end
	if ( frame.isTemporary ) then
		FCFManager_UnregisterDedicatedFrame(frame, frame.chatType, frame.chatTarget);
		frame.isRegistered = false;
		frame.inUse = false;
	end
	if ( PENDING_BN_WHISPER_TO_CONVERSATION_FRAME == frame ) then
		PENDING_BN_WHISPER_TO_CONVERSATION_FRAME = nil;
	end
	
	--Reset what this window receives.
	ChatFrame_RemoveAllMessageGroups(frame);
	ChatFrame_RemoveAllChannels(frame);
	ChatFrame_ReceiveAllPrivateMessages(frame);
	ChatFrame_ReceiveAllBNConversations(frame);
end

function FCF_RestoreChatsToFrame(targetFrame, sourceFrame)
	--Restore chat types
	for _, messageType in pairs(sourceFrame.messageTypeList) do
		ChatFrame_AddMessageGroup(targetFrame, messageType);
	end
	
	--Restore channels
	for _, channel in pairs(sourceFrame.channelList) do
		ChatFrame_AddChannel(targetFrame, channel);
	end
	
	--Restore whispers
	if ( sourceFrame.privateMessageList ) then
		for name, value in pairs(sourceFrame.privateMessageList) do
			if ( value ) then
				ChatFrame_RemoveExcludePrivateMessageTarget(targetFrame, name);
			end
		end
	end
	
	if ( sourceFrame.bnConversationList ) then
		for name, value in pairs(sourceFrame.bnConversationList) do
			if ( value ) then
				ChatFrame_RemoveExcludeBNConversationTarget(targetFrame, name);
			end
		end
	end
end

-- Tab flashing functions
function FCF_FlashTab(self)
	local tabFlash = _G[self:GetName().."TabFlash"];
	if ( not self.isDocked or (self == SELECTED_DOCK_FRAME) or UIFrameIsFlashing(tabFlash) ) then
		return;
	end
	tabFlash:Show();
	UIFrameFlash(tabFlash, 0.25, 0.25, 60, nil, 0.5, 0.5);
end

-- Function for repositioning the chat dock depending on if there's a shapeshift bar/stance bar, etc...
function FCF_UpdateDockPosition()
	if ( DEFAULT_CHAT_FRAME:IsUserPlaced() ) then
		return;
	end
	
	local chatOffset = 85;
	if ( GetNumShapeshiftForms() > 0 or HasPetUI() or PetHasActionBar() ) then
		if ( MultiBarBottomLeft:IsShown() ) then
			chatOffset = chatOffset + 55;
		else
			chatOffset = chatOffset + 15;
		end
	elseif ( MultiBarBottomLeft:IsShown() ) then
		chatOffset = chatOffset + 15;
	end
	DEFAULT_CHAT_FRAME:SetPoint("BOTTOMLEFT", "UIParent", "BOTTOMLEFT", 32, chatOffset);
	FCF_DockUpdate();
end

function FCF_Set_NormalChat()
	ChatFrame2:StartMoving();
	ChatFrame2:StopMovingOrSizing();
	FCF_SetLocked(ChatFrame2, nil);
	-- to fix a bug with the combat log not repositioning its tab properly when coming out of
	-- simple chat, we need to update now
	FCF_DockUpdate();
end

-- Functions to set and remove the chat window show delay on mouseover
function SetChatMouseOverDelay(noDelay)
	if ( noDelay == "1" ) then
		CHAT_TAB_SHOW_DELAY = 0;
		CHAT_FRAME_FADE_TIME = 0;
	else
		CHAT_TAB_SHOW_DELAY = 0.2;
		CHAT_FRAME_FADE_TIME = 0.15;
	end
end

-- Reset the chat windows to default
function FCF_ResetChatWindows()
	ChatFrame1:ClearAllPoints();
	ChatFrame1:SetPoint("BOTTOMLEFT", "UIParent", "BOTTOMLEFT", 32, 95);
	ChatFrame1:SetWidth(430);
	ChatFrame1:SetHeight(120);
	ChatFrame1.isInitialized = 0;
	FCF_SetButtonSide(ChatFrame1, "left")
	FCF_SetChatWindowFontSize(nil, ChatFrame1, 14);
	FCF_SetWindowName(ChatFrame1, GENERAL);
	FCF_SetWindowColor(ChatFrame1, DEFAULT_CHATFRAME_COLOR.r, DEFAULT_CHATFRAME_COLOR.g, DEFAULT_CHATFRAME_COLOR.b);
	FCF_SetWindowAlpha(ChatFrame1, DEFAULT_CHATFRAME_ALPHA);
	FCF_UnDockFrame(ChatFrame1);
	ChatFrame_RemoveAllMessageGroups(ChatFrame1);
	ChatFrame_RemoveAllChannels(ChatFrame1);
	ChatFrame_ReceiveAllPrivateMessages(ChatFrame1);
	ChatFrame_ReceiveAllBNConversations(ChatFrame1);
	SELECTED_CHAT_FRAME = ChatFrame1;
	DEFAULT_CHAT_FRAME.chatframe = DEFAULT_CHAT_FRAME;

	FCF_SetChatWindowFontSize(nil, ChatFrame2, 14);
	FCF_SetWindowName(ChatFrame2, COMBAT_LOG);
	FCF_SetWindowColor(ChatFrame2, DEFAULT_CHATFRAME_COLOR.r, DEFAULT_CHATFRAME_COLOR.g, DEFAULT_CHATFRAME_COLOR.b);
	FCF_SetWindowAlpha(ChatFrame2, DEFAULT_CHATFRAME_ALPHA);
	ChatFrame_RemoveAllMessageGroups(ChatFrame2);
	ChatFrame_RemoveAllChannels(ChatFrame2);
	ChatFrame_ReceiveAllPrivateMessages(ChatFrame2);
	ChatFrame_ReceiveAllBNConversations(ChatFrame2);
	FCF_UnDockFrame(ChatFrame2);
	ChatFrame2.isInitialized = 0;
	for _, chatFrameName in ipairs(CHAT_FRAMES) do
		if ( chatFrameName ~= "ChatFrame1" ) then
			local chatFrame = _G[chatFrameName];
			if ( chatFrame.isTemporary and chatFrame.chatType == "BN_CONVERSATION" and
				BNGetConversationInfo(tonumber(chatFrame.chatTarget)) and (GetCVar("conversationMode") == "popout" or GetCVar("conversationMode") == "popout_and_inline") ) then
				--We're still in this conversation, so we just want to reset the position, not remove the frame.
				FCF_DockFrame(chatFrame, 3);	--Put it after General and Combat Log
			else
				chatFrame.isInitialized = 0;
				FCF_SetTabPosition(chatFrame, 0);
				FCF_Close(chatFrame);
				FCF_UnDockFrame(chatFrame);
				FCF_SetWindowName(chatFrame, "");
				ChatFrame_RemoveAllMessageGroups(chatFrame);
				ChatFrame_RemoveAllChannels(chatFrame);
				ChatFrame_ReceiveAllPrivateMessages(chatFrame);
				ChatFrame_ReceiveAllBNConversations(chatFrame);
			end
			FCF_SetChatWindowFontSize(nil, chatFrame, 14);
			FCF_SetWindowColor(chatFrame, DEFAULT_CHATFRAME_COLOR.r, DEFAULT_CHATFRAME_COLOR.g, DEFAULT_CHATFRAME_COLOR.b);
			FCF_SetWindowAlpha(chatFrame, DEFAULT_CHATFRAME_ALPHA);
		end
	end
	ChatFrame1.init = 0;
	FCF_DockFrame(ChatFrame1, 1, true);
	FCF_DockFrame(ChatFrame2, 2);

	-- resets to hard coded defaults
	ResetChatWindows();
	UIParent_ManageFramePositions();
	FCFDock_SelectWindow(GENERAL_CHAT_DOCK, ChatFrame1);
end

function IsCombatLog(frame)
	if ( frame == ChatFrame2 and IsAddOnLoaded("Blizzard_CombatLog") ) then
		return true;
	else
		return false;
	end
end

function FCFClickAnywhereButton_OnLoad(self)
	self:SetFrameLevel(self:GetParent():GetFrameLevel() - 1);
	self:RegisterEvent("VARIABLES_LOADED");
	self:RegisterEvent("CVAR_UPDATE");
	self:RegisterForClicks("LeftButtonDown", "RightButtonDown");
	FCFClickAnywhereButton_UpdateState(self);
end

function FCFClickAnywhereButton_OnEvent(self, event, ...)
	local arg1 = ...;
	if ( event == "VARIABLES_LOADED" or
		(event == "CVAR_UPDATE" and (arg1 == "chatStyle" or arg1 == "CHAT_WHOLE_WINDOW_CLICKABLE")) ) then
		FCFClickAnywhereButton_UpdateState(self);
	end
end

function FCFClickAnywhereButton_UpdateState(self)
	if ( GetCVar("chatStyle") == "im" and GetCVarBool("wholeChatWindowClickable") and
		LAST_ACTIVE_CHAT_EDIT_BOX ~=  self:GetParent().editBox ) then
		self:Show();
	else
		self:Hide();
	end
end

function FCF_MinimizeFrame(chatFrame, side)
	local chatTab = _G[chatFrame:GetName().."Tab"];
	
	local createdFrame = false;
	if ( not chatFrame.minFrame ) then
		chatFrame.minFrame = FCF_CreateMinimizedFrame(chatFrame);
	end
	
	if ( chatFrame.minFrame.resetPosition ) then
		chatFrame.minFrame:ClearAllPoints();
		chatFrame.minFrame:SetPoint("TOP"..side, chatFrame, "TOP"..side, 0, 0);
		chatFrame.minFrame.resetPosition = false;
	end
	
	chatFrame.minimized = true;
	
	chatFrame.minFrame:Show();
	chatFrame:Hide();
	chatTab:Hide();
end

function FCF_MaximizeFrame(chatFrame)
	local minFrame = chatFrame.minFrame;
	local chatTab = _G[chatFrame:GetName().."Tab"];
	
	chatFrame.minimized = false;
	
	minFrame:UnlockHighlight();
	minFrame:Hide();
	chatFrame:Show();
	chatTab:Show();
	
	FCF_FadeInChatFrame(chatFrame);
	
	if ( GetCVar("chatStyle") == "im" ) then
		ChatEdit_SetLastActiveWindow(chatFrame.editBox);
	end
end

function FCF_CreateMinimizedFrame(chatFrame)
	local chatTab = _G[chatFrame:GetName().."Tab"];
	
	local minFrame = CreateFrame("Button", chatFrame:GetName().."Minimized", UIParent, "FloatingChatFrameMinimizedTemplate");
	minFrame.maxFrame = chatFrame;
	
	minFrame:SetText(chatFrame.name);

	--Copy the colors from the minimized frame.
	minFrame.selectedColorTable = chatTab.selectedColorTable;
	FCFMin_UpdateColors(minFrame);
	
	if ( not chatFrame.isTemporary ) then
		minFrame.conversationIcon:Hide();
	else
		local conversationIcon;
		if ( chatFrame.chatType == "WHISPER" or chatFrame.chatType == "BN_WHISPER" ) then
			conversationIcon = "Interface\\ChatFrame\\UI-ChatWhisperIcon";
		elseif ( chatFrame.chatType == "PET_BATTLE_COMBAT_LOG" ) then
			conversationIcon = "Interface\\Icons\\Tracking_WildPet";
		else
			conversationIcon = "Interface\\ChatFrame\\UI-ChatConversationIcon";
		end
		
		minFrame.conversationIcon:SetTexture(conversationIcon);
	end
	
	if (chatFrame.isTemporary) then
		minFrame.Text:SetJustifyH("LEFT");
		minFrame.Text:SetPoint("LEFT", minFrame, "LEFT", 30, 0);
	end
	
	--Make sure the position is reset.
	minFrame.resetPosition = true;
	
	return minFrame;
end

function FCFMin_UpdateColors(minFrame)
	--Color it.
	local colorTable = minFrame.selectedColorTable or DEFAULT_TAB_SELECTED_COLOR_TABLE;
	
	if ( minFrame.selectedColorTable ) then
		minFrame:GetFontString():SetTextColor(colorTable.r, colorTable.g, colorTable.b);
	else
		minFrame:GetFontString():SetTextColor(NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b);
	end

	minFrame.leftHighlightTexture:SetVertexColor(colorTable.r, colorTable.g, colorTable.b);
	minFrame.middleHighlightTexture:SetVertexColor(colorTable.r, colorTable.g, colorTable.b);
	minFrame.rightHighlightTexture:SetVertexColor(colorTable.r, colorTable.g, colorTable.b);
	minFrame.glow:SetVertexColor(colorTable.r, colorTable.g, colorTable.b);
	
	minFrame.conversationIcon:SetVertexColor(colorTable.r, colorTable.g, colorTable.b);
end

--This function just makes the position be reset the next time the minimize frame is shown.
function FCF_FlagMinimizedPositionReset(chatFrame)
	if ( chatFrame.minFrame ) then
		chatFrame.minFrame.resetPosition = true;
	end
end

------Docking related functions for the new docking system
--[[
Since we've been discussing allowing multiple docks, this code is designed to be mostly-OO. Please try not to use global variables.
(Theoretically, all of these functions may be put in a metatable to allow, e.g., "Dock:SelectWindow(chatWindow)".
To keep with this, please ensure that "dock" is the first argument of every function.)
]]

function FCFDock_OnLoad(dock)
	dock.DOCKED_CHAT_FRAMES = {};
	dock.isDirty = true;	--You dirty, dirty frame
end

function FCFDock_OnEvent(dock, event, ...)
	if ( event == "UPDATE_CHAT_WINDOWS" ) then
		--FCFDock_ForceTabSort(dock);
		--FCFDock_ForceReanchoring(dock);
	end
end

function FCFDock_GetChatFrames(dock)
	return dock.DOCKED_CHAT_FRAMES;
end

function FCFDock_SetPrimary(dock, chatFrame)
	dock.primary = chatFrame;
	dock:SetPoint("BOTTOMLEFT", chatFrame, "TOPLEFT", 0, 6);
	dock:SetPoint("BOTTOMRIGHT", chatFrame, "TOPRIGHT", 0, 6);
	
	chatFrame:SetScript("OnSizeChanged", function(self) FCFDock_OnPrimarySizeChanged(dock) end);
	
	if ( not FCFDock_GetSelectedWindow(dock) ) then
		FCFDock_SelectWindow(dock, chatFrame);
	end
	
	FCFDock_ForceReanchoring(dock);
	
	FCFDock_AddChatFrame(dock, chatFrame, 1);
end

function FCFDock_OnPrimarySizeChanged(dock)
	dock.isDirty = true;
	
	--We have to save off the current leftmost-tab before we resize the tabs.
	dock.leftTab = FCFDockScrollFrame_GetLeftmostTab(dock.scrollFrame);
	
	--We have to do it on the next frame to deal with issues caused by resizing the WoW client (frame positions may not be valid)
	dock:SetScript("OnUpdate", FCFDock_OnUpdate);
end

function FCFDock_OnUpdate(self)
	--These may fail if we're resizing the WoW client
	if ( FCFDock_UpdateTabs(self) and FCFDockScrollFrame_JumpToTab(self.scrollFrame, self.leftTab) ) then
		self.leftTab = nil;
		self:SetScript("OnUpdate", nil);
	end
end

function FCFDock_ForceReanchoring(dock)
	for index, chatFrame in pairs(dock.DOCKED_CHAT_FRAMES) do
		if ( dock.primary ~= chatFrame ) then
			chatFrame:ClearAllPoints();
			chatFrame:SetAllPoints(dock.primary);
		end
	end
end

function FCFDock_AddChatFrame(dock, chatFrame, position)
	if ( not dock.primary ) then
		error("Need a primary window before another can be added.");
	end
	
	if ( FCFDock_HasDockedChatFrame(dock, chatFrame) ) then
		return;	--We're already docked...
	end
	
	dock.isDirty = true;
	chatFrame.isDocked = 1;
	
	if ( position and position <= #dock.DOCKED_CHAT_FRAMES + 1 ) then
		assert(position ~= 1 or chatFrame == dock.primary);
		tinsert(dock.DOCKED_CHAT_FRAMES, position, chatFrame);
	else
		tinsert(dock.DOCKED_CHAT_FRAMES, chatFrame);
	end
	
	FCFDock_HideInsertHighlight(dock);
	
	if ( dock.primary ~= chatFrame ) then
		chatFrame:ClearAllPoints();
		chatFrame:SetAllPoints(dock.primary);
		chatFrame:SetMovable(false);
		chatFrame:SetResizable(false);
	end
	
	if ( chatFrame.conversationButton ) then
		BNConversationButton_UpdateAttachmentPoint(chatFrame.conversationButton);
	end
	
	chatFrame.buttonFrame.minimizeButton:Hide();
	chatFrame.buttonFrame:SetAlpha(1.0);
	
	dock.overflowButton.list:Hide();
	FCFDock_UpdateTabs(dock);
end

function FCFDock_RemoveChatFrame(dock, chatFrame)
	assert(chatFrame ~= dock.primary or #dock.DOCKED_CHAT_FRAMES == 1);
	dock.isDirty = true;
	tDeleteItem(dock.DOCKED_CHAT_FRAMES, chatFrame);
	local chatTab = _G[chatFrame:GetName().."Tab"];
	chatFrame.isDocked = nil;
	chatTab:SetParent(UIParent);
	chatTab:SetFrameStrata("LOW");
	chatFrame:SetMovable(true);
	chatFrame:SetResizable(true);
	FCFTab_UpdateColors(chatTab, true);
	PanelTemplates_TabResize(chatTab, chatTab.sizePadding or 0, nil, nil, nil, chatTab.textWidth);
	if ( FCFDock_GetSelectedWindow(dock) == chatFrame ) then
		FCFDock_SelectWindow(dock, dock.DOCKED_CHAT_FRAMES[1]);
	end
	
	if ( chatFrame.conversationButton ) then
		BNConversationButton_UpdateAttachmentPoint(chatFrame.conversationButton);
	end
	
	chatFrame.buttonFrame.minimizeButton:Show();
	dock.overflowButton.list:Hide();
	chatFrame:Show();
	FCFDock_UpdateTabs(dock);
end

function FCFDock_HasDockedChatFrame(dock, chatFrame)
	return tContains(dock.DOCKED_CHAT_FRAMES, chatFrame);
end

function FCFDock_SelectWindow(dock, chatFrame)
	assert(chatFrame)
	dock.isDirty = true;
	dock.selected = chatFrame;
	dock.overflowButton.list:Hide();
	FCFDock_UpdateTabs(dock);
	
	if ( ChatFrameMenuButton ) then
		if ( chatFrame.conversationButton and chatFrame.conversationButton:IsShown() ) then
			ChatFrameMenuButton:Hide();
		else
			ChatFrameMenuButton:Show();
		end
	end
end

function FCFDock_GetSelectedWindow(dock)
	return dock.selected;
end

function FCFDock_UpdateTabs(dock, forceUpdate)
	if ( not dock.isDirty and not forceUpdate ) then	--No changes have been made since the last update.
		return;
	end
	
	local scrollChild = dock.scrollFrame:GetScrollChild();
	local lastDockedStaticTab = nil;
	local lastDockedDynamicTab = nil;
	
	local numDynFrames = 0;	--Number of dynamicly sized frames.
	local selectedDynIndex = nil;
	
	for index, chatFrame in ipairs(dock.DOCKED_CHAT_FRAMES) do
		local chatTab = _G[chatFrame:GetName().."Tab"];
		if ( chatFrame == FCFDock_GetSelectedWindow(dock) ) then
			chatFrame:Show();
		else
			chatFrame:Hide();
		end
		FCFTab_UpdateAlpha(chatFrame);
		chatTab:ClearAllPoints();
		chatTab:Show();
		FCFTab_UpdateColors(chatTab, chatFrame == FCFDock_GetSelectedWindow(dock));
		
		if ( chatFrame.isStaticDocked ) then
			chatTab:SetParent(dock);
			PanelTemplates_TabResize(chatTab, chatTab.sizePadding or 0);
			if ( lastDockedStaticTab ) then
				chatTab:SetPoint("LEFT", lastDockedStaticTab, "RIGHT", 0, 0);
			else
				chatTab:SetPoint("LEFT", dock, "LEFT", 0, 0);
			end
			lastDockedStaticTab = chatTab;
		else
			chatTab:SetParent(scrollChild);
			numDynFrames = numDynFrames + 1;
			
			if ( FCFDock_GetSelectedWindow(dock) == chatFrame ) then
				selectedDynIndex = numDynFrames;
			end
			
			if ( lastDockedDynamicTab ) then
				chatTab:SetPoint("LEFT", lastDockedDynamicTab, "RIGHT", 0, 0);
			else
				chatTab:SetPoint("LEFT", scrollChild, "LEFT", 0, 0);
			end
			lastDockedDynamicTab = chatTab;
		end
	end
	
	local dynTabSize, hasOverflow = FCFDock_CalculateTabSize(dock, numDynFrames);
	
	for index, chatFrame in ipairs(dock.DOCKED_CHAT_FRAMES) do
		if ( not chatFrame.isStaticDocked ) then
			local chatTab = _G[chatFrame:GetName().."Tab"];
			PanelTemplates_TabResize(chatTab, chatTab.sizePadding or 0, dynTabSize);
		end
	end
	
	dock.scrollFrame:SetPoint("LEFT", lastDockedStaticTab, "RIGHT", 0, 0);
	if ( hasOverflow ) then
		dock.overflowButton:Show();
		dock.scrollFrame:SetPoint("BOTTOMRIGHT", dock.overflowButton, "BOTTOMLEFT", 0, 0);
	else
		dock.overflowButton:Hide();
		dock.scrollFrame:SetPoint("BOTTOMRIGHT", dock, "BOTTOMRIGHT", 0, -5);
	end
	
	--Cache some of this data on the scroll frame for animating to the selected tab.
	dock.scrollFrame.dynTabSize = dynTabSize;
	dock.scrollFrame.numDynFrames = numDynFrames;
	dock.scrollFrame.selectedDynIndex = selectedDynIndex;
	
	dock.isDirty = false;
	
	return FCFDock_ScrollToSelectedTab(dock);
end

--Returns dynTabSize, hasOverflow
function FCFDock_CalculateTabSize(dock, numDynFrames)
	local MIN_SIZE, MAX_SIZE = 60, 90;
	local scrollSize = dock.scrollFrame:GetWidth() + (dock.overflowButton:IsShown() and dock.overflowButton.width or 0); --We want the total width assuming no overflow button.
	
	--First, see if we can fit all the tabs at the maximum size
	if ( numDynFrames * MAX_SIZE < scrollSize ) then
		return MAX_SIZE, false;
	end
	
	if ( scrollSize / MIN_SIZE < numDynFrames ) then
		--Not everything fits, so we'll need room for the overflow button.
		scrollSize = scrollSize - dock.overflowButton.width;
	end
	
	--Figure out how many tabs we're going to be able to fit at the minimum size
	local numWholeTabs = min(floor(scrollSize / MIN_SIZE), numDynFrames)
	
	if ( numWholeTabs == 0 ) then
		return scrollSize, true;
	end
	
	--How big each tab should be.
	local tabSize = scrollSize / numWholeTabs;
	
	return tabSize, (numDynFrames > numWholeTabs);
end

function FCFDock_ScrollToSelectedTab(dock)
	if ( FCFDockScrollFrame_GetScrollDistanceNeeded(dock.scrollFrame, dock.scrollFrame.selectedDynIndex) ~= 0) then
		dock.scrollFrame:SetScript("OnUpdate", FCFDockScrollFrame_OnUpdate);
		return true;
	else
		return FCFDockScrollFrame_JumpToTab(dock.scrollFrame, FCFDockScrollFrame_GetLeftmostTab(dock.scrollFrame));	--Make sure we're exactly aligned with the tab.
	end
end

---These functions deal with the scroll frame handling dynamic tabs.
function FCFDockScrollFrame_OnUpdate(self, elapsed)
	local MOVEMENT_SPEED = 10;
	
	local totalDistanceNeeded = FCFDockScrollFrame_GetScrollDistanceNeeded(self, self.selectedDynIndex);
	if ( abs(totalDistanceNeeded) < 1.0 ) then	--Delta chosen through experimentation
		self:SetScript("OnUpdate", nil);
		FCFDockScrollFrame_JumpToTab(self, FCFDockScrollFrame_GetLeftmostTab(self));	--Make sure we're exactly aligned with the tab.
		return;
	end
	
	local currentPosition = self:GetHorizontalScroll();
	
	local distanceNoCap = totalDistanceNeeded * MOVEMENT_SPEED * elapsed;
	local distanceToMove = (totalDistanceNeeded > 0) and min(totalDistanceNeeded, distanceNoCap) or max(totalDistanceNeeded, distanceNoCap);
	
	self:SetHorizontalScroll(max(currentPosition + distanceToMove, 0));
end

function FCFDock_GetInsertIndex(dock, chatFrame, mouseX, mouseY)
	if ( chatFrame.isStaticDocked ) then
		local maxPosition = 0;
		for index, value in ipairs(dock.DOCKED_CHAT_FRAMES) do
			if ( value.isStaticDocked ) then
				local tab = _G[value:GetName().."Tab"];
				if ( mouseX < (tab:GetLeft() + tab:GetRight()) / 2 and	--Find the first tab we're on the left of. (Being on top of the tab, but left of the center counts)
					tab:GetID() ~= dock.primary:GetID()) then	--We never count as being to the left of the primary tab.
					return index;
				end
				maxPosition = index;
			end
		end
		--We aren't to the left of anything, so we're going into the far-right position.
		return maxPosition + 1;
	else
		--Find the dynamic insertion spot
		local maxPosition = 9^9;
		local leftTab = FCFDockScrollFrame_GetLeftmostTab(dock.scrollFrame);
		local numDynTabsDisplayed = dock.scrollFrame:GetWidth() / dock.scrollFrame.dynTabSize;
		
		local currTabNum = 0;
		for index, value in ipairs(dock.DOCKED_CHAT_FRAMES) do
			if ( not value.isStaticDocked ) then
				currTabNum = currTabNum + 1;
				if ( currTabNum >= leftTab and currTabNum < leftTab + numDynTabsDisplayed ) then
					local tab = _G[value:GetName().."Tab"];
					if ( mouseX < (tab:GetLeft() + tab:GetRight())/2 ) then
						return index;
					end
					maxPosition = index;
				end
			end
		end
		return min(#dock.DOCKED_CHAT_FRAMES + 1, maxPosition + 1);
	end
end

function FCFDock_PlaceInsertHighlight(dock, chatFrame, mouseX, mouseY)
	local insert = FCFDock_GetInsertIndex(dock, chatFrame, mouseX, mouseY);
	
	local attachFrame = dock.primary;
	
	local leftDynTab = FCFDockScrollFrame_GetLeftmostTab(dock.scrollFrame);
	local numDynTabsDisplayed = dock.scrollFrame:GetWidth() / dock.scrollFrame.dynTabSize;
	
	local dynamicIndex = 0;
	for index, value in ipairs(dock.DOCKED_CHAT_FRAMES) do
		if ( index < insert ) then
			if ( value.isStaticDocked ) then
				attachFrame = value;
			else
				dynamicIndex = dynamicIndex + 1;
				if ( dynamicIndex >= leftDynTab and dynamicIndex < leftDynTab + numDynTabsDisplayed ) then
					attachFrame = value;
				end
			end
		end
	end
	
	dock.insertHighlight:ClearAllPoints();
	dock.insertHighlight:SetPoint("BOTTOMLEFT", _G[attachFrame:GetName().."Tab"], "BOTTOMRIGHT", -15, -4);
	dock.insertHighlight:Show();
end

function FCFDock_HideInsertHighlight(dock)
	dock.insertHighlight:Hide();
end

function FCFDock_SetDirty(dock)
	dock.isDirty = true;
end

function FCFDockScrollFrame_GetScrollDistanceNeeded(scrollFrame, dynFrameIndex)
	
	local firstIndex = (scrollFrame:GetHorizontalScroll() / scrollFrame.dynTabSize) + 1;
	
	local numDisplayedFrames = scrollFrame:GetWidth() / scrollFrame.dynTabSize;
	local lastIndex = firstIndex + numDisplayedFrames - 1;

	if ( dynFrameIndex and dynFrameIndex < firstIndex ) then	--Need to scroll left to get to the selected button
		return (dynFrameIndex - firstIndex) * scrollFrame.dynTabSize;
	elseif ( dynFrameIndex and dynFrameIndex > lastIndex )	then --Need to scroll right to get to the selected button
		return (dynFrameIndex - lastIndex) * scrollFrame.dynTabSize;
	elseif (  firstIndex > 1 and scrollFrame.numDynFrames < lastIndex ) then --Need to scroll left to fill in empty space at the end.
		return (scrollFrame.numDynFrames - lastIndex) * scrollFrame.dynTabSize;
	else
		return 0;
	end
end

function FCFDockScrollFrame_GetLeftmostTab(scrollFrame)
	return floor((scrollFrame:GetHorizontalScroll() / scrollFrame.dynTabSize) + 0.5) + 1;
end

function FCFDockScrollFrame_JumpToTab(scrollFrame, leftTab)
	--If we have a selected frame, make sure it's still in view.
	local numTabsDisplayed = scrollFrame:GetWidth() / scrollFrame.dynTabSize;
	
	if ( scrollFrame.selectedDynIndex ) then
		if ( scrollFrame.selectedDynIndex >= leftTab + numTabsDisplayed ) then
			leftTab = scrollFrame.selectedDynIndex - numTabsDisplayed + 1;
		elseif ( scrollFrame.selectedDynIndex < leftTab ) then
			leftTab = scrollFrame.selectedDynIndex;
		end
	end
	
	--Make sure, if we can show more frames, we do.
	leftTab = min(leftTab, scrollFrame.numDynFrames - numTabsDisplayed + 1);
	
	--And make sure we never go to the left of 1 (for example, if we have extra space)
	leftTab = max(leftTab, 1);
	
	scrollFrame:SetHorizontalScroll(scrollFrame.dynTabSize * (leftTab - 1));
	
	return FCFDockOverflowButton_UpdatePulseState(scrollFrame:GetParent().overflowButton);
end

--Dock list related functions
function FCFDockOverflow_CloseLists()
	local list = GENERAL_CHAT_DOCK.overflowButton.list;
	if ( list:IsShown() ) then
		list:Hide();
		return true;
	else
		return false;
	end
end

function FCFDockOverflowButton_UpdatePulseState(self)
	local dock = self:GetParent();
	local shouldPulse = false;
	for _, chatFrame in pairs(FCFDock_GetChatFrames(dock)) do
		local chatTab = _G[chatFrame:GetName().."Tab"];
		if ( not chatFrame.isStaticDocked and chatTab.alerting) then
			--Make sure the rects are valid. (Not always the case when resizing the WoW client
			if ( not chatTab:GetRight() or not dock.scrollFrame:GetRight() ) then
				return false;
			end
			--Check if it's off the screen.
			local DELTA = 3;	--Chosen through experimentation
			if ( chatTab:GetRight() < (dock.scrollFrame:GetLeft() + DELTA) or chatTab:GetLeft() > (dock.scrollFrame:GetRight() - DELTA) ) then
				shouldPulse = true;
				break;
			end
		end
	end
	
	if ( shouldPulse ) then
		UIFrameFlash(self:GetHighlightTexture(), 1.0, 1.0, -1, true, 0, 0, "chat");
		self:LockHighlight();
		self.alerting = true;
	else
		UIFrameFlashStop(self:GetHighlightTexture());
		self:UnlockHighlight();
		self:GetHighlightTexture():Show();
		self.alerting = false;
	end
	
	if ( self.list:IsShown() ) then
		FCFDockOverflowList_Update(self.list, dock);
	end
	return true;
end

function FCFDockOverflowButton_OnClick(self, button)
	PlaySound("UChatScrollButton");
	if ( self.list:IsShown() ) then
		self.list:Hide();
	else
		FCFDockOverflowList_Update(self.list, self:GetParent());
		self.list:Show();
	end
end

function FCFDockOverflowButton_OnEvent(self, event, ...)
	if ( event == "UPDATE_CHAT_COLOR" and self.list:IsShown() ) then
		FCFDockOverflowList_Update(self.list, self:GetParent());
	end		
end

function FCFDockOverflowList_Update(list, dock)
	local dockedFrames = FCFDock_GetChatFrames(dock);
	
	list:SetHeight(#dockedFrames *15 + 35);
	
	list.numTabs:SetFormattedText(CHAT_WINDOWS_COUNT, #dockedFrames);
	
	for i = 1, #dockedFrames do
		local button = list.buttons[i];
		if ( not button ) then
			list.buttons[i] = CreateFrame("Button", list:GetName().."Button"..i, list, "DockManagerOverflowListButtonTemplate");
			button = list.buttons[i];
			
			if ( not list.buttons[i-1] ) then
				button:SetPoint("TOPLEFT", list, "TOPLEFT", 5, -19);
			else
				button:SetPoint("TOPLEFT", list.buttons[i-1], "BOTTOMLEFT", 0, -3);
			end
			button:SetWidth(list:GetWidth() - 10);	-- buttons are 5 pixels in on both sides
		end
		
		FCFDockOverflowListButton_SetValue(button, dockedFrames[i]);
	end
	
	for i = #dockedFrames + 1, #list.buttons do
		list.buttons[i]:Hide();
	end
end

function FCFDockOverflowListButton_SetValue(button, chatFrame)
	local chatTab = _G[chatFrame:GetName().."Tab"];
	button.chatFrame = chatFrame;
	button:SetText(chatFrame.name);
	
	local colorTable = chatTab.selectedColorTable or DEFAULT_TAB_SELECTED_COLOR_TABLE;
	
	if ( chatTab.selectedColorTable ) then
		button:GetFontString():SetTextColor(colorTable.r, colorTable.g, colorTable.b);
	else
		button:GetFontString():SetTextColor(NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b);
	end
	
	button.glow:SetVertexColor(colorTable.r, colorTable.g, colorTable.b);
	
	if ( chatTab.conversationIcon ) then
		button.conversationIcon:SetVertexColor(colorTable.r, colorTable.g, colorTable.b);
		button.conversationIcon:Show();
	else
		button.conversationIcon:Hide();
	end
	
	if ( chatTab.alerting ) then
		button.alerting = true;
		UIFrameFlash(button.glow, 1.0, 1.0, -1, false, 0, 0, "chat");
	else
		button.alerting = false;
		UIFrameFlashStop(button.glow);
	end
	
	button:Show();
end

function FCFDockOverflowListButton_OnClick(self, button)
	FCFDock_SelectWindow(self:GetParent():GetParent():GetParent(), self.chatFrame);
end

---------------------------------------------------
-----------Temp Window Manager-------------
----------------------------------------------------
local dedicatedWindows = {};

local function FCFManager_GetToken(chatType, chatTarget)
	return strlower(chatType)..(chatTarget and ";;"..strlower(chatTarget) or "");
end

function FCFManager_RegisterDedicatedFrame(chatFrame, chatType, chatTarget)
	local token = FCFManager_GetToken(chatType, chatTarget);
	if ( not dedicatedWindows[token] ) then
		dedicatedWindows[token] = {};
	end
	
	if ( not tContains(dedicatedWindows[token], chatFrame) ) then
		tinsert(dedicatedWindows[token], chatFrame);
	end
end

function FCFManager_UnregisterDedicatedFrame(chatFrame, chatType, chatTarget)
	local token = FCFManager_GetToken(chatType, chatTarget);
	local windowList = dedicatedWindows[token];
	if ( windowList ) then
		tDeleteItem(windowList, chatFrame);
	end
end

function FCFManager_GetNumDedicatedFrames(chatType, chatTarget)
	local token = FCFManager_GetToken(chatType, chatTarget);
	local windowList = dedicatedWindows[token];
	return (windowList and #windowList or 0);
end

function FCFManager_ShouldSuppressMessage(chatFrame, chatType, chatTarget)
	--Using GetToken probably isn't the best way to do this due to the string concatenation, but it's the easiest to get in quickly.
	if ( chatFrame.chatType and FCFManager_GetToken(chatType, chatTarget) == FCFManager_GetToken(chatFrame.chatType, chatFrame.chatTarget) ) then
		--This frame is a dedicated frame of this type, so we should always display.
		return false;
	end
	
	if ( (chatType == "BN_CONVERSATION" and GetCVar("conversationMode") == "popout")
		or (chatType == "BN_WHISPER" and GetCVar("bnWhisperMode") == "popout")
		or (chatType == "WHISPER" and GetCVar("whisperMode") == "popout") ) then
		return true;
	end
	
	return false;
end

function FCFManager_ShouldSuppressMessageFlash(chatFrame, chatType, chatTarget)
	--Using GetToken probably isn't the best way to do this due to the string concatenation, but it's the easiest to get in quickly.
	if ( chatFrame.chatType and FCFManager_GetToken(chatType, chatTarget) == FCFManager_GetToken(chatFrame.chatType, chatFrame.chatTarget) ) then
		--This frame is a dedicated frame of this type, so we should always display.
		return false;
	end
	
	if ( (chatType == "BN_CONVERSATION" and GetCVar("conversationMode") == "popout_and_inline")
		or (chatType == "BN_WHISPER" and GetCVar("bnWhisperMode") == "popout_and_inline") 
		or (chatType == "WHISPER" and GetCVar("whisperMode") == "popout_and_inline") ) then
		return true;
	end
	
	return false;
end

function FCFManager_StopFlashOnDedicatedWindows(chatType, chatTarget)
	local token = FCFManager_GetToken(chatType, chatTarget);
	local windowList = dedicatedWindows[token];
	if (windowList) then
		for i, frame in pairs(windowList) do
			FCF_StopAlertFlash(frame);
		end
	end
end

function FloatingChatFrameManager_OnLoad(self)
	--Register for BN_CONVERSATION related messages to be able to spawn off new windows as needed
	for _, event in pairs(ChatTypeGroup["BN_CONVERSATION"]) do
		self:RegisterEvent(event);
	end
	for _, event in pairs(ChatTypeGroup["BN_WHISPER"]) do
		self:RegisterEvent(event);
	end
	for _, event in pairs(ChatTypeGroup["WHISPER"]) do
		self:RegisterEvent(event);
	end
end

function FloatingChatFrameManager_OnEvent(self, event, ...)
	local arg1 = ...;
	if ( strsub(event, 1, 9) == "CHAT_MSG_" ) then
		local chatType = strsub(event, 10);
		local chatGroup = Chat_GetChatCategory(chatType);
		
		if ( chatGroup == "BN_CONVERSATION" ) then
			if ( GetCVar("conversationMode") == "popout" or GetCVar("conversationMode") == "popout_and_inline" ) then
				if( not (event == "CHAT_MSG_BN_CONVERSATION_NOTICE" and arg1 == "YOU_LEFT_CONVERSATION") ) then
					local chatTarget = tostring(select(8, ...));
					if ( FCFManager_GetNumDedicatedFrames(chatGroup, chatTarget) == 0 ) then
						local chatFrame = FCF_OpenTemporaryWindow(chatGroup, chatTarget);
						chatFrame:GetScript("OnEvent")(chatFrame, event, ...);	--Re-fire the event for the frame.
					elseif (GetCVar("conversationMode") == "popout_and_inline" and BNIsSelf(select(13, ...))) then
						FCFManager_StopFlashOnDedicatedWindows(chatGroup, chatTarget);
					end
				end
			end
		end
		
		if ( (chatGroup == "BN_WHISPER" and (GetCVar("bnWhisperMode") == "popout" or GetCVar("bnWhisperMode") == "popout_and_inline"))
			or (chatGroup == "WHISPER" and (GetCVar("whisperMode") == "popout" or GetCVar("whisperMode") == "popout_and_inline"))) then
			local chatTarget = tostring(select(2, ...));
			
			if ( FCFManager_GetNumDedicatedFrames(chatGroup, chatTarget) == 0 ) then
				local chatFrame = FCF_OpenTemporaryWindow(chatGroup, chatTarget);
				chatFrame:GetScript("OnEvent")(chatFrame, event, ...);	--Re-fire the event for the frame.

				-- If you started the whisper, immediately select the tab
				if ((event == "CHAT_MSG_WHISPER_INFORM" and GetCVar("whisperMode") == "popout")
					or (event == "CHAT_MSG_BN_WHISPER_INFORM" and GetCVar("bnWhisperMode") == "popout") ) then
					FCF_SelectDockFrame(chatFrame);
					FCF_FadeInChatFrame(chatFrame);
				end
			else
				-- While in "Both" mode, if you reply to a whisper, stop the flash on that dedicated whisper tab
				if ( (chatType == "WHISPER_INFORM" and GetCVar("whisperMode") == "popout_and_inline")
				or (chatType == "BN_WHISPER_INFORM" and GetCVar("bnWhisperMode") == "popout_and_inline")) then
					FCFManager_StopFlashOnDedicatedWindows(chatGroup, chatTarget);
				end
			end
		end
	end
end