Quantcast
local A, L = ...
local options = L.C

--------------------------------------
-- grab from elvui chat module
local _G = _G
local gsub, strfind, gmatch, format, max = gsub, strfind, gmatch, format, max
local ipairs, sort, wipe, time, difftime = ipairs, sort, wipe, time, difftime
local pairs, unpack, select, tostring, pcall, next, tonumber, type = pairs, unpack, select, tostring, pcall, next, tonumber, type
local strlower, strsub, strlen, strupper, strtrim, strmatch = strlower, strsub, strlen, strupper, strtrim, strmatch
local tinsert, tremove, tconcat = tinsert, tremove, table.concat

local Ambiguate = Ambiguate
local BetterDate = BetterDate
local BNGetFriendInfo = BNGetFriendInfo
local BNGetFriendInfoByID = BNGetFriendInfoByID
local BNGetNumFriendInvites = BNGetNumFriendInvites
local BNGetNumFriends = BNGetNumFriends
local CreateFrame = CreateFrame
local FlashClientIcon = FlashClientIcon
local GetBNPlayerCommunityLink = GetBNPlayerCommunityLink
local GetBNPlayerLink = GetBNPlayerLink
local GetChannelName = GetChannelName
local GetChatWindowInfo = GetChatWindowInfo
local GetCursorPosition = GetCursorPosition
local GetCVar, GetCVarBool = GetCVar, GetCVarBool
local GetGuildRosterMOTD = GetGuildRosterMOTD
local GetInstanceInfo = GetInstanceInfo
local GetMouseFocus = GetMouseFocus
local GetNumGroupMembers = GetNumGroupMembers
local GetPlayerCommunityLink = GetPlayerCommunityLink
local GetPlayerInfoByGUID = GetPlayerInfoByGUID
local GetPlayerLink = GetPlayerLink
local GetRaidRosterInfo = GetRaidRosterInfo
local GMChatFrame_IsGM = GMChatFrame_IsGM
local GMError = GMError
local hooksecurefunc = hooksecurefunc
local InCombatLockdown = InCombatLockdown
local IsAltKeyDown = IsAltKeyDown
local IsInRaid, IsInGroup = IsInRaid, IsInGroup
local IsShiftKeyDown = IsShiftKeyDown
local PlaySoundFile = PlaySoundFile
local RemoveExtraSpaces = RemoveExtraSpaces
local RemoveNewlines = RemoveNewlines
local ToggleFrame = ToggleFrame
local UnitName = UnitName

local C_DateAndTime_GetCurrentCalendarTime = C_DateAndTime.GetCurrentCalendarTime
local C_Club_GetInfoFromLastCommunityChatLine = C_Club.GetInfoFromLastCommunityChatLine
local Chat_GetChatCategory = Chat_GetChatCategory
local ChatHistory_GetAccessID = ChatHistory_GetAccessID
local ChatFrame_ResolvePrefixedChannelName = ChatFrame_ResolvePrefixedChannelName
local BNet_GetValidatedCharacterName = BNet_GetValidatedCharacterName
local BNet_GetClientEmbeddedTexture = BNet_GetClientEmbeddedTexture
local BNET_CLIENT_WOW = BNET_CLIENT_WOW

local tabTexs = {
	'',
	'Selected',
	'Highlight'
}

local function GetDockerParent(docker, chat)
	if not docker then return end

	local _, relativeTo = chat:GetPoint()
	if relativeTo == docker then
		return docker:GetParent()
	end
end

local function UpdateEditboxAnchors(self)
	local cvar = (type(self) == 'string' and self) or GetCVar('chatStyle')

	local classic = cvar == 'classic'
	local leftChat = classic and _G.LeftChatPanel
	local panel = 22

	for _, name in ipairs(_G.CHAT_FRAMES) do
		local frame = _G[name]
		local editbox = frame and frame.editBox
		if not editbox then return end
		editbox.chatStyle = cvar
		editbox:ClearAllPoints()

		local anchorTo = leftChat or frame
		local below, belowInside = options.editBoxPosition == 'BELOW_CHAT', options.editBoxPosition == 'BELOW_CHAT_INSIDE'
		if below or belowInside then
			-- local showLeftPanel = E.db.datatexts.panels.LeftChatDataPanel.enable
			editbox:SetPoint('TOPLEFT', anchorTo, 'BOTTOMLEFT', classic and 0 or -2, (classic and (belowInside and 1 or 0) or -5))
			editbox:SetPoint('BOTTOMRIGHT', anchorTo, 'BOTTOMRIGHT', classic and 0 or -2, (classic and (belowInside and 1 or 0) or -5) + (belowInside and panel or -panel))
		else
			local aboveInside = options.editBoxPosition == 'ABOVE_CHAT_INSIDE'
			editbox:SetPoint('BOTTOMLEFT', anchorTo, 'TOPLEFT', classic and (aboveInside and 1 or 0) or -2, (classic and (aboveInside and -1 or 0) or 2))
			editbox:SetPoint('TOPRIGHT', anchorTo, 'TOPRIGHT', classic and (aboveInside and -1 or 0) or 2, (classic and (aboveInside and -1 or 0) or 2) + (aboveInside and -panel or panel))
		end
	end
