local d={}local D=game:GetService("\080\108\097\121\101\114\115")local P=game:GetService("\067\104\097\116")local q=D.LocalPlayer local B=q:WaitForChild("\080\108\097\121\101\114\071\117\105")local y=640 local b=1024 local R=1 local m=2 local l=3 local S=script.Parent.Parent.Parent local r=game:GetService("\067\104\097\116")local h=S:WaitForChild("\067\108\105\101\110\116\067\104\097\116\077\111\100\117\108\101\115")local E=script.Parent local W=require(E:WaitForChild("\067\104\097\116\067\104\097\110\110\101\108"))local s=require(h:WaitForChild("\067\104\097\116\083\101\116\116\105\110\103\115"))local G=require(E:WaitForChild("\067\117\114\118\101\085\116\105\108"))local f={}f.__index=f function getClassicChatEnabled()if s.ClassicChatEnabled~=nil then return s.ClassicChatEnabled end return D.ClassicChat end function getBubbleChatEnabled()if s.BubbleChatEnabled~=nil then return s.BubbleChatEnabled end return D.BubbleChat end function bubbleChatOnly()return not getClassicChatEnabled()and getBubbleChatEnabled()end function mergeProps(d,D)if not d or not D then return end for d,P in pairs(d)do if D[d]~=nil then D[d]=P end end end function f.CreateGuiObjects(D,d)local P=Instance.new("\070\114\097\109\101")P.BackgroundTransparency=1 P.Active=s.WindowDraggable P.Parent=d P.AutoLocalize=false local q=Instance.new("\070\114\097\109\101")q.Selectable=false q.Name="\067\104\097\116\066\097\114\080\097\114\101\110\116\070\114\097\109\101"q.BackgroundTransparency=1 q.Parent=P local S=Instance.new("\070\114\097\109\101")S.Selectable=false S.Name="\067\104\097\110\110\101\108\115\066\097\114\080\097\114\101\110\116\070\114\097\109\101"S.BackgroundTransparency=1 S.Position=UDim2.new(0,0,0,0)S.Parent=P local r=Instance.new("\070\114\097\109\101")r.Selectable=false r.Name="\067\104\097\116\067\104\097\110\110\101\108\080\097\114\101\110\116\070\114\097\109\101"r.BackgroundTransparency=1 r.BackgroundColor3=s.BackGroundColor r.BackgroundTransparency=.6 r.BorderSizePixel=0 r.Parent=P local h=Instance.new("\073\109\097\103\101\066\117\116\116\111\110")h.Selectable=false h.Image=""h.BackgroundTransparency=.6 h.BorderSizePixel=0 h.Visible=false h.BackgroundColor3=s.BackGroundColor h.Active=true if bubbleChatOnly()then h.Position=UDim2.new(1,-h.AbsoluteSize.X,0,0)else h.Position=UDim2.new(1,-h.AbsoluteSize.X,1,-h.AbsoluteSize.Y)end h.Parent=P local E=Instance.new("\073\109\097\103\101\076\097\098\101\108")E.Selectable=false E.Size=UDim2.new(.8,0,.8,0)E.Position=UDim2.new(.2,0,.2,0)E.BackgroundTransparency=1 E.Image="\114\098\120\097\115\115\101\116\105\100\058\047\047\050\054\049\056\056\048\055\052\051"E.Parent=h local function W()local d=P while d and not d:IsA("\083\099\114\101\101\110\071\117\105")do d=d.Parent end return d end local G=l local f=W()if f.AbsoluteSize.X<=y then G=R elseif f.AbsoluteSize.X<=b then G=m end local c=false local function N()if c then return end c=true if not P:IsDescendantOf(B)then return end local d=W()local D=s.MinimumWindowSize local y=s.MaximumWindowSize local b=S.AbsoluteSize.Y+q.AbsoluteSize.Y local R=D.X.Scale*d.AbsoluteSize.X+D.X.Offset local m=math.max(D.Y.Scale*d.AbsoluteSize.Y+D.Y.Offset,b)local l=y.X.Scale*d.AbsoluteSize.X+y.X.Offset local r=y.Y.Scale*d.AbsoluteSize.Y+y.Y.Offset local h=P.AbsoluteSize.X local E=P.AbsoluteSize.Y if h<R then local d=UDim2.new(0,R-h,0,0)P.Size=P.Size+d elseif h>l then local d=UDim2.new(0,l-h,0,0)P.Size=P.Size+d end if E<m then local d=UDim2.new(0,0,0,m-E)P.Size=P.Size+d elseif E>r then local d=UDim2.new(0,0,0,r-E)P.Size=P.Size+d end local G=P.AbsoluteSize.X/d.AbsoluteSize.X local f=P.AbsoluteSize.Y/d.AbsoluteSize.Y P.Size=UDim2.new(G,0,f,0)c=false end P.Changed:connect(function(d)if d=="\065\098\115\111\108\117\116\101\083\105\122\101"then N()end end)h.DragBegin:connect(function(d)P.Draggable=false end)local function n(d)if s.WindowDraggable==false and s.WindowResizable==false then return end local D=(d-P.AbsolutePosition)+h.AbsoluteSize P.Size=UDim2.new(0,D.X,0,D.Y)if bubbleChatOnly()then h.Position=UDim2.new(1,-h.AbsoluteSize.X,0,0)else h.Position=UDim2.new(1,-h.AbsoluteSize.X,1,-h.AbsoluteSize.Y)end end h.DragStopped:connect(function(d,D)P.Draggable=s.WindowDraggable end)local L=false h.Changed:connect(function(d)if d=="\065\098\115\111\108\117\116\101\080\111\115\105\116\105\111\110"and not P.Draggable then if L then return end L=true n(h.AbsolutePosition)L=false end end)local function Z(d)if G==R then d=d or s.ChatChannelsTabTextSizePhone else d=d or s.ChatChannelsTabTextSize end local D=d local P=math.max(32,D+8)+2 return P end local function x(d)if G==R then d=d or s.ChatBarTextSizePhone else d=d or s.ChatBarTextSize end local D=d local P=(D+14)+10 return P end if bubbleChatOnly()then q.Position=UDim2.new(0,0,0,0)S.Visible=false S.Active=false r.Visible=false r.Active=false local d=0 local D=0 local B=W()if G==R then d=s.DefaultWindowSizePhone.X.Scale D=s.DefaultWindowSizePhone.X.Offset elseif G==m then d=s.DefaultWindowSizeTablet.X.Scale D=s.DefaultWindowSizeTablet.X.Offset else d=s.DefaultWindowSizeDesktop.X.Scale D=s.DefaultWindowSizeDesktop.X.Offset end local y=x()P.Size=UDim2.new(d,D,0,y)P.Position=s.DefaultWindowPosition else local d=W()if G==R then P.Size=s.DefaultWindowSizePhone elseif G==m then P.Size=s.DefaultWindowSizeTablet else P.Size=s.DefaultWindowSizeDesktop end P.Position=s.DefaultWindowPosition end if G==R then s.ChatWindowTextSize=s.ChatWindowTextSizePhone s.ChatChannelsTabTextSize=s.ChatChannelsTabTextSizePhone s.ChatBarTextSize=s.ChatBarTextSizePhone end local function a(d)P.Active=d P.Draggable=d end local function k(d)h.Visible=d h.Draggable=d local D=q.Size.Y.Offset if d then q.Size=UDim2.new(1,-D-2,0,D)if not bubbleChatOnly()then q.Position=UDim2.new(0,0,1,-D)end else q.Size=UDim2.new(1,0,0,D)if not bubbleChatOnly()then q.Position=UDim2.new(0,0,1,-D)end end end local function Y()local d=Z()local D=x()if s.ShowChannelsBar then r.Size=UDim2.new(1,0,1,-(((d+D)+2)+2))r.Position=UDim2.new(0,0,0,d+2)else r.Size=UDim2.new(1,0,1,-((D+2)+2))r.Position=UDim2.new(0,0,0,2)end end local function V(d)local D=Z(d)S.Size=UDim2.new(1,0,0,D)Y()end local function w(d)local D=x(d)q.Size=UDim2.new(1,0,0,D)if not bubbleChatOnly()then q.Position=UDim2.new(0,0,1,-D)end h.Size=UDim2.new(0,D,0,D)h.Position=UDim2.new(1,-D,1,-D)Y()k(s.WindowResizable)end local function T(d)S.Visible=d Y()end V(s.ChatChannelsTabTextSize)w(s.ChatBarTextSize)a(s.WindowDraggable)k(s.WindowResizable)T(s.ShowChannelsBar)s.SettingsChanged:connect(function(d,D)if d=="\087\105\110\100\111\119\068\114\097\103\103\097\098\108\101"then a(D)elseif d=="\087\105\110\100\111\119\082\101\115\105\122\097\098\108\101"then k(D)elseif d=="\067\104\097\116\067\104\097\110\110\101\108\115\084\097\098\084\101\120\116\083\105\122\101"then V(D)elseif d=="\067\104\097\116\066\097\114\084\101\120\116\083\105\122\101"then w(D)elseif d=="\083\104\111\119\067\104\097\110\110\101\108\115\066\097\114"then T(D)end end)D.GuiObject=P D.GuiObjects.BaseFrame=P D.GuiObjects.ChatBarParentFrame=q D.GuiObjects.ChannelsBarParentFrame=S D.GuiObjects.ChatChannelParentFrame=r D.GuiObjects.ChatResizerFrame=h D.GuiObjects.ResizeIcon=E D:AnimGuiObjects()end function f.GetChatBar(d)return d.ChatBar end function f.RegisterChatBar(D,d)D.ChatBar=d D.ChatBar:CreateGuiObjects(D.GuiObjects.ChatBarParentFrame)end function f.RegisterChannelsBar(D,d)D.ChannelsBar=d D.ChannelsBar:CreateGuiObjects(D.GuiObjects.ChannelsBarParentFrame)end function f.RegisterMessageLogDisplay(D,d)D.MessageLogDisplay=d D.MessageLogDisplay.GuiObject.Parent=D.GuiObjects.ChatChannelParentFrame end function f.AddChannel(D,d)if D:GetChannel(d)then error("\067\104\097\110\110\101\108 \'"..(d.."\' \097\108\114\101\097\100\121 \101\120\105\115\116\115\033"))return end local P=W.new(d,D.MessageLogDisplay)D.Channels[d:lower()]=P P:SetActive(false)local q=D.ChannelsBar:AddChannelTab(d)q.NameTag.MouseButton1Click:connect(function()D:SwitchCurrentChannel(d)end)P:RegisterChannelTab(q)return P end function f.GetFirstChannel(d)for d,D in pairs(d.Channels)do return D end return nil end function f.RemoveChannel(D,d)if not D:GetChannel(d)then error("\067\104\097\110\110\101\108 \'"..(d.."\' \100\111\101\115 \110\111\116 \101\120\105\115\116\033"))end local P=d:lower()local q=false if D.Channels[P]==D:GetCurrentChannel()then q=true D:SwitchCurrentChannel(nil)end D.Channels[P]:Destroy()D.Channels[P]=nil D.ChannelsBar:RemoveChannelTab(d)if q then local d=D:GetChannel(s.GeneralChannelName)~=nil local q=P==s.GeneralChannelName:lower()local B=nil if d and not q then B=s.GeneralChannelName else local d=D:GetFirstChannel()B=d and d.Name or nil end D:SwitchCurrentChannel(B)end if not s.ShowChannelsBar then if D.ChatBar.TargetChannel==d then D.ChatBar:SetChannelTarget(s.GeneralChannelName)end end end function f.GetChannel(D,d)return d and D.Channels[d:lower()]or nil end function f.GetTargetMessageChannel(d)if not s.ShowChannelsBar then return d.ChatBar.TargetChannel else local D=d:GetCurrentChannel()return D and D.Name end end function f.GetCurrentChannel(d)return d.CurrentChannel end function f.SwitchCurrentChannel(D,d)if not s.ShowChannelsBar then local P=D:GetChannel(d)if P then D.ChatBar:SetChannelTarget(P.Name)end d=s.GeneralChannelName end local P=D:GetCurrentChannel()local q=D:GetChannel(d)if q==nil then error(string.format("\067\104\097\110\110\101\108 \'\037\115\' \100\111\101\115 \110\111\116 \101\120\105\115\116\046",d))end if q~=P then if P then P:SetActive(false)local d=D.ChannelsBar:GetChannelTab(P.Name)d:SetActive(false)end if q then q:SetActive(true)local d=D.ChannelsBar:GetChannelTab(q.Name)d:SetActive(true)end D.CurrentChannel=q end end function f.UpdateFrameVisibility(d)d.GuiObject.Visible=d.Visible and d.CoreGuiEnabled end function f.GetVisible(d)return d.Visible end function f.SetVisible(D,d)D.Visible=d D:UpdateFrameVisibility()end function f.GetCoreGuiEnabled(d)return d.CoreGuiEnabled end function f.SetCoreGuiEnabled(D,d)D.CoreGuiEnabled=d D:UpdateFrameVisibility()end function f.EnableResizable(d)d.GuiObjects.ChatResizerFrame.Active=true end function f.DisableResizable(d)d.GuiObjects.ChatResizerFrame.Active=false end function f.FadeOutBackground(D,d)D.ChannelsBar:FadeOutBackground(d)D.MessageLogDisplay:FadeOutBackground(d)D.ChatBar:FadeOutBackground(d)D.AnimParams.Background_TargetTransparency=1 D.AnimParams.Background_NormalizedExptValue=G:NormalizedDefaultExptValueInSeconds(d)end function f.FadeInBackground(D,d)D.ChannelsBar:FadeInBackground(d)D.MessageLogDisplay:FadeInBackground(d)D.ChatBar:FadeInBackground(d)D.AnimParams.Background_TargetTransparency=.6 D.AnimParams.Background_NormalizedExptValue=G:NormalizedDefaultExptValueInSeconds(d)end function f.FadeOutText(D,d)D.MessageLogDisplay:FadeOutText(d)D.ChannelsBar:FadeOutText(d)end function f.FadeInText(D,d)D.MessageLogDisplay:FadeInText(d)D.ChannelsBar:FadeInText(d)end function f.AnimGuiObjects(d)d.GuiObjects.ChatChannelParentFrame.BackgroundTransparency=d.AnimParams.Background_CurrentTransparency d.GuiObjects.ChatResizerFrame.BackgroundTransparency=d.AnimParams.Background_CurrentTransparency d.GuiObjects.ResizeIcon.ImageTransparency=d.AnimParams.Background_CurrentTransparency end function f.InitializeAnimParams(d)d.AnimParams.Background_TargetTransparency=.6 d.AnimParams.Background_CurrentTransparency=.6 d.AnimParams.Background_NormalizedExptValue=G:NormalizedDefaultExptValueInSeconds(0)end function f.Update(D,d)D.ChatBar:Update(d)D.ChannelsBar:Update(d)D.MessageLogDisplay:Update(d)D.AnimParams.Background_CurrentTransparency=G:Expt(D.AnimParams.Background_CurrentTransparency,D.AnimParams.Background_TargetTransparency,D.AnimParams.Background_NormalizedExptValue,d)D:AnimGuiObjects()end function d.new()local d=setmetatable({},f)d.GuiObject=nil d.GuiObjects={}d.ChatBar=nil d.ChannelsBar=nil d.MessageLogDisplay=nil d.Channels={}d.CurrentChannel=nil d.Visible=true d.CoreGuiEnabled=true d.AnimParams={}d:InitializeAnimParams()return d end return d
--!nonstrict
--	// FileName: ChatWindow.lua
--	// Written by: Xsitsu
--	// Description: Main GUI window piece. Manages ChatBar, ChannelsBar, and ChatChannels.

