local v=require((game:GetService("\082\101\112\108\105\099\097\116\101\100\083\116\111\114\097\103\101")):WaitForChild("\078\101\118\101\114\109\111\114\101"))local E=v("\077\097\105\100")local F=v("\083\105\103\110\097\108")local N={}N.ClassName="\066\111\117\110\100\076\105\110\107\067\111\108\108\101\099\116\105\111\110"N.__index=N function N.new(v,Q,c)local l=setmetatable({},N)l._maid=E.new()l._linkName=Q or error("\078\111 \108\105\110\107\078\097\109\101")l._binder=v or error("\078\111 \098\105\110\100\101\114")l.ClassAdded=F.new()l.ClassRemoved=F.new()l._classes={}l._canidates={}l._linkCanidate={}l._linkValues={}l._maid:GiveTask((l._binder:GetClassAddedSignal()):Connect(function(...)l:_handleNewClassBound(...)end))l._maid:GiveTask((l._binder:GetClassRemovingSignal()):Connect(function(v)l:_removeClass(v)end))l:TrackParent(c)return l end function N.HasClass(E,v)return E._classes[v]end function N.GetClasses(v)local E={}for v,F in pairs(v._classes)do table.insert(E,v)end return E end function N.HasClass(E,v)return E._classes[v]~=nil end function N.TrackParent(E,v)assert(v)E._maid:GiveTask(v.ChildAdded:Connect(function(v)if v:IsA("\079\098\106\101\099\116\086\097\108\117\101")and v.Name==E._linkName then E:_handleNewLink(v)end end))E._maid:GiveTask(v.ChildRemoved:Connect(function(v)E:_removeLink(v)end))for v,F in pairs(v:GetChildren())do if F:IsA("\079\098\106\101\099\116\086\097\108\117\101")and F.Name==E._linkName then E:_handleNewLink(F)end end end function N._removeLink(E,v)E._maid[v]=nil end function N._handleNewLink(F,v)local N=E.new()N:GiveTask(v.Changed:Connect(function()F:_handleLinkChanged(v)end))N:GiveTask(function()F:_removeLinkCanidates(v)end)F._maid[v]=N F:_handleLinkChanged(v)end function N._handleLinkChanged(E,v)E:_removeLinkCanidates(v)if v.Value then E:_addCanidate(v,v.Value)end end function N._removeLinkCanidates(E,v)local F=E._linkCanidate[v]if not F then return end E._linkCanidate[v]=nil if not E._canidates[F]then error("\091\066\111\117\110\100\076\105\110\107\067\111\108\108\101\099\116\105\111\110\093 - \071\111\116 \108\105\110\107 \099\097\110\105\100\097\116\101 \116\104\097\116 \105\115\110\'\'\116 \114\101\097\108\046 \084\104\105\115 \115\104\111\117\108\100\110\'\116 \104\097\112\112\101\110\046")return end local N=E._canidates[F]N[v]=nil if not next(N)then E:_removeCanidate(F)end end function N._removeCanidate(E,v)E._canidates[v]=nil local F=E._binder:Get(v)if not F then return end E:_removeClass(F)end function N._addCanidate(F,v,E)assert(not F._linkCanidate[v],"\083\104\111\117\108\100 \110\111\116 \104\097\118\101 \101\120\105\115\116\105\110\103 \099\097\110\105\100\097\116\101 \115\101\116 \102\111\114 \108\105\110\107")F._linkCanidate[v]=E if not F._canidates[E]then F._canidates[E]={}end F._canidates[E][v]=true local N=F._binder:Get(E)if not N then return end F:_addClass(N)end function N._removeClass(E,v)if not E._classes[v]then return end E._classes[v]=nil E.ClassRemoved:Fire(v)end function N._addClass(E,v)if E._classes[v]then return end E._classes[v]=true E.ClassAdded:Fire(v)end function N._handleNewClassBound(F,v,E)if not F._canidates[E]then return end F:_addClass(v)end function N.Destroy(v)v._maid:Destroy()setmetatable(v,nil)end return N
--- Handles searching for bound objects following links (object values) under a parent
-- with a specific name.
-- @classmod BoundLinkCollection
-- @author Quenty

local require = require(game:GetService("ReplicatedStorage"):WaitForChild("Nevermore"))

local Maid = require("Maid")
local Signal = require("Signal")

