local z=game:GetService("\080\108\097\121\101\114\115")local s=z.LocalPlayer return function(z)local B={Items={},ItemOptions={},SelectedItem=0}local o=z.Util local X=((s:WaitForChild("\080\108\097\121\101\114\071\117\105")):WaitForChild("\067\109\100\114")):WaitForChild("\065\117\116\111\099\111\109\112\108\101\116\101")local G=X:WaitForChild("\084\101\120\116\066\117\116\116\111\110")local Y=X:WaitForChild("\084\105\116\108\101")local r=X:WaitForChild("\068\101\115\099\114\105\112\116\105\111\110")local O=(X.Parent:WaitForChild("\070\114\097\109\101")):WaitForChild("\069\110\116\114\121")G.Parent=nil local i=X.ScrollBarThickness local function W(z,s,B,X)z.Visible=B~=nil s.Text=B or""if X then s.Size=UDim2.new(0,(o.GetTextSize(B or"",s,Vector2.new(1000,1000),1,0)).X,z.Size.Y.Scale,z.Size.Y.Offset)end end local function P()X.Size=UDim2.new(0,math.max(Y.Field.TextBounds.X+Y.Field.Type.TextBounds.X,X.Size.X.Offset),0,math.min(X.UIListLayout.AbsoluteContentSize.Y,(X.Parent.AbsoluteSize.Y-X.AbsolutePosition.Y)-10))end local function j(z)W(Y,Y.Field,z.name,true)W(Y.Field.Type,Y.Field.Type,z.type and"\058 "..((z.type:sub(1,1)):upper()..z.type:sub(2)))W(r,r.Label,z.description)r.Label.TextColor3=z.invalid and Color3.fromRGB(255,73,73)or Color3.fromRGB(255,255,255)r.Size=UDim2.new(1,0,0,40)while not r.Label.TextFits do r.Size=r.Size+UDim2.new(0,0,0,2)if r.Size.Y.Offset>500 then break end end task.wait()X.UIListLayout:ApplyLayout()P()X.ScrollBarThickness=i end function B.Show(B,z,s)s=s or{}for z,s in pairs(B.Items)do if s.gui then s.gui:Destroy()end end B.SelectedItem=1 B.Items=z B.Prefix=s.prefix or""B.LastItem=s.isLast or false B.Command=s.command B.Arg=s.arg B.NumArgs=s.numArgs B.IsPartial=s.isPartial local Y=200 X.ScrollBarThickness=0 for z,s in pairs(B.Items)do local o=s[1]local r=s[2]local O=G:Clone()O.Name=o..r O.BackgroundTransparency=z==B.SelectedItem and.5 or 1 local i,W=string.find(r:lower(),o:lower(),1,true)if i==nil and W==nil then i,W=1,string.len(r)end O.Typed.Text=string.rep(" ",i-1)..o O.Suggest.Text=string.sub(r,0,i-1)..(string.rep(" ",#o)..string.sub(r,W+1))O.Parent=X O.LayoutOrder=z local P=math.max(O.Typed.TextBounds.X,O.Suggest.TextBounds.X)+20 if P>Y then Y=P end s.gui=O end X.UIListLayout:ApplyLayout()local r=O.TextBox.Text local i=o.SplitString(r)if r:sub(#r,#r)==" "and not s.at then i[#i+1]="\101"end table.remove(i,#i)local W=(s.at and s.at or#table.concat(i," ")+1)*7 X.Position=UDim2.new(0,(O.TextBox.AbsolutePosition.X-10)+W,0,O.TextBox.AbsolutePosition.Y+30)X.Size=UDim2.new(0,Y,0,X.UIListLayout.AbsoluteContentSize.Y)X.Visible=true j(B.Items[1]and B.Items[1].options or s)end function B.GetSelectedItem(z)if X.Visible==false then return nil end return B.Items[B.SelectedItem]end function B.Hide(z)X.Visible=false end function B.IsVisible(z)return X.Visible end function B.Select(s,z)if not X.Visible then return end s.SelectedItem=s.SelectedItem+z if s.SelectedItem>#s.Items then s.SelectedItem=1 elseif s.SelectedItem<1 then s.SelectedItem=#s.Items end for z,B in pairs(s.Items)do B.gui.BackgroundTransparency=z==s.SelectedItem and.5 or 1 end X.CanvasPosition=Vector2.new(0,math.max(0,((Y.Size.Y.Offset+r.Size.Y.Offset)+s.SelectedItem*G.Size.Y.Offset)-X.Size.Y.Offset))if s.Items[s.SelectedItem]and s.Items[s.SelectedItem].options then j(s.Items[s.SelectedItem].options or{})end end;(X.Parent:GetPropertyChangedSignal("\065\098\115\111\108\117\116\101\083\105\122\101")):Connect(P)return B end
local Players = game:GetService("Players")
local Player = Players.LocalPlayer

return function(Cmdr)
	local AutoComplete = {
		Items = {},
		ItemOptions = {},
		SelectedItem = 0,
	}

	local Util = Cmdr.Util

	local Gui = Player:WaitForChild("PlayerGui"):WaitForChild("Cmdr"):WaitForChild("Autocomplete")
	local AutoItem = Gui:WaitForChild("TextButton")
	local Title = Gui:WaitForChild("Title")
	local Description = Gui:WaitForChild("Description")
	local Entry = Gui.Parent:WaitForChild("Frame"):WaitForChild("Entry")
	AutoItem.Parent = nil

	local defaultBarThickness = Gui.ScrollBarThickness

	-- Helper function that sets text and resizes labels
	local function SetText(obj, textObj, text, sizeFromContents)
		obj.Visible = text ~= nil
		textObj.Text = text or ""

		if sizeFromContents then
			textObj.Size = UDim2.new(
				0,
				Util.GetTextSize(text or "", textObj, Vector2.new(1000, 1000), 1, 0).X,
				obj.Size.Y.Scale,
				obj.Size.Y.Offset
			)
		end
	end

	local function UpdateContainerSize()
		Gui.Size = UDim2.new(
			0,
			math.max(Title.Field.TextBounds.X + Title.Field.Type.TextBounds.X, Gui.Size.X.Offset),
			0,
			math.min(Gui.UIListLayout.AbsoluteContentSize.Y, Gui.Parent.AbsoluteSize.Y - Gui.AbsolutePosition.Y - 10)
		)
	end

	-- Update the info display (Name, type, and description) based on given options.
	local function UpdateInfoDisplay(options)
		-- Update the objects' text and sizes
		SetText(Title, Title.Field, options.name, true)
		SetText(
			Title.Field.Type,
			Title.Field.Type,
			options.type and ": " .. options.type:sub(1, 1):upper() .. options.type:sub(2)
		)
		SetText(Description, Description.Label, options.description)

		Description.Label.TextColor3 = options.invalid and Color3.fromRGB(255, 73, 73) or Color3.fromRGB(255, 255, 255)
		Description.Size = UDim2.new(1, 0, 0, 40)

		-- Flow description text
		while not Description.Label.TextFits do
			Description.Size = Description.Size + UDim2.new(0, 0, 0, 2)

			if Description.Size.Y.Offset > 500 then
				break
			end
		end

		-- Update container
		task.wait()
		Gui.UIListLayout:ApplyLayout()
		UpdateContainerSize()
		Gui.ScrollBarThickness = defaultBarThickness
	end

	-- Shows the auto complete menu with the given list and possible options
	-- item = {typedText, suggestedText, options?=options}
	-- The options table is optional. `at` should only be passed into AutoComplete::Show
	-- name, type, and description may be passed in an options dictionary inside the items as well
	-- options.at?: the character index at which to show the menu
	-- options.name?: The name to display in the info box
	-- options.type?: The type to display in the info box
	-- options.prefix?: The current type prefix (%Team)
	-- options.description?: The description for the currently active info box
	-- options.invalid?: If true, description is shown in red.
	-- options.isLast?: If true, auto complete won't keep going after this argument.
	function AutoComplete:Show(items, options)
		options = options or {}

		-- Remove old options.
		for _, item in pairs(self.Items) do
			if item.gui then
				item.gui:Destroy()
			end
		end

		-- Reset state
		self.SelectedItem = 1
		self.Items = items
		self.Prefix = options.prefix or ""
		self.LastItem = options.isLast or false
		self.Command = options.command
		self.Arg = options.arg
		self.NumArgs = options.numArgs
		self.IsPartial = options.isPartial

		-- Generate the new option labels
		local autocompleteWidth = 200

		Gui.ScrollBarThickness = 0

		for i, item in pairs(self.Items) do
			local leftText = item[1]
			local rightText = item[2]

			local btn = AutoItem:Clone()
			btn.Name = leftText .. rightText
			btn.BackgroundTransparency = i == self.SelectedItem and 0.5 or 1

			local start, stop = string.find(rightText:lower(), leftText:lower(), 1, true)
			if start == nil and stop == nil then
				--start and stop are nil when the type returns an autocomplete result that is completely different (such as a custom alias hanlded within the type).
				--One should never be nil without the other.
				start, stop = 1, string.len(rightText)
			end
			btn.Typed.Text = string.rep(" ", start - 1) .. leftText
			btn.Suggest.Text = string.sub(rightText, 0, start - 1)
				.. string.rep(" ", #leftText)
				.. string.sub(rightText, stop + 1)

			btn.Parent = Gui
			btn.LayoutOrder = i

			local maxBounds = math.max(btn.Typed.TextBounds.X, btn.Suggest.TextBounds.X) + 20
			if maxBounds > autocompleteWidth then
				autocompleteWidth = maxBounds
			end

			item.gui = btn
		end

		Gui.UIListLayout:ApplyLayout()

		-- Todo: Use TextService to find accurate position for auto complete box
		local text = Entry.TextBox.Text
		local words = Util.SplitString(text)
		if text:sub(#text, #text) == " " and not options.at then
			words[#words + 1] = "e"
		end
		table.remove(words, #words)
		local extra = (options.at and options.at or (#table.concat(words, " ") + 1)) * 7

		-- Update the auto complete container
		Gui.Position =
			UDim2.new(0, Entry.TextBox.AbsolutePosition.X - 10 + extra, 0, Entry.TextBox.AbsolutePosition.Y + 30)
		Gui.Size = UDim2.new(0, autocompleteWidth, 0, Gui.UIListLayout.AbsoluteContentSize.Y)
		Gui.Visible = true

		-- Finally, update thge info display
		UpdateInfoDisplay(self.Items[1] and self.Items[1].options or options)
	end

	-- Returns the selected item in the auto complete
	function AutoComplete:GetSelectedItem()
		if Gui.Visible == false then
			return nil
		end

		return AutoComplete.Items[AutoComplete.SelectedItem]
	end

	-- Hides the auto complete
	function AutoComplete:Hide()
		Gui.Visible = false
	end

	-- Returns if the menu is visible
	function AutoComplete:IsVisible()
		return Gui.Visible
	end

	-- Changes the user's item selection by the given delta
	function AutoComplete:Select(delta)
		if not Gui.Visible then
			return
		end

		self.SelectedItem = self.SelectedItem + delta

		if self.SelectedItem > #self.Items then
			self.SelectedItem = 1
		elseif self.SelectedItem < 1 then
			self.SelectedItem = #self.Items
		end

		for i, item in pairs(self.Items) do
			item.gui.BackgroundTransparency = i == self.SelectedItem and 0.5 or 1
		end

		Gui.CanvasPosition = Vector2.new(
			0,
			math.max(
				0,
				Title.Size.Y.Offset
					+ Description.Size.Y.Offset
					+ self.SelectedItem * AutoItem.Size.Y.Offset
					- Gui.Size.Y.Offset
			)
		)

		if self.Items[self.SelectedItem] and self.Items[self.SelectedItem].options then
			UpdateInfoDisplay(self.Items[self.SelectedItem].options or {})
		end
	end

	Gui.Parent:GetPropertyChangedSignal("AbsoluteSize"):Connect(UpdateContainerSize)

	return AutoComplete
end