local module = {}

local Players = game:GetService("Players")
local Chat = game:GetService("Chat")
local LocalPlayer = Players.LocalPlayer
local PlayerGui = LocalPlayer:WaitForChild("PlayerGui")

local PHONE_SCREEN_WIDTH = 640
local TABLET_SCREEN_WIDTH = 1024

local DEVICE_PHONE = 1
local DEVICE_TABLET = 2
local DEVICE_DESKTOP = 3

--////////////////////////////// Include
--//////////////////////////////////////
local chatFolder = script.Parent.Parent.Parent

local Chat = game:GetService("Chat")
local clientChatModules = chatFolder:WaitForChild("ClientChatModules")
local modulesFolder = script.Parent
local moduleChatChannel = require(modulesFolder:WaitForChild("ChatChannel"))
local ChatSettings = require(clientChatModules:WaitForChild("ChatSettings"))
local CurveUtil = require(modulesFolder:WaitForChild("CurveUtil"))

--////////////////////////////// Methods
--//////////////////////////////////////
local methods = {}
methods.__index = methods

function getClassicChatEnabled()
	if ChatSettings.ClassicChatEnabled ~= nil then
		return ChatSettings.ClassicChatEnabled
	end
	return Players.ClassicChat
end

function getBubbleChatEnabled()
	if ChatSettings.BubbleChatEnabled ~= nil then
		return ChatSettings.BubbleChatEnabled
	end
	return Players.BubbleChat
