local F={}F.__index=F function F.new(T)local r=setmetatable({_bindable=Instance.new("\066\105\110\100\097\098\108\101\069\118\101\110\116"),_connections={};_args={};_threads=0;_id=0},F)if T then T:GiveTask(r)end return r end function F.Proxy(T,r)assert(typeof(T)=="\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 u=F.new(r)u:_setProxy(T)return u end function F.Is(T)return type(T)=="\116\097\098\108\101"and getmetatable(T)==F end function F._setProxy(T,F)assert(typeof(F)=="\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")T:_clearProxy()T._proxyHandle=F:Connect(function(...)T:Fire(...)end)end function F._clearProxy(F)if F._proxyHandle then F._proxyHandle:Disconnect()F._proxyHandle=nil end end function F.Fire(F,...)local T=#F._connections+F._threads if T==0 then return end local r=F._id F._id+=1 F._args[r]={T;{n=select("\035",...),...}}F._threads=0 F._bindable:Fire(r)end function F.Wait(F)F._threads+=1 local T=F._bindable.Event:Wait()local r=F._args[T]r[1]-=1 if r[1]<=0 then F._args[T]=nil end return table.unpack(r[2],1,r[2].n)end function F.WaitPromise(F)return Promise.new(function(T)T(F:Wait())end)end function F.Connect(T,F)local r=Connection.new(T,T._bindable.Event:Connect(function(r)local u=T._args[r]u[1]-=1 if u[1]<=0 then T._args[r]=nil end F(table.unpack(u[2],1,u[2].n))end))table.insert(T._connections,r)return r end function F.DisconnectAll(F)for F,T in ipairs(F._connections)do if T._conn then T._conn:Disconnect()end end F._connections={}F._args={}end function F.Destroy(F)F:DisconnectAll()F:_clearProxy()F._bindable:Destroy()end return F
--------------------------------------------

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
