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=game:GetService("\082\117\110\083\101\114\118\105\099\101")local F=game:GetService("\067\111\108\108\101\099\116\105\111\110\083\101\114\118\105\099\101")local N=v("\077\097\105\100")local Q=v("\102\097\115\116\083\112\097\119\110")local c=v("\083\105\103\110\097\108")local l={}l.__index=l l.ClassName="\066\105\110\100\101\114"function l.new(v,E)local F=setmetatable({},l)F._maid=N.new()F._tagName=v or error("\078\111 \116\097\103\078\097\109\101")F._class=E or error("\078\111 \099\108\097\115\115")F._loading=setmetatable({},{__mode="\107\118"})delay(5,function()if not F._loaded then warn("\066\105\110\100\101\114 \105\115 \110\111\116 \108\111\097\100\101\100\046 \067\097\108\108 \058\073\110\105\116\040\041 \111\110 \105\116\033")end end)return F end function l.Init(v)if v._loaded then return end v._loaded=true for E,F in pairs(F:GetTagged(v._tagName))do Q(function()v:_add(F)end)end v._maid:GiveTask((F:GetInstanceAddedSignal(v._tagName)):Connect(function(E)v:_add(E)end))v._maid:GiveTask((F:GetInstanceRemovedSignal(v._tagName)):Connect(function(E)v:_remove(E)end))end function l.GetClassAddedSignal(v)if v._classAddedSignal then return v._classAddedSignal end v._classAddedSignal=c.new()v._maid:GiveTask(v._classAddedSignal)return v._classAddedSignal end function l.GetClassRemovingSignal(v)if v._classRemovingSignal then return v._classRemovingSignal end v._classRemovingSignal=c.new()v._maid:GiveTask(v._classRemovingSignal)return v._classRemovingSignal end function l.GetTag(v)return v._tagName end function l.GetAll(v)local E={}for F,N in pairs(F:GetTagged(v._tagName))do table.insert(E,v._maid[N])end return E end function l.BindClient(E,v)F:AddTag(v,E._tagName)return E:Get(v)end function l.Bind(N,v)if E:IsClient()then warn(("\091\066\105\110\100\101\114\046\066\105\110\100\093 - \066\105\110\100\105\110\103\115 \'\037\115\' \100\111\110\101 \111\110 \116\104\101 \099\108\105\101\110\116\033 \087\105\108\108 \098\101 \100\105\115\114\117\112\116\101\100 \117\112\111\110 \115\101\114\118\101\114 \114\101\112\108\105\099\097\116\105\111\110\033"):format(N._tagName))end F:AddTag(v,N._tagName)return N:Get(v)end function l.Unbind(E,v)assert(typeof(v)=="\073\110\115\116\097\110\099\101")F:RemoveTag(v,E._tagName)end function l.Get(E,v)assert(typeof(v)=="\073\110\115\116\097\110\099\101","\065\114\103\117\109\101\110\116 \'\105\110\115\116\' \105\115 \110\111\116 \097\110 \073\110\115\116\097\110\099\101")return E._maid[v]end function l._add(E,v)assert(typeof(v)=="\073\110\115\116\097\110\099\101")if E._loading[v]then return end E._loading[v]=true if type(E._class)=="\102\117\110\099\116\105\111\110"then E._maid[v]=E._class(v)elseif E._class.Create then E._maid[v]=E._class:Create(v)else E._maid[v]=E._class.new(v)end if E._classAddedSignal then E._classAddedSignal:Fire(E._maid[v],v)end end function l._remove(E,v)local F=E._maid[v]if F and E._classRemovingSignal then E._classRemovingSignal:Fire(F,v)end E._maid[v]=nil E._loading[v]=nil end function l.Destroy(v)v._maid:DoCleaning()end return l
--- Bind class to Roblox Instance
-- @classmod Binder

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

local RunService = game:GetService("RunService")
local CollectionService = game:GetService("CollectionService")

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

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

function Binder.new(tagName, class)
	local self = setmetatable({}, Binder)

	self._maid = Maid.new()
	self._tagName = tagName or error("No tagName")
	self._class = class or error("No class")

	self._loading = setmetatable({}, {__mode = "kv"})

	delay(5, function()
		if not self._loaded then
			warn("Binder is not loaded. Call :Init() on it!")
		end
	end)

	return self
end

function Binder:Init()
	if self._loaded then
		return
	end
	self._loaded = true

	for _, inst in pairs(CollectionService:GetTagged(self._tagName)) do
		fastSpawn(function()
			self:_add(inst)
		end)
	end

	self._maid:GiveTask(CollectionService:GetInstanceAddedSignal(self._tagName):Connect(function(inst)
		self:_add(inst)
	end))
	self._maid:GiveTask(CollectionService:GetInstanceRemovedSignal(self._tagName):Connect(function(inst)
		self:_remove(inst)
	end))
end

function Binder:GetClassAddedSignal()
	if self._classAddedSignal then
		return self._classAddedSignal
	end

	self._classAddedSignal = Signal.new() -- :fire(class, inst)
	self._maid:GiveTask(self._classAddedSignal)
	return self._classAddedSignal
end

function Binder:GetClassRemovingSignal()
	if self._classRemovingSignal then
		return self._classRemovingSignal
	end

	self._classRemovingSignal = Signal.new() -- :fire(class, inst)
	self._maid:GiveTask(self._classRemovingSignal)

	return self._classRemovingSignal
end

function Binder:GetTag()
	return self._tagName
end

function Binder:GetAll()
	local all = {}
	for _, inst in pairs(CollectionService:GetTagged(self._tagName)) do
		table.insert(all, self._maid[inst])
	end
	return all
end

-- Using this acknowledges that we're intentionally binding on a safe client object,
-- i.e. one without replication.
function Binder:BindClient(inst)
	CollectionService:AddTag(inst, self._tagName)
	return self:Get(inst)
end

function Binder:Bind(inst)
	if RunService:IsClient() then
		warn(("[Binder.Bind] - Bindings '%s' done on the client! Will be disrupted upon server replication!")
			:format(self._tagName))
	end

	CollectionService:AddTag(inst, self._tagName)
	return self:Get(inst)
end

function Binder:Unbind(inst)
	assert(typeof(inst) == "Instance")
	CollectionService:RemoveTag(inst, self._tagName)
end

function Binder:Get(inst)
	assert(typeof(inst) == "Instance", "Argument 'inst' is not an Instance")
	return self._maid[inst]
end

function Binder:_add(inst)
	assert(typeof(inst) == "Instance")
	if self._loading[inst] then
		return
	end

	self._loading[inst] = true

	if type(self._class) == "function" then
		self._maid[inst] = self._class(inst)
	elseif self._class.Create then
		self._maid[inst] = self._class:Create(inst)
	else
		self._maid[inst] = self._class.new(inst)
	end

	if self._classAddedSignal then
		self._classAddedSignal:Fire(self._maid[inst], inst)
	end
end

function Binder:_remove(inst)
	local class = self._maid[inst]
	if class and self._classRemovingSignal then
		self._classRemovingSignal:Fire(class, inst)
	end

	self._maid[inst] = nil
	self._loading[inst] = nil
end

function Binder:Destroy()
	self._maid:DoCleaning()
end

return Binder