end

function bubbleChatOnly()
	return not getClassicChatEnabled() and getBubbleChatEnabled()
end

-- only merge property defined on target
function mergeProps(source, target)
	if not source or not target then
		return
	end
	for prop, value in pairs(source) do
		if target[prop] ~= nil then
			target[prop] = value
		end
	end
end

function methods:CreateGuiObjects(targetParent)
	--[[local userDefinedChatWindowStyle
	pcall(function()
		userDefinedChatWindowStyle = Chat:InvokeChatCallback(Enum.ChatCallbackType.OnCreatingChatWindow, nil)
	end)

	-- merge the userdefined settings with the ChatSettings
	mergeProps(userDefinedChatWindowStyle, ChatSettings)]]

	local BaseFrame = Instance.new("Frame")
	BaseFrame.BackgroundTransparency = 1
	BaseFrame.Active = ChatSettings.WindowDraggable
	BaseFrame.Parent = targetParent
	BaseFrame.AutoLocalize = false

	local ChatBarParentFrame = Instance.new("Frame")
	ChatBarParentFrame.Selectable = false
	ChatBarParentFrame.Name = "ChatBarParentFrame"
	ChatBarParentFrame.BackgroundTransparency = 1
	ChatBarParentFrame.Parent = BaseFrame

	local ChannelsBarParentFrame = Instance.new("Frame")
	ChannelsBarParentFrame.Selectable = false
	ChannelsBarParentFrame.Name = "ChannelsBarParentFrame"
	ChannelsBarParentFrame.BackgroundTransparency = 1
	ChannelsBarParentFrame.Position = UDim2.new(0, 0, 0, 0)
	ChannelsBarParentFrame.Parent = BaseFrame

	local ChatChannelParentFrame = Instance.new("Frame")
	ChatChannelParentFrame.Selectable = false
	ChatChannelParentFrame.Name = "ChatChannelParentFrame"
	ChatChannelParentFrame.BackgroundTransparency = 1
	ChatChannelParentFrame.BackgroundColor3 = ChatSettings.BackGroundColor
	ChatChannelParentFrame.BackgroundTransparency = 0.6
	ChatChannelParentFrame.BorderSizePixel = 0
	ChatChannelParentFrame.Parent = BaseFrame

	local ChatResizerFrame = Instance.new("ImageButton")
	ChatResizerFrame.Selectable = false
	ChatResizerFrame.Image = ""
	ChatResizerFrame.BackgroundTransparency = 0.6
	ChatResizerFrame.BorderSizePixel = 0
	ChatResizerFrame.Visible = false
	ChatResizerFrame.BackgroundColor3 = ChatSettings.BackGroundColor
	ChatResizerFrame.Active = true
	if bubbleChatOnly() then
		ChatResizerFrame.Position = UDim2.new(1, -ChatResizerFrame.AbsoluteSize.X, 0, 0)
	else
		ChatResizerFrame.Position = UDim2.new(1, -ChatResizerFrame.AbsoluteSize.X, 1, -ChatResizerFrame.AbsoluteSize.Y)
	end
	ChatResizerFrame.Parent = BaseFrame

	local ResizeIcon = Instance.new("ImageLabel")
	ResizeIcon.Selectable = false
	ResizeIcon.Size = UDim2.new(0.8, 0, 0.8, 0)
	ResizeIcon.Position = UDim2.new(0.2, 0, 0.2, 0)
	ResizeIcon.BackgroundTransparency = 1
	ResizeIcon.Image = "rbxassetid://261880743"
	ResizeIcon.Parent = ChatResizerFrame

	local function GetScreenGuiParent()
		--// Travel up parent list until you find the ScreenGui that the chat window is parented to
		local screenGuiParent = BaseFrame
		while screenGuiParent and not screenGuiParent:IsA("ScreenGui") do
			screenGuiParent = screenGuiParent.Parent
		end

		return screenGuiParent
	end

	local deviceType = DEVICE_DESKTOP

	local screenGuiParent = GetScreenGuiParent()
	if screenGuiParent.AbsoluteSize.X <= PHONE_SCREEN_WIDTH then
		deviceType = DEVICE_PHONE
	elseif screenGuiParent.AbsoluteSize.X <= TABLET_SCREEN_WIDTH then
		deviceType = DEVICE_TABLET
	end

	local checkSizeLock = false
	local function doCheckSizeBounds()
		if checkSizeLock then
			return
		end
		checkSizeLock = true

		if not BaseFrame:IsDescendantOf(PlayerGui) then
			return
		end

		local screenGuiParent = GetScreenGuiParent()

		local minWinSize = ChatSettings.MinimumWindowSize
		local maxWinSize = ChatSettings.MaximumWindowSize

		local forceMinY = ChannelsBarParentFrame.AbsoluteSize.Y + ChatBarParentFrame.AbsoluteSize.Y

		local minSizePixelX = (minWinSize.X.Scale * screenGuiParent.AbsoluteSize.X) + minWinSize.X.Offset
		local minSizePixelY =
			math.max((minWinSize.Y.Scale * screenGuiParent.AbsoluteSize.Y) + minWinSize.Y.Offset, forceMinY)

		local maxSizePixelX = (maxWinSize.X.Scale * screenGuiParent.AbsoluteSize.X) + maxWinSize.X.Offset
		local maxSizePixelY = (maxWinSize.Y.Scale * screenGuiParent.AbsoluteSize.Y) + maxWinSize.Y.Offset

		local absSizeX = BaseFrame.AbsoluteSize.X
		local absSizeY = BaseFrame.AbsoluteSize.Y

		if absSizeX < minSizePixelX then
			local offset = UDim2.new(0, minSizePixelX - absSizeX, 0, 0)
			BaseFrame.Size = BaseFrame.Size + offset
		elseif absSizeX > maxSizePixelX then
			local offset = UDim2.new(0, maxSizePixelX - absSizeX, 0, 0)
			BaseFrame.Size = BaseFrame.Size + offset
		end

		if absSizeY < minSizePixelY then
			local offset = UDim2.new(0, 0, 0, minSizePixelY - absSizeY)
			BaseFrame.Size = BaseFrame.Size + offset
		elseif absSizeY > maxSizePixelY then
			local offset = UDim2.new(0, 0, 0, maxSizePixelY - absSizeY)
			BaseFrame.Size = BaseFrame.Size + offset
		end

		local xScale = BaseFrame.AbsoluteSize.X / screenGuiParent.AbsoluteSize.X
		local yScale = BaseFrame.AbsoluteSize.Y / screenGuiParent.AbsoluteSize.Y

		BaseFrame.Size = UDim2.new(xScale, 0, yScale, 0)

		checkSizeLock = false
	end

	BaseFrame.Changed:connect(function(prop)
		if prop == "AbsoluteSize" then
			doCheckSizeBounds()
		end
	end)

	ChatResizerFrame.DragBegin:connect(function(startUdim)
		BaseFrame.Draggable = false
	end)

	local function UpdatePositionFromDrag(atPos)
		if ChatSettings.WindowDraggable == false and ChatSettings.WindowResizable == false then
			return
		end
		local newSize = atPos - BaseFrame.AbsolutePosition + ChatResizerFrame.AbsoluteSize
		BaseFrame.Size = UDim2.new(0, newSize.X, 0, newSize.Y)
		if bubbleChatOnly() then
			ChatResizerFrame.Position = UDim2.new(1, -ChatResizerFrame.AbsoluteSize.X, 0, 0)
		else
			ChatResizerFrame.Position =
				UDim2.new(1, -ChatResizerFrame.AbsoluteSize.X, 1, -ChatResizerFrame.AbsoluteSize.Y)
		end
	end

	ChatResizerFrame.DragStopped:connect(function(endX, endY)
		BaseFrame.Draggable = ChatSettings.WindowDraggable
		--UpdatePositionFromDrag(Vector2.new(endX, endY))
	end)

	local resizeLock = false
	ChatResizerFrame.Changed:connect(function(prop)
		if prop == "AbsolutePosition" and not BaseFrame.Draggable then
			if resizeLock then
				return
			end
			resizeLock = true

			UpdatePositionFromDrag(ChatResizerFrame.AbsolutePosition)

			resizeLock = false
		end
	end)

	local function CalculateChannelsBarPixelSize(textSize)
		if deviceType == DEVICE_PHONE then
			textSize = textSize or ChatSettings.ChatChannelsTabTextSizePhone
		else
			textSize = textSize or ChatSettings.ChatChannelsTabTextSize
		end

		local channelsBarTextYSize = textSize
		local chatChannelYSize = math.max(32, channelsBarTextYSize + 8) + 2

		return chatChannelYSize
	end

	local function CalculateChatBarPixelSize(textSize)
		if deviceType == DEVICE_PHONE then
			textSize = textSize or ChatSettings.ChatBarTextSizePhone
		else
			textSize = textSize or ChatSettings.ChatBarTextSize
		end

		local chatBarTextSizeY = textSize
		local chatBarYSize = chatBarTextSizeY + (7 * 2) + (5 * 2)

		return chatBarYSize
	end

	if bubbleChatOnly() then
		ChatBarParentFrame.Position = UDim2.new(0, 0, 0, 0)
		ChannelsBarParentFrame.Visible = false
		ChannelsBarParentFrame.Active = false
		ChatChannelParentFrame.Visible = false
		ChatChannelParentFrame.Active = false

		local useXScale = 0
		local useXOffset = 0

		local screenGuiParent = GetScreenGuiParent()

		if deviceType == DEVICE_PHONE then
			useXScale = ChatSettings.DefaultWindowSizePhone.X.Scale
			useXOffset = ChatSettings.DefaultWindowSizePhone.X.Offset
		elseif deviceType == DEVICE_TABLET then
			useXScale = ChatSettings.DefaultWindowSizeTablet.X.Scale
			useXOffset = ChatSettings.DefaultWindowSizeTablet.X.Offset
		else
			useXScale = ChatSettings.DefaultWindowSizeDesktop.X.Scale
			useXOffset = ChatSettings.DefaultWindowSizeDesktop.X.Offset
		end

		local chatBarYSize = CalculateChatBarPixelSize()

		BaseFrame.Size = UDim2.new(useXScale, useXOffset, 0, chatBarYSize)
		BaseFrame.Position = ChatSettings.DefaultWindowPosition
	else
		local screenGuiParent = GetScreenGuiParent()

		if deviceType == DEVICE_PHONE then
			BaseFrame.Size = ChatSettings.DefaultWindowSizePhone
		elseif deviceType == DEVICE_TABLET then
			BaseFrame.Size = ChatSettings.DefaultWindowSizeTablet
		else
			BaseFrame.Size = ChatSettings.DefaultWindowSizeDesktop
		end

		BaseFrame.Position = ChatSettings.DefaultWindowPosition
	end

	if deviceType == DEVICE_PHONE then
		ChatSettings.ChatWindowTextSize = ChatSettings.ChatWindowTextSizePhone
		ChatSettings.ChatChannelsTabTextSize = ChatSettings.ChatChannelsTabTextSizePhone
		ChatSettings.ChatBarTextSize = ChatSettings.ChatBarTextSizePhone
	end

	local function UpdateDraggable(enabled)
		BaseFrame.Active = enabled
		BaseFrame.Draggable = enabled
	end

	local function UpdateResizable(enabled)
		ChatResizerFrame.Visible = enabled
		ChatResizerFrame.Draggable = enabled

		local frameSizeY = ChatBarParentFrame.Size.Y.Offset

		if enabled then
			ChatBarParentFrame.Size = UDim2.new(1, -frameSizeY - 2, 0, frameSizeY)
			if not bubbleChatOnly() then
				ChatBarParentFrame.Position = UDim2.new(0, 0, 1, -frameSizeY)
			end
		else
			ChatBarParentFrame.Size = UDim2.new(1, 0, 0, frameSizeY)
			if not bubbleChatOnly() then
				ChatBarParentFrame.Position = UDim2.new(0, 0, 1, -frameSizeY)
			end
		end
	end

	local function UpdateChatChannelParentFrameSize()
		local channelsBarSize = CalculateChannelsBarPixelSize()
		local chatBarSize = CalculateChatBarPixelSize()

		if ChatSettings.ShowChannelsBar then
			ChatChannelParentFrame.Size = UDim2.new(1, 0, 1, -(channelsBarSize + chatBarSize + 2 + 2))
			ChatChannelParentFrame.Position = UDim2.new(0, 0, 0, channelsBarSize + 2)
		else
			ChatChannelParentFrame.Size = UDim2.new(1, 0, 1, -(chatBarSize + 2 + 2))
			ChatChannelParentFrame.Position = UDim2.new(0, 0, 0, 2)
		end
	end

	local function UpdateChatChannelsTabTextSize(size)
		local channelsBarSize = CalculateChannelsBarPixelSize(size)
		ChannelsBarParentFrame.Size = UDim2.new(1, 0, 0, channelsBarSize)

		UpdateChatChannelParentFrameSize()
	end

	local function UpdateChatBarTextSize(size)
		local chatBarSize = CalculateChatBarPixelSize(size)

		ChatBarParentFrame.Size = UDim2.new(1, 0, 0, chatBarSize)
		if not bubbleChatOnly() then
			ChatBarParentFrame.Position = UDim2.new(0, 0, 1, -chatBarSize)
		end

		ChatResizerFrame.Size = UDim2.new(0, chatBarSize, 0, chatBarSize)
		ChatResizerFrame.Position = UDim2.new(1, -chatBarSize, 1, -chatBarSize)

		UpdateChatChannelParentFrameSize()
		UpdateResizable(ChatSettings.WindowResizable)
	end

	local function UpdateShowChannelsBar(enabled)
		ChannelsBarParentFrame.Visible = enabled
		UpdateChatChannelParentFrameSize()
	end

	UpdateChatChannelsTabTextSize(ChatSettings.ChatChannelsTabTextSize)
	UpdateChatBarTextSize(ChatSettings.ChatBarTextSize)
	UpdateDraggable(ChatSettings.WindowDraggable)
	UpdateResizable(ChatSettings.WindowResizable)
	UpdateShowChannelsBar(ChatSettings.ShowChannelsBar)

	ChatSettings.SettingsChanged:connect(function(setting, value)
		if setting == "WindowDraggable" then
			UpdateDraggable(value)
		elseif setting == "WindowResizable" then
			UpdateResizable(value)
		elseif setting == "ChatChannelsTabTextSize" then
			UpdateChatChannelsTabTextSize(value)
		elseif setting == "ChatBarTextSize" then
			UpdateChatBarTextSize(value)
		elseif setting == "ShowChannelsBar" then
			UpdateShowChannelsBar(value)
		end
	end)

	self.GuiObject = BaseFrame

	self.GuiObjects.BaseFrame = BaseFrame
	self.GuiObjects.ChatBarParentFrame = ChatBarParentFrame
	self.GuiObjects.ChannelsBarParentFrame = ChannelsBarParentFrame
	self.GuiObjects.ChatChannelParentFrame = ChatChannelParentFrame
	self.GuiObjects.ChatResizerFrame = ChatResizerFrame
	self.GuiObjects.ResizeIcon = ResizeIcon
	self:AnimGuiObjects()