end

local function RepositionOverflowButton()
	_G.GeneralDockManagerOverflowButton:ClearAllPoints()
	_G.GeneralDockManagerOverflowButton:SetPoint('RIGHT', _G.ChatFrameChannelButton, 'LEFT', -4, 0)
end

local function CreateChatVoicePanel()
	local Holder = CreateFrame('Frame', 'ChatButtonHolder', UIParent)
	Holder:ClearAllPoints()
	Holder:SetPoint('BOTTOMLEFT', _G.LeftChatPanel, 'TOPLEFT', 0, 1)
	Holder:SetSize(30, 86)
    Holder.backdrop = L.CreateBackdrop(Holder)
	Holder.backdrop:SetBackdropColor(options.panelColor.r, options.panelColor.g, options.panelColor.b, options.panelColor.a)
    --create drag frame
    -- rLib:CreateDragFrame(Holder, L.dragFrames, -2, true)

	_G.ChatFrameChannelButton:ClearAllPoints()
	_G.ChatFrameChannelButton:SetPoint('TOP', Holder, 'TOP', 0, -2)

	L.HandleButton(_G.ChatFrameChannelButton, nil, nil, true)
	_G.ChatFrameChannelButton.Icon:SetParent(_G.ChatFrameChannelButton)
	_G.ChatFrameChannelButton.Icon:SetDesaturated(options.desaturateVoiceIcons)
	_G.ChatFrameChannelButton:SetParent(Holder)

	_G.ChatAlertFrame:ClearAllPoints()
	_G.ChatAlertFrame:SetPoint('BOTTOM', _G.ChatFrameChannelButton, 'TOP', 1, 3)
end

local function HandleChatVoiceIcons()
	-- reset this btn disabled in rchat
	_G.ChatFrameChannelButton:HookScript("OnShow", _G.ChatFrameChannelButton.Show)
	_G.ChatFrameChannelButton:Show()

	if options.hideVoiceButtons then
		_G.ChatFrameChannelButton:Hide()
	elseif options.pinVoiceButtons then
		L.HandleButton(_G.ChatFrameChannelButton)
		_G.ChatFrameChannelButton.Icon:SetDesaturated(options.desaturateVoiceIcons)
		_G.ChatFrameChannelButton:ClearAllPoints()
		_G.ChatFrameChannelButton:SetPoint('RIGHT', _G.GeneralDockManager, 'RIGHT', 2, 0)

		RepositionOverflowButton()
	else
		CreateChatVoicePanel()
	end

	if not options.hideVoiceButtons then
		_G.GeneralDockManagerOverflowButtonList:SetFrameStrata('LOW')
		_G.GeneralDockManagerOverflowButtonList:SetFrameLevel(5)
	end

	if not options.pinVoiceButtons then
		_G.GeneralDockManagerOverflowButton:ClearAllPoints()
		_G.GeneralDockManagerOverflowButton:SetPoint('RIGHT', _G.GeneralDockManager, 'RIGHT', -4, 0)
	end
end

local function UpdateFading()
	for _, frameName in ipairs(_G.CHAT_FRAMES) do
		local frame = _G[frameName]
		if frame then
			frame:SetTimeVisible(options.inactivityTimer)
			frame:SetFading(options.fade)
		end
	end
end

local canChangeMessage = function(arg1, id)
	if id and arg1 == '' then return id end
end

local function MessageIsProtected(message)
	return message and (message ~= gsub(message, '(:?|?)|K(.-)|k', canChangeMessage))
end

local function GetOwner(tab)
	if not tab.owner then
		tab.owner = _G[format('ChatFrame%s', tab:GetID())]
	end

	return tab.owner
end

local function GetTab(chat)
	if not chat.tab then
		chat.tab = _G[format('ChatFrame%sTab', chat:GetID())]
	end

	return chat.tab
end

local function ChatFrameTab_SetAlpha(self, _, skip)
	if skip then return end
	local chat = GetOwner(self)
	self:SetAlpha((not chat.isDocked or self.selected) and 1 or 0.6, true)
