local P={}P.__index=P local function l()local P=Instance.new("\070\114\097\109\101")P.Selectable=false P.Size=UDim2.new(1,0,1,0)P.BackgroundTransparency=1 local l=Instance.new("\083\099\114\111\108\108\105\110\103\070\114\097\109\101")l.Selectable=ChatSettings.GamepadNavigationEnabled l.Name="\083\099\114\111\108\108\101\114"l.BackgroundTransparency=1 l.BorderSizePixel=0 l.Position=UDim2.new(0,0,0,3)l.Size=UDim2.new(1,-4,1,-6)l.CanvasSize=UDim2.new(0,0,0,0)l.ScrollBarThickness=module.ScrollBarThickness l.Active=false l.Parent=P return P,l end function P.Destroy(P)P.GuiObject:Destroy()P.Destroyed=true end function P.SetActive(l,P)l.GuiObject.Visible=P end function P.UpdateMessageFiltered(l,P)local r=nil local O=1 local v=l.MessageObjectLog while#v>=O do local l=v[O]if l.ID==P.ID then r=l break end O=O+1 end if r then r.UpdateTextFunction(P)l:ReorderAllMessages()end end function P.AddMessage(l,P)l:WaitUntilParentedCorrectly()local r=MessageLabelCreator:CreateMessageLabel(P,l.CurrentChannelName)if r==nil then return end table.insert(l.MessageObjectLog,r)l:PositionMessageLabelInWindow(r)end function P.AddMessageAtIndex(r,P,l)local O=MessageLabelCreator:CreateMessageLabel(P,r.CurrentChannelName)if O==nil then return end table.insert(r.MessageObjectLog,l,O)local v=r:IsScrolledDown()r:ReorderAllMessages()if v then r.Scroller.CanvasPosition=Vector2.new(0,math.max(0,r.Scroller.CanvasSize.Y.Offset-r.Scroller.AbsoluteSize.Y))end end function P.RemoveLastMessage(P)P:WaitUntilParentedCorrectly()local l=P.MessageObjectLog[1]local r=UDim2.new(0,0,0,l.BaseFrame.AbsoluteSize.Y)l:Destroy()table.remove(P.MessageObjectLog,1)for P,l in pairs(P.MessageObjectLog)do l.BaseFrame.Position=l.BaseFrame.Position-r end P.Scroller.CanvasSize=P.Scroller.CanvasSize-r end function P.IsScrolledDown(P)local l=P.Scroller.CanvasSize.Y.Offset local r=P.Scroller.AbsoluteWindowSize.Y local O=P.Scroller.CanvasPosition.Y return l<r or l-O<=r+5 end function min(P,l)return P<l and P or l end function P.PositionMessageLabelInWindow(l,P)l:WaitUntilParentedCorrectly()local r=P.BaseFrame r.Parent=l.Scroller r.Position=UDim2.new(0,0,0,l.Scroller.CanvasSize.Y.Offset)r.Size=UDim2.new(1,0,0,P.GetHeightFunction(l.Scroller.AbsoluteSize.X))if P.BaseMessage then local O=l.Scroller.AbsoluteSize.X local v=min(l.Scroller.AbsoluteSize.X-10,0)while not P.BaseMessage.TextFits do O=O-1 if O<v then break end r.Size=UDim2.new(1,0,0,P.GetHeightFunction(O))end end local O=l:IsScrolledDown()local v=UDim2.new(0,0,0,r.Size.Y.Offset)l.Scroller.CanvasSize=l.Scroller.CanvasSize+v if O then l.Scroller.CanvasPosition=Vector2.new(0,math.max(0,l.Scroller.CanvasSize.Y.Offset-l.Scroller.AbsoluteSize.Y))end end function P.ReorderAllMessages(P)P:WaitUntilParentedCorrectly()if P.GuiObject.AbsoluteSize.Y<1 then return end local l=P.Scroller.CanvasPosition local r=P:IsScrolledDown()P.Scroller.CanvasSize=UDim2.new(0,0,0,0)for l,r in pairs(P.MessageObjectLog)do P:PositionMessageLabelInWindow(r)end if not r then P.Scroller.CanvasPosition=l end end function P.Clear(P)for P,l in pairs(P.MessageObjectLog)do l:Destroy()end P.MessageObjectLog={}P.Scroller.CanvasSize=UDim2.new(0,0,0,0)end function P.SetCurrentChannelName(l,P)l.CurrentChannelName=P end function P.FadeOutBackground(l,P) end function P.FadeInBackground(l,P) end function P.FadeOutText(l,P)for r=1,#l.MessageObjectLog,1 do if l.MessageObjectLog[r].FadeOutFunction then l.MessageObjectLog[r].FadeOutFunction(P,CurveUtil)end end end function P.FadeInText(l,P)for r=1,#l.MessageObjectLog,1 do if l.MessageObjectLog[r].FadeInFunction then l.MessageObjectLog[r].FadeInFunction(P,CurveUtil)end end end function P.Update(l,P)for r=1,#l.MessageObjectLog,1 do if l.MessageObjectLog[r].UpdateAnimFunction then l.MessageObjectLog[r].UpdateAnimFunction(P,CurveUtil)end end end
--////////////////////////////// Methods
--//////////////////////////////////////
local methods = {}
methods.__index = methods