local BoundLinkCollection = {}
BoundLinkCollection.ClassName = "BoundLinkCollection"
BoundLinkCollection.__index = BoundLinkCollection

function BoundLinkCollection.new(binder, linkName, parent)
	local self = setmetatable({}, BoundLinkCollection)

	self._maid = Maid.new()

	self._linkName = linkName or error("No linkName")
	self._binder = binder or error("No binder")

	self.ClassAdded = Signal.new() -- :Fire(class)
	self.ClassRemoved = Signal.new() -- :Fire(class)

	self._classes = {} -- [class] = true
	self._canidates = {} -- [inst] = { [objValue] = true }
	self._linkCanidate = {} -- [objValue] = [inst]
	self._linkValues = {} -- [objValue] = true

	self._maid:GiveTask(self._binder:GetClassAddedSignal():Connect(function(...)
		self:_handleNewClassBound(...)
	end))
	self._maid:GiveTask(self._binder:GetClassRemovingSignal():Connect(function(class)
		self:_removeClass(class)
	end))

	self:TrackParent(parent)

	return self
end

function BoundLinkCollection:HasClass(class)
	return self._classes[class]
end

function BoundLinkCollection:GetClasses()
	local list = {}
	for class, _ in pairs(self._classes) do
		table.insert(list, class)
	end
	return list
end

function BoundLinkCollection:HasClass(class)
	return self._classes[class] ~= nil
end

function BoundLinkCollection:TrackParent(parent)
	assert(parent)

	self._maid:GiveTask(parent.ChildAdded:Connect(function(child)
		if child:IsA("ObjectValue") and child.Name == self._linkName then
			self:_handleNewLink(child)
		end
	end))

	self._maid:GiveTask(parent.ChildRemoved:Connect(function(child)
		self:_removeLink(child)
	end))

	for _, child in pairs(parent:GetChildren()) do
		if child:IsA("ObjectValue") and child.Name == self._linkName then
			self:_handleNewLink(child)
		end
	end
end

function BoundLinkCollection:_removeLink(objValue)
	self._maid[objValue] = nil
end

function BoundLinkCollection:_handleNewLink(objValue)
	local maid = Maid.new()

	maid:GiveTask(objValue.Changed:Connect(function()
		self:_handleLinkChanged(objValue)
	end))

	maid:GiveTask(function()
		self:_removeLinkCanidates(objValue)
	end)

	self._maid[objValue] = maid

	self:_handleLinkChanged(objValue)
end

function BoundLinkCollection:_handleLinkChanged(objValue)
	self:_removeLinkCanidates(objValue)

	if objValue.Value then
		self:_addCanidate(objValue, objValue.Value)
	end
end

function BoundLinkCollection:_removeLinkCanidates(objValue)
	local canidate = self._linkCanidate[objValue]
	if not canidate then
		return
	end

	self._linkCanidate[objValue] = nil

	if not self._canidates[canidate] then
		error("[BoundLinkCollection] - Got link canidate that isn''t real. This shouldn't happen.")
		return
	end

	local canidateLinks = self._canidates[canidate]
	canidateLinks[objValue] = nil

	if not next(canidateLinks) then
		self:_removeCanidate(canidate)
	end
end

function BoundLinkCollection:_removeCanidate(canidate)
	self._canidates[canidate] = nil

	local class = self._binder:Get(canidate)
	if not class then
		return
	end

	self:_removeClass(class)
end

function BoundLinkCollection:_addCanidate(objValue, canidate)
	assert(not self._linkCanidate[objValue], "Should not have existing canidate set for link")

	self._linkCanidate[objValue] = canidate

	if not self._canidates[canidate] then
		self._canidates[canidate] = {}
	end
	self._canidates[canidate][objValue] = true

	local class = self._binder:Get(canidate)
	if not class then
		return
	end
	self:_addClass(class)
end

function BoundLinkCollection:_removeClass(class)
	if not self._classes[class] then
		return
	end

	self._classes[class] = nil
	self.ClassRemoved:Fire(class)
end

function BoundLinkCollection:_addClass(class)
	if self._classes[class] then
		return
	end

	self._classes[class] = true
	self.ClassAdded:Fire(class)
end

function BoundLinkCollection:_handleNewClassBound(class, inst)
	if not self._canidates[inst] then
		return
	end

	self:_addClass(class)
end

function BoundLinkCollection:Destroy()
	self._maid:Destroy()
	setmetatable(self, nil)
end

return BoundLinkCollection