end

local function ShowBackground(background, show)
	if not background then return end

	if show then
		background.Show = nil
		background:Show()
	else
		L.Kill(background)
	end
end

local function TabOnEnter(tab)
	tab.Text:Show()

	if tab.conversationIcon then
		tab.conversationIcon:Show()
	end

	if not options.hideCopyButton then
		local chat = GetOwner(tab)
		if chat and chat.copyButton and GetMouseFocus() ~= chat.copyButton then
			chat.copyButton:SetAlpha(0.35)
		end
	end
end

local function TabOnLeave(tab)
	tab.Text:Hide()

	if tab.conversationIcon then
		tab.conversationIcon:Hide()
	end

	if not options.hideCopyButton then
		local chat = GetOwner(tab)
		if chat and chat.copyButton and GetMouseFocus() ~= chat.copyButton then
			chat.copyButton:SetAlpha(0)
		end
	end
end

local function ChatOnEnter(chat)
	TabOnEnter(GetTab(chat))
end

local function ChatOnLeave(chat)
	TabOnLeave(GetTab(chat))
end

local function IsUndocked(chat, docker)
	if not docker then docker = _G.GeneralDockManager.primary end

	local primaryUndocked = docker ~= rChatPlus.LeftChatWindow and docker ~= rChatPlus.RightChatWindow
	return not chat.isDocked or (primaryUndocked and ((chat == docker) or GetDockerParent(docker, chat)))
end

local function HandleFadeTabs(chat, hook)
	local tab = GetTab(chat)

	if hook then
        chat:SetScript('OnEnter', ChatOnEnter)
        chat:SetScript('OnLeave', ChatOnLeave)

		tab:SetScript('OnEnter', TabOnEnter)
        tab:SetScript('OnLeave', TabOnLeave)
	else
        chat:SetScript('OnEnter', nil)
        chat:SetScript('OnLeave', nil)

		tab:SetScript('OnEnter', nil)
        tab:SetScript('OnLeave', nil)
	end

	local focus = GetMouseFocus()
	if not hook then
		TabOnEnter(tab)
	elseif focus ~= tab and focus ~= chat then
		TabOnLeave(tab)
	end
end

local function UpdateChatTab(chat)
	local fadeLeft, fadeRight
	if options.fadeTabsNoBackdrop then
		local both = options.panelBackdrop == 'HIDEBOTH'
		fadeLeft = (both or options.panelBackdrop == 'RIGHT')
		fadeRight = (both or options.panelBackdrop == 'LEFT')
	end

	if chat == rChatPlus.LeftChatWindow then
		GetTab(chat):SetParent(_G.LeftChatPanel or _G.UIParent)
		chat:SetParent(_G.LeftChatPanel or _G.UIParent)

		HandleFadeTabs(chat, fadeLeft)
	elseif chat == rChatPlus.RightChatWindow then
		GetTab(chat):SetParent(_G.RightChatPanel or _G.UIParent)
		chat:SetParent(_G.RightChatPanel or _G.UIParent)

		HandleFadeTabs(chat, fadeRight)
	else
		local docker = _G.GeneralDockManager.primary
		local parent = GetDockerParent(docker, chat)

		-- we need to update the tab parent to mimic the docker
		GetTab(chat):SetParent(parent or _G.UIParent)
		chat:SetParent(parent or _G.UIParent)

		if parent and docker == rChatPlus.LeftChatWindow then
			HandleFadeTabs(chat, fadeLeft)
		elseif parent and docker == rChatPlus.RightChatWindow then
			HandleFadeTabs(chat, fadeRight)
		else
			HandleFadeTabs(chat, options.fadeUndockedTabs and IsUndocked(chat, docker))
		end
	end
end

local function UpdateChatTabs()
	for _, name in ipairs(_G.CHAT_FRAMES) do
		UpdateChatTab(_G[name])
	end
end

local function GetAnchorParents(chat)
	local Left = (chat == rChatPlus.LeftChatWindow and _G.LeftChatPanel)
	local Right = (chat == rChatPlus.RightChatWindow and _G.RightChatPanel)
	local Chat, TabPanel = Left or Right or _G.UIParent

	return TabPanel or Chat, Chat
end

local function ReparentVoiceChatIcon(parent)
	if not parent then
		parent = GetAnchorParents(_G.GeneralDockManager.primary)
	end

	_G.ChatFrameChannelButton:SetParent(parent)
end