end

function methods:GetChatBar()
	return self.ChatBar
end

function methods:RegisterChatBar(ChatBar)
	self.ChatBar = ChatBar
	self.ChatBar:CreateGuiObjects(self.GuiObjects.ChatBarParentFrame)
end

function methods:RegisterChannelsBar(ChannelsBar)
	self.ChannelsBar = ChannelsBar
	self.ChannelsBar:CreateGuiObjects(self.GuiObjects.ChannelsBarParentFrame)
end

function methods:RegisterMessageLogDisplay(MessageLogDisplay)
	self.MessageLogDisplay = MessageLogDisplay
	self.MessageLogDisplay.GuiObject.Parent = self.GuiObjects.ChatChannelParentFrame
end

function methods:AddChannel(channelName)
	if self:GetChannel(channelName) then
		error("Channel '" .. channelName .. "' already exists!")
		return
	end

	local channel = moduleChatChannel.new(channelName, self.MessageLogDisplay)
	self.Channels[channelName:lower()] = channel

	channel:SetActive(false)

	local tab = self.ChannelsBar:AddChannelTab(channelName)
	tab.NameTag.MouseButton1Click:connect(function()
		self:SwitchCurrentChannel(channelName)
	end)

	channel:RegisterChannelTab(tab)

	return channel