local function CreateGuiObjects()
	local BaseFrame = Instance.new("Frame")
	BaseFrame.Selectable = false
	BaseFrame.Size = UDim2.new(1, 0, 1, 0)
	BaseFrame.BackgroundTransparency = 1

	local Scroller = Instance.new("ScrollingFrame")
	Scroller.Selectable = ChatSettings.GamepadNavigationEnabled
	Scroller.Name = "Scroller"
	Scroller.BackgroundTransparency = 1
	Scroller.BorderSizePixel = 0
	Scroller.Position = UDim2.new(0, 0, 0, 3)
	Scroller.Size = UDim2.new(1, -4, 1, -6)
	Scroller.CanvasSize = UDim2.new(0, 0, 0, 0)
	Scroller.ScrollBarThickness = module.ScrollBarThickness
	Scroller.Active = false
	Scroller.Parent = BaseFrame

	return BaseFrame, Scroller
end

function methods:Destroy()
	self.GuiObject:Destroy()
	self.Destroyed = true
end

function methods:SetActive(active)
	self.GuiObject.Visible = active
end

function methods:UpdateMessageFiltered(messageData)
	local messageObject = nil
	local searchIndex = 1
	local searchTable = self.MessageObjectLog

	while #searchTable >= searchIndex do
		local obj = searchTable[searchIndex]

		if obj.ID == messageData.ID then
			messageObject = obj
			break
		end

		searchIndex = searchIndex + 1
	end

	if messageObject then
		messageObject.UpdateTextFunction(messageData)
		self:ReorderAllMessages()
	end
end

function methods:AddMessage(messageData)
	self:WaitUntilParentedCorrectly()

	local messageObject = MessageLabelCreator:CreateMessageLabel(messageData, self.CurrentChannelName)
	if messageObject == nil then
		return
	end

	table.insert(self.MessageObjectLog, messageObject)
	self:PositionMessageLabelInWindow(messageObject)
end

function methods:AddMessageAtIndex(messageData, index)
	local messageObject = MessageLabelCreator:CreateMessageLabel(messageData, self.CurrentChannelName)
	if messageObject == nil then
		return
	end

	table.insert(self.MessageObjectLog, index, messageObject)

	local wasScrolledToBottom = self:IsScrolledDown()
	self:ReorderAllMessages()
	if wasScrolledToBottom then
		self.Scroller.CanvasPosition =
			Vector2.new(0, math.max(0, self.Scroller.CanvasSize.Y.Offset - self.Scroller.AbsoluteSize.Y))
	end
end

function methods:RemoveLastMessage()
	self:WaitUntilParentedCorrectly()

	local lastMessage = self.MessageObjectLog[1]
	local posOffset = UDim2.new(0, 0, 0, lastMessage.BaseFrame.AbsoluteSize.Y)

	lastMessage:Destroy()
	table.remove(self.MessageObjectLog, 1)

	for i, messageObject in pairs(self.MessageObjectLog) do
		messageObject.BaseFrame.Position = messageObject.BaseFrame.Position - posOffset
	end

	self.Scroller.CanvasSize = self.Scroller.CanvasSize - posOffset