local function FindChatWindows()
	if not options.panelSnapping then return end

	local left, right = rChatPlus.LeftChatWindow, rChatPlus.RightChatWindow

	-- they already exist just return them :)
	if left and right then
		return left, right
	end

	local docker = _G.GeneralDockManager.primary
	for _, name in ipairs(_G.CHAT_FRAMES) do
		local chat = _G[name]
		if (chat.isDocked and docker) or chat:IsShown() then
			if not left and L.FramesOverlap(chat, _G.LeftChatPanel) then
				left = chat
			elseif not right and L.FramesOverlap(chat, _G.RightChatPanel) then
				right = chat
			end

			-- if both are found just return now, don't wait
			if left and right then
				return left, right
			end
		end
	end

	-- none or one was found
	return left, right
end

local function PositionChat(chat)
	rChatPlus.LeftChatWindow, rChatPlus.RightChatWindow = FindChatWindows()

	local docker = _G.GeneralDockManager.primary
	if chat == docker then
		local iconParent, chatParent = GetAnchorParents(chat)
		_G.GeneralDockManager:SetParent(chatParent)

		if options.pinVoiceButtons and not options.hideVoiceButtons then
			ReparentVoiceChatIcon(iconParent or chatParent)
		end
	end

	UpdateChatTab(chat)

	if chat:IsMovable() then
		chat:SetUserPlaced(true)
	end

	if chat.FontStringContainer then
		chat.FontStringContainer:ClearAllPoints()
		chat.FontStringContainer:SetPoint('TOPLEFT', chat, 'TOPLEFT', -1, 1)
		chat.FontStringContainer:SetPoint('BOTTOMRIGHT', chat, 'BOTTOMRIGHT', 1, -1)
	end

	if chat:IsShown() then
		-- that chat font container leaks outside of its frame
		-- we cant clip it, so lets force that leak sooner so
		-- i can position it properly, patch: 8.3.0 ~Simpy
		chat:Hide()
		chat:Show()
	end

	local BASE_OFFSET = 32
	if chat == rChatPlus.LeftChatWindow then
		local LOG_OFFSET = chat:GetID() == 2 and (_G.LeftChatTab:GetHeight() + 4) or 0

		chat:ClearAllPoints()
		chat:SetPoint('BOTTOMLEFT', _G.LeftChatPanel, 'BOTTOMLEFT', 5, 5)
		chat:SetSize(options.panelWidth - 10, options.panelHeight - BASE_OFFSET - LOG_OFFSET)

		ShowBackground(chat.Background, false)
	elseif chat == rChatPlus.RightChatWindow then
		local LOG_OFFSET = chat:GetID() == 2 and (_G.LeftChatTab:GetHeight() + 4) or 0

		chat:ClearAllPoints()
		chat:SetPoint('BOTTOMLEFT', _G.RightChatPanel, 'BOTTOMLEFT', 5, 5)
		chat:SetSize((options.separateSizes and options.panelWidthRight or options.panelWidth) - 10, (options.separateSizes and options.panelHeightRight or options.panelHeight) - BASE_OFFSET - LOG_OFFSET)

		ShowBackground(chat.Background, false)
	else -- show if: not docked, or ChatFrame1, or attached to ChatFrame1
		ShowBackground(chat.Background, IsUndocked(chat, docker))
	end
end

local function PositionChats()
	for _, name in ipairs(_G.CHAT_FRAMES) do
		PositionChat(_G[name])
	end
end

local copyLines = {}
function GetLines(frame)
	local index = 1
	for i = 1, frame:GetNumMessages() do
		local message, r, g, b = frame:GetMessageInfo(i)
		if message and not MessageIsProtected(message) then
			--Set fallback color values
			r, g, b = r or 1, g or 1, b or 1

			copyLines[index] = message
			index = index + 1
		end
	end

	return index - 1
end

local function CopyChat(frame)
	if not _G.CopyChatFrame:IsShown() then
		local _, fontSize = _G.FCF_GetChatWindowInfo(frame:GetID())
		if fontSize < 10 then fontSize = 12 end
		_G.FCF_SetChatWindowFontSize(frame, frame, 0.01)
		_G.CopyChatFrame:Show()
		local lineCt = GetLines(frame)
		local text = tconcat(copyLines, ' \n', 1, lineCt)
		_G.FCF_SetChatWindowFontSize(frame, frame, fontSize)
		_G.CopyChatFrameEditBox:SetText(text)
	else
		_G.CopyChatFrame:Hide()
	end
end

local function EmoteButtonOnMouseUp(self, btn)
	local chat = self:GetParent()
	L.ToggleEmoteTable()
end

local function EmoteButtonOnEnter(self)
	self:SetAlpha(1)
	self.texture:SetDesaturated(false)