end

function methods:GetFirstChannel()
	--// Channels are not indexed numerically, so this function is necessary.
	--// Grabs and returns the first channel it happens to, or nil if none exist.
	for i, v in pairs(self.Channels) do
		return v
	end
	return nil
end

function methods:RemoveChannel(channelName)
	if not self:GetChannel(channelName) then
		error("Channel '" .. channelName .. "' does not exist!")
	end

	local indexName = channelName:lower()

	local needsChannelSwitch = false
	if self.Channels[indexName] == self:GetCurrentChannel() then
		needsChannelSwitch = true

		self:SwitchCurrentChannel(nil)
	end

	self.Channels[indexName]:Destroy()
	self.Channels[indexName] = nil

	self.ChannelsBar:RemoveChannelTab(channelName)

	if needsChannelSwitch then
		local generalChannelExists = (self:GetChannel(ChatSettings.GeneralChannelName) ~= nil)
		local removingGeneralChannel = (indexName == ChatSettings.GeneralChannelName:lower())

		local targetSwitchChannel = nil

		if generalChannelExists and not removingGeneralChannel then
			targetSwitchChannel = ChatSettings.GeneralChannelName
		else
			local firstChannel = self:GetFirstChannel()
			targetSwitchChannel = (firstChannel and firstChannel.Name or nil)
		end

		self:SwitchCurrentChannel(targetSwitchChannel)
	end

	if not ChatSettings.ShowChannelsBar then
		if self.ChatBar.TargetChannel == channelName then
			self.ChatBar:SetChannelTarget(ChatSettings.GeneralChannelName)
		end
	end
