local V={}V.__index=V local function c()local V=Instance.new("\070\114\097\109\101")V.Selectable=false V.Size=UDim2.new(1,0,1,0)V.BackgroundTransparency=1 local c=Instance.new("\083\099\114\111\108\108\105\110\103\070\114\097\109\101")c.Selectable=ChatSettings.GamepadNavigationEnabled c.Name="\083\099\114\111\108\108\101\114"c.BackgroundTransparency=1 c.BorderSizePixel=0 c.Position=UDim2.new(0,0,0,3)c.Size=UDim2.new(1,-4,1,-6)c.CanvasSize=UDim2.new(0,0,0,0)c.ScrollBarThickness=module.ScrollBarThickness c.Active=true c.Parent=V local t=Instance.new("\085\073\076\105\115\116\076\097\121\111\117\116")t.SortOrder=Enum.SortOrder.LayoutOrder t.Parent=c return V,c,t end function V.Destroy(V)V.GuiObject:Destroy()V.Destroyed=true end function V.SetActive(c,V)c.GuiObject.Visible=V end function V.UpdateMessageFiltered(c,V)local t=nil local s=1 local h=c.MessageObjectLog while#h>=s do local c=h[s]if c.ID==V.ID then t=c break end s=s+1 end if t then if UserFlagRemoveMessageOnTextFilterFailures then if V.Message==""then c:RemoveMessageAtIndex(s)else t.UpdateTextFunction(V)c:PositionMessageLabelInWindow(t,s)end else t.UpdateTextFunction(V)c:PositionMessageLabelInWindow(t,s)end end end function V.AddMessage(c,V)c:WaitUntilParentedCorrectly()local t=MessageLabelCreator:CreateMessageLabel(V,c.CurrentChannelName)if t==nil then return end table.insert(c.MessageObjectLog,t)c:PositionMessageLabelInWindow(t,#c.MessageObjectLog)end function V.RemoveMessageAtIndex(c,V)c:WaitUntilParentedCorrectly()local t=c.MessageObjectLog[V]if t then t:Destroy()table.remove(c.MessageObjectLog,V)end end function V.AddMessageAtIndex(t,V,c)local s=MessageLabelCreator:CreateMessageLabel(V,t.CurrentChannelName)if s==nil then return end table.insert(t.MessageObjectLog,c,s)t:PositionMessageLabelInWindow(s,c)end function V.RemoveLastMessage(V)V:WaitUntilParentedCorrectly()local c=V.MessageObjectLog[1]c:Destroy()table.remove(V.MessageObjectLog,1)end function V.IsScrolledDown(V)local c=V.Scroller.CanvasSize.Y.Offset local t=V.Scroller.AbsoluteWindowSize.Y local s=V.Scroller.CanvasPosition.Y return c<t or c-s<=t+5 end function V.UpdateMessageTextHeight(c,V)local t=V.BaseFrame for s=1,10,1 do if V.BaseMessage.TextFits then break end local h=c.Scroller.AbsoluteSize.X-s t.Size=UDim2.new(1,0,0,V.GetHeightFunction(h))end end function V.PositionMessageLabelInWindow(t,V,c)t:WaitUntilParentedCorrectly()local s=t:IsScrolledDown()local h=V.BaseFrame local p=1 if t.MessageObjectLog[c-1]then if c==#t.MessageObjectLog then p=t.MessageObjectLog[c-1].BaseFrame.LayoutOrder+1 else p=t.MessageObjectLog[c-1].BaseFrame.LayoutOrder end end h.LayoutOrder=p h.Size=UDim2.new(1,0,0,V.GetHeightFunction(t.Scroller.AbsoluteSize.X))h.Parent=t.Scroller if V.BaseMessage then t:UpdateMessageTextHeight(V)end if s then t.Scroller.CanvasPosition=Vector2.new(0,math.max(0,t.Scroller.CanvasSize.Y.Offset-t.Scroller.AbsoluteSize.Y))end end function V.ReorderAllMessages(V)V:WaitUntilParentedCorrectly()if V.GuiObject.AbsoluteSize.Y<1 then return end local c=V.Scroller.CanvasPosition local t=V:IsScrolledDown()for c,t in pairs(V.MessageObjectLog)do V:UpdateMessageTextHeight(t)end if not t then V.Scroller.CanvasPosition=c else V.Scroller.CanvasPosition=Vector2.new(0,math.max(0,V.Scroller.CanvasSize.Y.Offset-V.Scroller.AbsoluteSize.Y))end end function V.Clear(V)for V,c in pairs(V.MessageObjectLog)do c:Destroy()end V.MessageObjectLog={}end function V.SetCurrentChannelName(c,V)c.CurrentChannelName=V end function V.FadeOutBackground(c,V) end function V.FadeInBackground(c,V) end function V.FadeOutText(c,V)for t=1,#c.MessageObjectLog,1 do if c.MessageObjectLog[t].FadeOutFunction then c.MessageObjectLog[t].FadeOutFunction(V,CurveUtil)end end end function V.FadeInText(c,V)for t=1,#c.MessageObjectLog,1 do if c.MessageObjectLog[t].FadeInFunction then c.MessageObjectLog[t].FadeInFunction(V,CurveUtil)end end end function V.Update(c,V)for t=1,#c.MessageObjectLog,1 do if c.MessageObjectLog[t].UpdateAnimFunction then c.MessageObjectLog[t].UpdateAnimFunction(V,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 = true
	Scroller.Parent = BaseFrame

	local Layout = Instance.new("UIListLayout")
	Layout.SortOrder = Enum.SortOrder.LayoutOrder
	Layout.Parent = Scroller

	return BaseFrame, Scroller, Layout
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
		if UserFlagRemoveMessageOnTextFilterFailures then
			if messageData.Message == "" then
				self:RemoveMessageAtIndex(searchIndex)
			else
				messageObject.UpdateTextFunction(messageData)
				self:PositionMessageLabelInWindow(messageObject, searchIndex)
			end
		else
			messageObject.UpdateTextFunction(messageData)
			self:PositionMessageLabelInWindow(messageObject, searchIndex)
		end
	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, #self.MessageObjectLog)