end

local function EmoteButtonOnLeave(self)
	local chat = self:GetParent()
	if _G[chat:GetName()..'TabText']:IsShown() then
		self:SetAlpha(0.35)
	else
		self:SetAlpha(0)
	end
	self.texture:SetDesaturated(true)
end

local function CopyButtonOnMouseUp(self, btn)
	local chat = self:GetParent()
	if btn == 'RightButton' and chat:GetID() == 1 then
		ToggleFrame(_G.ChatMenu)
	else
		CopyChat(chat)
	end
end

local function CopyButtonOnEnter(self)
	self:SetAlpha(1)
end

local function CopyButtonOnLeave(self)
	local chat = self:GetParent()
	if _G[chat:GetName()..'TabText']:IsShown() then
		self:SetAlpha(0.35)
	else
		self:SetAlpha(0)
	end
end


local function ToggleChatButton(button)
	if button then
		button:SetShown(not options.hideCopyButton)
	end
end

local function Panels_ColorUpdate()
	local panelColor = options.panelColor
	_G.LeftChatPanel.backdrop:SetBackdropColor(panelColor.r, panelColor.g, panelColor.b, panelColor.a)

	if _G.ChatButtonHolder then
		_G.ChatButtonHolder:SetBackdropColor(panelColor.r, panelColor.g, panelColor.b, panelColor.a)
	end
end

local function StyleChat(frame)
	local name = frame:GetName()
	local tab = GetTab(frame)

	frame:SetTimeVisible(options.inactivityTimer)
	frame:SetMaxLines(options.maxLines)
	frame:SetFading(options.fade)

	if frame.styled then return end

	frame:SetFrameLevel(4)
	frame:SetClampRectInsets(0,0,0,0)
	frame:SetClampedToScreen(false)
	L.StripTextures(frame, true)

	L.Kill(_G[name..'ButtonFrame'])

	local editbox = frame.editBox

	for _, texName in pairs(tabTexs) do
		_G[name..'Tab'..texName..'Left']:SetTexture()
		_G[name..'Tab'..texName..'Middle']:SetTexture()
		_G[name..'Tab'..texName..'Right']:SetTexture()
	end

	hooksecurefunc(tab, 'SetAlpha', ChatFrameTab_SetAlpha)

	if not tab.left then tab.left = _G[name..'TabLeft'] end
	tab.Text:ClearAllPoints()
	tab.Text:SetPoint('LEFT', tab, 'LEFT', tab.left:GetWidth(), 0)
	tab:SetHeight(22)

	if tab.conversationIcon then
		tab.conversationIcon:ClearAllPoints()
		tab.conversationIcon:SetPoint('RIGHT', tab.Text, 'LEFT', -1, 0)
	end

	--local a, b, c = select(6, editbox:GetRegions()); a:Kill(); b:Kill(); c:Kill()
	L.Kill(_G[name..'EditBoxLeft'])
	L.Kill(_G[name..'EditBoxMid'])
	L.Kill(_G[name..'EditBoxRight'])

	editbox:SetAltArrowKeyMode(options.useAltKey)

	--copy chat button
	local copyButton = CreateFrame('Frame', format('%sCopyChatButton%d', A, id), frame)
	copyButton:EnableMouse(true)
	copyButton:SetAlpha(0.35)
	copyButton:SetSize(20, 22)
	copyButton:SetPoint('TOPRIGHT', 0, -4)
	copyButton:SetFrameLevel(frame:GetFrameLevel() + 5)
	frame.copyButton = copyButton

	local copyTexture = L.CreateIcon(frame.copyButton, 'OVERLAY')
	copyTexture:SetTexture(options.mediapath .. "Copy.tga")
	copyButton.texture = copyTexture

	copyButton:SetScript('OnMouseUp', CopyButtonOnMouseUp)
	copyButton:SetScript('OnEnter', CopyButtonOnEnter)
	copyButton:SetScript('OnLeave', CopyButtonOnLeave)
	ToggleChatButton(copyButton)

	--emote button
	if options.emote.enable then
		local emoteButton = CreateFrame('Frame', format('%sEmoteButton%d', A, id), frame)
		emoteButton:EnableMouse(true)
		emoteButton:SetAlpha(0.35)
		emoteButton:SetSize(20, 20)
		emoteButton:SetPoint('TOPRIGHT', 0, -30)
		emoteButton:SetFrameLevel(frame:GetFrameLevel() + 5)
		frame.emoteButton = emoteButton

		local emoteTexture = L.CreateIcon(frame.emoteButton, 'OVERLAY')
		emoteTexture:SetTexture(options.mediapath .. "Emote.blp")
		emoteTexture:SetDesaturated(true)
		emoteButton.texture = emoteTexture

		emoteButton:SetScript('OnMouseUp', EmoteButtonOnMouseUp)
		emoteButton:SetScript('OnEnter', EmoteButtonOnEnter)
		emoteButton:SetScript('OnLeave', EmoteButtonOnLeave)
	end

	local lang = _G[name.."EditBoxLanguage"]
	lang:GetRegions():SetAlpha(0)
	lang:SetPoint("TOPRIGHT", editbox, "TOPRIGHT", -15, -2)
	lang:SetPoint("BOTTOMRIGHT", editbox, "BOTTOMRIGHT", -15, 2)
	-- L.CreateBackdrop(lang)

	frame.styled = true