end

function methods:GetChannel(channelName)
	return channelName and self.Channels[channelName:lower()] or nil
end

function methods:GetTargetMessageChannel()
	if not ChatSettings.ShowChannelsBar then
		return self.ChatBar.TargetChannel
	else
		local curChannel = self:GetCurrentChannel()
		return curChannel and curChannel.Name
	end
end

function methods:GetCurrentChannel()
	return self.CurrentChannel
end

function methods:SwitchCurrentChannel(channelName)
	if not ChatSettings.ShowChannelsBar then
		local targ = self:GetChannel(channelName)
		if targ then
			self.ChatBar:SetChannelTarget(targ.Name)
		end

		channelName = ChatSettings.GeneralChannelName
	end

	local cur = self:GetCurrentChannel()
	local new = self:GetChannel(channelName)
	if new == nil then
		error(string.format("Channel '%s' does not exist.", channelName))
	end

	if new ~= cur then
		if cur then
			cur:SetActive(false)
			local tab = self.ChannelsBar:GetChannelTab(cur.Name)
			tab:SetActive(false)
		end

		if new then
			new:SetActive(true)
			local tab = self.ChannelsBar:GetChannelTab(new.Name)
			tab:SetActive(true)
		end

		self.CurrentChannel = new
	end
end

function methods:UpdateFrameVisibility()
	self.GuiObject.Visible = (self.Visible and self.CoreGuiEnabled)