end

function methods:RemoveMessageAtIndex(index)
	self:WaitUntilParentedCorrectly()

	local removeThis = self.MessageObjectLog[index]

	if removeThis then
		removeThis:Destroy()
		table.remove(self.MessageObjectLog, index)
	end
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)

	self:PositionMessageLabelInWindow(messageObject, index)
end

function methods:RemoveLastMessage()
	self:WaitUntilParentedCorrectly()

	local lastMessage = self.MessageObjectLog[1]

	lastMessage:Destroy()
	table.remove(self.MessageObjectLog, 1)
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 methods:UpdateMessageTextHeight(messageObject)
	local baseFrame = messageObject.BaseFrame
	for i = 1, 10 do
		if messageObject.BaseMessage.TextFits then
			break
		end

		local trySize = self.Scroller.AbsoluteSize.X - i
		baseFrame.Size = UDim2.new(1, 0, 0, messageObject.GetHeightFunction(trySize))
	end
end

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

	local wasScrolledDown = self:IsScrolledDown()

	local baseFrame = messageObject.BaseFrame

	local layoutOrder = 1
	if self.MessageObjectLog[index - 1] then
		if index == #self.MessageObjectLog then
			layoutOrder = self.MessageObjectLog[index - 1].BaseFrame.LayoutOrder + 1
		else
			layoutOrder = self.MessageObjectLog[index - 1].BaseFrame.LayoutOrder
		end
	end
	baseFrame.LayoutOrder = layoutOrder

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

	if messageObject.BaseMessage then
		self:UpdateMessageTextHeight(messageObject)
	end

	if wasScrolledDown 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()

	for _, messageObject in pairs(self.MessageObjectLog) do
		self:UpdateMessageTextHeight(messageObject)
	end

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

function methods:Clear()
	for _, v in pairs(self.MessageObjectLog) do
		v:Destroy()
	end
	self.MessageObjectLog = {}
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