end

local function SetupChat()
	for _, frameName in ipairs(_G.CHAT_FRAMES) do
		local frame = _G[frameName]
		local id = frame:GetID()
		StyleChat(frame)

		_G.FCFTab_UpdateAlpha(frame)
	end

	local chat = _G.GeneralDockManager.primary
	_G.GeneralDockManager:ClearAllPoints()
	_G.GeneralDockManager:SetPoint('BOTTOMLEFT', chat, 'TOPLEFT', 0, 3)
	_G.GeneralDockManager:SetPoint('BOTTOMRIGHT', chat, 'TOPRIGHT', 0, 3)
	_G.GeneralDockManager:SetHeight(22)
	_G.GeneralDockManagerScrollFrame:SetHeight(22)
	_G.GeneralDockManagerScrollFrameChild:SetHeight(22)

	PositionChats()

	if not rChatPlus.HookSecured then
        hooksecurefunc("FCF_OpenTemporaryWindow", SetupChat)
		rChatPlus.HookSecured = true
	end
end

local function UpdateEditboxFont(chatFrame)
	local style = GetCVar('chatStyle')
	if style == 'classic' and rChatPlus.LeftChatWindow then
		chatFrame = rChatPlus.LeftChatWindow
	end

	if chatFrame == _G.GeneralDockManager.primary then
		chatFrame = _G.GeneralDockManager.selected
	end

	local editbox = _G.ChatEdit_ChooseBoxForSend(chatFrame)
	-- the header and text will not update the placement without focus
	if editbox and editbox:IsShown() then
		editbox:SetFocus()
	end
end

local function ChatEdit_SetLastActiveWindow(editbox)
	local style = editbox.chatStyle or GetCVar('chatStyle')
	if style == 'im' then editbox:SetAlpha(0.5) end
end

local function ChatEdit_DeactivateChat(editbox)
	local style = editbox.chatStyle or GetCVar('chatStyle')
	if style == 'im' then editbox:Hide() end
end

local function ChatEdit_ActivateChat(editbox)
	if editbox and editbox.chatFrame then
		UpdateEditboxFont(editbox.chatFrame)
	end
end

local function ChatEdit_OnEnterPressed(editBox)
	-- editBox:ClearHistory() -- we will use our own editbox history so keeping them populated on blizzards end is pointless

	local chatType = editBox:GetAttribute('chatType')
	local chatFrame = chatType and editBox:GetParent()
	if chatFrame and (not chatFrame.isTemporary) and (_G.ChatTypeInfo[chatType].sticky == 1) then
		if not options.sticky then chatType = 'SAY' end
		editBox:SetAttribute('chatType', chatType)
	end
end

local function GetCombatLog()
	local LOG = _G.ChatFrame2 -- ChatFrame2
	if LOG then return LOG, GetTab(LOG) end
end

local function FCFDock_UpdateTabs(dock)
	if dock == _G.GeneralDockManager then
		local logchat, logchattab = GetCombatLog()
		dock.scrollFrame:ClearAllPoints()
		dock.scrollFrame:SetPoint('RIGHT', dock.overflowButton, 'LEFT')
		dock.scrollFrame:SetPoint('TOPLEFT', (logchat.isDocked and logchattab) or GetTab(dock.primary), 'TOPRIGHT')
	end
end

local function FCF_Close(chat)
	-- clear these off when it's closed, used by FCFTab_UpdateColors
	local tab = GetTab(chat)
	tab.whisperName = nil
	tab.classColor = nil
end

local function FCF_SetWindowAlpha(frame, alpha)
	frame.oldAlpha = alpha or 1
end