end

function methods:GetVisible()
	return self.Visible
end

function methods:SetVisible(visible)
	self.Visible = visible
	self:UpdateFrameVisibility()
end

function methods:GetCoreGuiEnabled()
	return self.CoreGuiEnabled
end

function methods:SetCoreGuiEnabled(enabled)
	self.CoreGuiEnabled = enabled
	self:UpdateFrameVisibility()
end

function methods:EnableResizable()
	self.GuiObjects.ChatResizerFrame.Active = true
end

function methods:DisableResizable()
	self.GuiObjects.ChatResizerFrame.Active = false
end

function methods:FadeOutBackground(duration)
	self.ChannelsBar:FadeOutBackground(duration)
	self.MessageLogDisplay:FadeOutBackground(duration)
	self.ChatBar:FadeOutBackground(duration)

	self.AnimParams.Background_TargetTransparency = 1
	self.AnimParams.Background_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(duration)
end

function methods:FadeInBackground(duration)
	self.ChannelsBar:FadeInBackground(duration)
	self.MessageLogDisplay:FadeInBackground(duration)
	self.ChatBar:FadeInBackground(duration)

	self.AnimParams.Background_TargetTransparency = 0.6
	self.AnimParams.Background_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(duration)
end

function methods:FadeOutText(duration)
	self.MessageLogDisplay:FadeOutText(duration)
	self.ChannelsBar:FadeOutText(duration)
