local a={}a.__index=a function a.new(Z)local T=setmetatable({_bindable=Instance.new("\066\105\110\100\097\098\108\101\069\118\101\110\116");_connections={},_args={},_threads=0;_id=0},a)if Z then Z:GiveTask(T)end return T end function a.Proxy(Z,T)assert(typeof(Z)=="\082\066\088\083\099\114\105\112\116\083\105\103\110\097\108","\065\114\103\117\109\101\110\116 \035\049 \109\117\115\116 \098\101 \111\102 \116\121\112\101 \082\066\088\083\099\114\105\112\116\083\105\103\110\097\108")local D=a.new(T)D:_setProxy(Z)return D end function a.Is(Z)return type(Z)=="\116\097\098\108\101"and getmetatable(Z)==a end function a._setProxy(Z,a)assert(typeof(a)=="\082\066\088\083\099\114\105\112\116\083\105\103\110\097\108","\065\114\103\117\109\101\110\116 \035\049 \109\117\115\116 \098\101 \111\102 \116\121\112\101 \082\066\088\083\099\114\105\112\116\083\105\103\110\097\108")Z:_clearProxy()Z._proxyHandle=a:Connect(function(...)Z:Fire(...)end)end function a._clearProxy(a)if a._proxyHandle then a._proxyHandle:Disconnect()a._proxyHandle=nil end end function a.Fire(a,...)local Z=#a._connections+a._threads if Z==0 then return end local T=a._id a._id+=1 a._args[T]={Z;{n=select("\035",...);...}}a._threads=0 a._bindable:Fire(T)end function a.Wait(a)a._threads+=1 local Z=a._bindable.Event:Wait()local T=a._args[Z]T[1]-=1 if T[1]<=0 then a._args[Z]=nil end return table.unpack(T[2],1,T[2].n)end function a.WaitPromise(a)return Promise.new(function(Z)Z(a:Wait())end)end function a.Connect(Z,a)local T=Connection.new(Z,Z._bindable.Event:Connect(function(T)local D=Z._args[T]D[1]-=1 if D[1]<=0 then Z._args[T]=nil end a(table.unpack(D[2],1,D[2].n))end))table.insert(Z._connections,T)return T end function a.DisconnectAll(a)for a,Z in ipairs(a._connections)do if Z._conn then Z._conn:Disconnect()end end a._connections={}a._args={}end function a.Destroy(a)a:DisconnectAll()a:_clearProxy()a._bindable:Destroy()end return a


local Signal = {}
Signal.__index = Signal


function Signal.new(maid)
	local self = setmetatable({
		_bindable = Instance.new("BindableEvent");
		_connections = {};
		_args = {};
		_threads = 0;
		_id = 0;
	}, Signal)
	if (maid) then
		maid:GiveTask(self)
	end
	return self
end


function Signal.Proxy(rbxScriptSignal, maid)
	assert(typeof(rbxScriptSignal) == "RBXScriptSignal", "Argument #1 must be of type RBXScriptSignal")
	local signal = Signal.new(maid)
	signal:_setProxy(rbxScriptSignal)
	return signal
end


function Signal.Is(obj)
	return (type(obj) == "table" and getmetatable(obj) == Signal)
end


function Signal:_setProxy(rbxScriptSignal)
	assert(typeof(rbxScriptSignal) == "RBXScriptSignal", "Argument #1 must be of type RBXScriptSignal")
	self:_clearProxy()
	self._proxyHandle = rbxScriptSignal:Connect(function(...)
		self:Fire(...)
	end)
end


function Signal:_clearProxy()
	if (self._proxyHandle) then
		self._proxyHandle:Disconnect()
		self._proxyHandle = nil
	end
end


function Signal:Fire(...)
	local totalListeners = (#self._connections + self._threads)
	if (totalListeners == 0) then return end
	local id = self._id
	self._id += 1
	self._args[id] = {totalListeners, {n = select("#", ...), ...}}
	self._threads = 0
	self._bindable:Fire(id)
end


function Signal:Wait()
	self._threads += 1
	local id = self._bindable.Event:Wait()
	local args = self._args[id]
	args[1] -= 1
	if (args[1] <= 0) then
		self._args[id] = nil
	end
	return table.unpack(args[2], 1, args[2].n)
end


function Signal:WaitPromise()
	return Promise.new(function(resolve)
		resolve(self:Wait())
	end)
end


function Signal:Connect(handler)
	local connection = Connection.new(self, self._bindable.Event:Connect(function(id)
		local args = self._args[id]
		args[1] -= 1
		if (args[1] <= 0) then
			self._args[id] = nil
		end
		handler(table.unpack(args[2], 1, args[2].n))
	end))
	table.insert(self._connections, connection)
	return connection
end


function Signal:DisconnectAll()
	for _,c in ipairs(self._connections) do
		if (c._conn) then
			c._conn:Disconnect()
		end
	end
	self._connections = {}
	self._args = {}
end


function Signal:Destroy()
	self:DisconnectAll()
	self:_clearProxy()
	self._bindable:Destroy()
end


return Signal