local function FCFTab_UpdateColors(tab, selected)
	if not tab then return end

	if tab:GetParent() == _G.ChatConfigFrameChatTabManager then
		if selected then
			tab.Text:SetTextColor(1, 1, 1)
		end

		local name = GetChatWindowInfo(tab:GetID())
		if name then
			tab.Text:SetText(name)
		end

		tab:SetAlpha(1) -- for some reason blizzard likes to change the alpha here? idk
	else -- actual chat tab and other
		local chat = GetOwner(tab)
		if not chat then return end

		tab.selected = selected

		local whisper = tab.conversationIcon and chat.chatTarget
		local name = chat.name or UNKNOWN

		if whisper and not tab.whisperName then
			tab.whisperName = gsub(name, '([%S]-)%-[%S]+', '%1|cFF999999*|r')
		end

		if selected then
			if options.tabSelector == 'NONE' then
				tab:SetFormattedText(rChatPlus.TabStyles.NONE, tab.whisperName or name)
			else
				local color = options.tabSelectorColor
				local hexColor = L.RGBToHex(color.r, color.g, color.b)
				tab:SetFormattedText(rChatPlus.TabStyles[options.tabSelector] or rChatPlus.TabStyles.ARROW1, hexColor, tab.whisperName or name, hexColor)
			end

			if options.tabSelectedTextEnabled then
				local color = options.tabSelectedTextColor
				tab.Text:SetTextColor(color.r, color.g, color.b)
				return -- using selected text color
			end
		end

		if whisper then
			if not selected then
				tab:SetText(tab.whisperName or name)
			end
		else
			if not selected then
				tab:SetText(name)
			end
		end
	end
end

local function FCFDock_SelectWindow(_, chatFrame)
	if chatFrame then
		UpdateEditboxFont(chatFrame)
	end
end

local function Unsnapped(chat)
	if chat == rChatPlus.LeftChatWindow then
		rChatPlus.LeftChatWindow = nil
	elseif chat == rChatPlus.RightChatWindow then
		rChatPlus.RightChatWindow = nil
	end
end

local function ClearSnapping()
	rChatPlus.LeftChatWindow = nil
	rChatPlus.RightChatWindow = nil
end

local function SnappingChanged(chat)
	Unsnapped(chat)

	if chat == _G.GeneralDockManager.primary then
		for _, frame in ipairs(_G.GeneralDockManager.DOCKED_CHAT_FRAMES) do
			PositionChat(frame)
		end
	else
		PositionChat(chat)
	end
end

local function BuildCopyChatFrame()
	local frame = CreateFrame('Frame', 'CopyChatFrame', UIParent, 'BackdropTemplate')
	tinsert(_G.UISpecialFrames, 'CopyChatFrame')
    frame.backdrop = L.CreateBackdrop(frame)
	frame:SetSize(700, 200)
	frame:SetPoint('CENTER', UIParent, 'CENTER')
	frame:Hide()
	frame:SetMovable(true)
	frame:EnableMouse(true)
	frame:SetResizable(true)
	frame:SetMinResize(350, 100)
	frame:SetScript('OnMouseDown', function(copyChat, button)
		if button == 'LeftButton' and not copyChat.isMoving then
			copyChat:StartMoving()
			copyChat.isMoving = true
		elseif button == 'RightButton' and not copyChat.isSizing then
			copyChat:StartSizing()
			copyChat.isSizing = true
		end
	end)
	frame:SetScript('OnMouseUp', function(copyChat, button)
		if button == 'LeftButton' and copyChat.isMoving then
			copyChat:StopMovingOrSizing()
			copyChat.isMoving = false
		elseif button == 'RightButton' and copyChat.isSizing then
			copyChat:StopMovingOrSizing()
			copyChat.isSizing = false
		end
	end)
	frame:SetScript('OnHide', function(copyChat)
		if copyChat.isMoving or copyChat.isSizing then
			copyChat:StopMovingOrSizing()
			copyChat.isMoving = false
			copyChat.isSizing = false
		end
	end)
	frame:SetFrameStrata('DIALOG')

	local scrollArea = CreateFrame('ScrollFrame', 'CopyChatScrollFrame', frame, 'UIPanelScrollFrameTemplate')
	scrollArea:SetPoint('TOPLEFT', frame, 'TOPLEFT', 8, -30)
	scrollArea:SetPoint('BOTTOMRIGHT', frame, 'BOTTOMRIGHT', -30, 8)
	scrollArea:SetScript('OnSizeChanged', function(scroll)
		_G.CopyChatFrameEditBox:SetWidth(scroll:GetWidth())
		_G.CopyChatFrameEditBox:SetHeight(scroll:GetHeight())
	end)
	scrollArea:SetScript('OnVerticalScroll', function(scroll, offset)
		_G.CopyChatFrameEditBox:SetHitRectInsets(0, 0, offset, (_G.CopyChatFrameEditBox:GetHeight() - offset - scroll:GetHeight()))
	end)

	local editBox = CreateFrame('EditBox', 'CopyChatFrameEditBox', frame)
	editBox:SetMultiLine(true)
	editBox:SetMaxLetters(99999)
	editBox:EnableMouse(true)
	editBox:SetAutoFocus(false)
	editBox:SetFontObject(_G.ChatFontNormal)
	editBox:SetWidth(scrollArea:GetWidth())
	editBox:SetHeight(200)
	editBox:SetScript('OnEscapePressed', function() _G.CopyChatFrame:Hide() end)
	scrollArea:SetScrollChild(editBox)
	_G.CopyChatFrameEditBox:SetScript('OnTextChanged', function(_, userInput)
		if userInput then return end
		local _, Max = _G.CopyChatScrollFrameScrollBar:GetMinMaxValues()
		for _ = 1, Max do
			_G.ScrollFrameTemplate_OnMouseWheel(_G.CopyChatScrollFrame, -1)
		end
	end)

	local close = CreateFrame('Button', 'CopyChatFrameCloseButton', frame, 'UIPanelCloseButton, BackdropTemplate')
	close:SetPoint('TOPRIGHT')
	close:SetFrameLevel(close:GetFrameLevel() + 1)
	close:EnableMouse(true)