end

function methods:FadeInText(duration)
	self.MessageLogDisplay:FadeInText(duration)
	self.ChannelsBar:FadeInText(duration)
end

function methods:AnimGuiObjects()
	self.GuiObjects.ChatChannelParentFrame.BackgroundTransparency = self.AnimParams.Background_CurrentTransparency
	self.GuiObjects.ChatResizerFrame.BackgroundTransparency = self.AnimParams.Background_CurrentTransparency
	self.GuiObjects.ResizeIcon.ImageTransparency = self.AnimParams.Background_CurrentTransparency
end

function methods:InitializeAnimParams()
	self.AnimParams.Background_TargetTransparency = 0.6
	self.AnimParams.Background_CurrentTransparency = 0.6
	self.AnimParams.Background_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(0)
end

function methods:Update(dtScale)
	self.ChatBar:Update(dtScale)
	self.ChannelsBar:Update(dtScale)
	self.MessageLogDisplay:Update(dtScale)

	self.AnimParams.Background_CurrentTransparency = CurveUtil:Expt(
		self.AnimParams.Background_CurrentTransparency,
		self.AnimParams.Background_TargetTransparency,
		self.AnimParams.Background_NormalizedExptValue,
		dtScale
	)

	self:AnimGuiObjects()
end

--///////////////////////// Constructors
--//////////////////////////////////////

function module.new()
	local obj = setmetatable({}, methods)

	obj.GuiObject = nil
	obj.GuiObjects = {}

	obj.ChatBar = nil
	obj.ChannelsBar = nil
	obj.MessageLogDisplay = nil

	obj.Channels = {}
	obj.CurrentChannel = nil

	obj.Visible = true
	obj.CoreGuiEnabled = true

	obj.AnimParams = {}

	obj:InitializeAnimParams()

	return obj
end

return module