end

function methods:IsScrolledDown()
	local yCanvasSize = self.Scroller.CanvasSize.Y.Offset
	local yContainerSize = self.Scroller.AbsoluteWindowSize.Y
	local yScrolledPosition = self.Scroller.CanvasPosition.Y

	return (yCanvasSize < yContainerSize or yCanvasSize - yScrolledPosition <= yContainerSize + 5)
end

function min(x, y)
	return x < y and x or y
end

function methods:PositionMessageLabelInWindow(messageObject)
	self:WaitUntilParentedCorrectly()

	local baseFrame = messageObject.BaseFrame

	baseFrame.Parent = self.Scroller
	baseFrame.Position = UDim2.new(0, 0, 0, self.Scroller.CanvasSize.Y.Offset)

	baseFrame.Size = UDim2.new(1, 0, 0, messageObject.GetHeightFunction(self.Scroller.AbsoluteSize.X))

	if messageObject.BaseMessage then
		local trySize = self.Scroller.AbsoluteSize.X
		local minTrySize = min(self.Scroller.AbsoluteSize.X - 10, 0)
		while not messageObject.BaseMessage.TextFits do
			trySize = trySize - 1
			if trySize < minTrySize then
				break
			end
			baseFrame.Size = UDim2.new(1, 0, 0, messageObject.GetHeightFunction(trySize))
		end
	end

	local isScrolledDown = self:IsScrolledDown()

	local add = UDim2.new(0, 0, 0, baseFrame.Size.Y.Offset)
	self.Scroller.CanvasSize = self.Scroller.CanvasSize + add

	if isScrolledDown then
		self.Scroller.CanvasPosition =
			Vector2.new(0, math.max(0, self.Scroller.CanvasSize.Y.Offset - self.Scroller.AbsoluteSize.Y))
	end
end

function methods:ReorderAllMessages()
	self:WaitUntilParentedCorrectly()

	--// Reordering / reparenting with a size less than 1 causes weird glitches to happen with scrolling as repositioning happens.
	if self.GuiObject.AbsoluteSize.Y < 1 then
		return
	end

	local oldCanvasPositon = self.Scroller.CanvasPosition
	local wasScrolledDown = self:IsScrolledDown()

	self.Scroller.CanvasSize = UDim2.new(0, 0, 0, 0)
	for i, messageObject in pairs(self.MessageObjectLog) do
		self:PositionMessageLabelInWindow(messageObject)
	end

	if not wasScrolledDown then
		self.Scroller.CanvasPosition = oldCanvasPositon
	end
end

function methods:Clear()
	for i, v in pairs(self.MessageObjectLog) do
		v:Destroy()
	end
	self.MessageObjectLog = {}

	self.Scroller.CanvasSize = UDim2.new(0, 0, 0, 0)
end

function methods:SetCurrentChannelName(name)
	self.CurrentChannelName = name
end

function methods:FadeOutBackground(duration)
	--// Do nothing
end

function methods:FadeInBackground(duration)
	--// Do nothing
end

function methods:FadeOutText(duration)
	for i = 1, #self.MessageObjectLog do
		if self.MessageObjectLog[i].FadeOutFunction then
			self.MessageObjectLog[i].FadeOutFunction(duration, CurveUtil)
		end
	end
end

function methods:FadeInText(duration)
	for i = 1, #self.MessageObjectLog do
		if self.MessageObjectLog[i].FadeInFunction then
			self.MessageObjectLog[i].FadeInFunction(duration, CurveUtil)
		end
	end
end

function methods:Update(dtScale)
	for i = 1, #self.MessageObjectLog do
		if self.MessageObjectLog[i].UpdateAnimFunction then
			self.MessageObjectLog[i].UpdateAnimFunction(dtScale, CurveUtil)
		end
	end
end