end

function rChatPlus:Initialize()
	SetupChat()
	UpdateFading()
	Panels_ColorUpdate()
	HandleChatVoiceIcons()
	UpdateEditboxAnchors()

    hooksecurefunc("ChatEdit_SetLastActiveWindow", ChatEdit_SetLastActiveWindow)
    hooksecurefunc("ChatEdit_DeactivateChat", ChatEdit_DeactivateChat)
    hooksecurefunc("ChatEdit_ActivateChat", ChatEdit_ActivateChat)
    hooksecurefunc("ChatEdit_OnEnterPressed", ChatEdit_OnEnterPressed)
    hooksecurefunc("FCFDock_UpdateTabs", FCFDock_UpdateTabs)
    hooksecurefunc("FCF_Close", FCF_Close)
    hooksecurefunc("FCF_SetWindowAlpha", FCF_SetWindowAlpha)
    hooksecurefunc("FCFTab_UpdateColors", FCFTab_UpdateColors)
    hooksecurefunc('FCFDock_SelectWindow', FCFDock_SelectWindow)
	hooksecurefunc('FCF_SavePositionAndDimensions', SnappingChanged)
	hooksecurefunc('FCF_UnDockFrame', SnappingChanged)
	hooksecurefunc('FCF_DockFrame', SnappingChanged)
	hooksecurefunc('FCF_ResetChatWindows', ClearSnapping)
	hooksecurefunc('RedockChatWindows', ClearSnapping)

	self:RegisterEvent('UPDATE_CHAT_WINDOWS')
	self:RegisterEvent('UPDATE_FLOATING_CHAT_WINDOWS')
    self:SetScript('OnEvent', function(self, event)
        if event == 'UPDATE_CHAT_WINDOWS' or event == 'UPDATE_FLOATING_CHAT_WINDOWS' then
            SetupChat()
        end
    end)

	BuildCopyChatFrame()

	-- Editbox Backdrop Color
	hooksecurefunc('ChatEdit_UpdateHeader', function(editbox)
		local chatType = editbox:GetAttribute('chatType')
		if not chatType then return end

		local ChatTypeInfo = _G.ChatTypeInfo
		local info = ChatTypeInfo[chatType]
		local chanTarget = editbox:GetAttribute('channelTarget')
		local chanName = chanTarget and GetChannelName(chanTarget)

		--Increase inset on right side to make room for character count text
		local insetLeft, insetRight, insetTop, insetBottom = editbox:GetTextInsets()
		editbox:SetTextInsets(insetLeft, insetRight + 30, insetTop, insetBottom)

		if not editbox.backdrop then
            editbox.backdrop = L.CreateBackdrop(editbox)
		end

		if chanName and (chatType == 'CHANNEL') then
			if chanName == 0 then
				editbox.backdrop:SetBackdropBorderColor(unpack(options.backdrop.edgeColor))
			else
				info = ChatTypeInfo[chatType..chanName]
				editbox.backdrop:SetBackdropBorderColor(info.r, info.g, info.b)
			end
		else
			editbox.backdrop:SetBackdropBorderColor(info.r, info.g, info.b)
		end
	end)
end