local u=require(script.Parent.Logging)local q="\067\104\097\110\103\101\046"local h={Disabled="\068\105\115\097\098\108\101\100";Suspended="\083\117\115\112\101\110\100\101\100",Enabled="\069\110\097\098\108\101\100"}local S={}S.__index=S function S.new(u)local q=setmetatable({_suspendedEventQueue={};_connections={};_listeners={};_status=h.Disabled,_isResuming=false;_instance=u},S)return q end function S.connectEvent(h,u,q)h:_connect(u,h._instance[u],q)end function S.connectPropertyChange(S,u,h)local Y=S._instance:GetPropertyChangedSignal(u)S:_connect(q..u,Y,h)end function S._connect(Y,u,q,S)if S==nil then if Y._connections[u]~=nil then Y._connections[u]:Disconnect()Y._connections[u]=nil end Y._listeners[u]=nil else if Y._connections[u]==nil then Y._connections[u]=q:Connect(function(...)if Y._status==h.Enabled then Y._listeners[u](Y._instance,...)elseif Y._status==h.Suspended then local q=select("\035",...)table.insert(Y._suspendedEventQueue,{u,q,...})end end)end Y._listeners[u]=S end end function S.suspend(u)u._status=h.Suspended end function S.resume(q)if q._isResuming then return end q._isResuming=true local S=1 while S<=#q._suspendedEventQueue do local h=q._suspendedEventQueue[S]local Y=q._listeners[h[1]]local p=h[2]if Y~=nil then local S=coroutine.create(Y)local t,e=coroutine.resume(S,q._instance,unpack(h,3,2+p))if not t then u.warn("\037\115",e)end end S=S+1 end q._isResuming=false q._status=h.Enabled q._suspendedEventQueue={}end return S
--[[
	A manager for a single host virtual node's connected events.
]]

local Logging = require(script.Parent.Logging)

local CHANGE_PREFIX = "Change."

local EventStatus = {
	-- No events are processed at all; they're silently discarded
	Disabled = "Disabled",

	-- Events are stored in a queue; listeners are invoked when the manager is resumed
	Suspended = "Suspended",

	-- Event listeners are invoked as the events fire
	Enabled = "Enabled",
}

local SingleEventManager = {}
SingleEventManager.__index = SingleEventManager

function SingleEventManager.new(instance)
	local self = setmetatable({
		-- The queue of suspended events
		_suspendedEventQueue = {},

		-- All the event connections being managed
		-- Events are indexed by a string key
		_connections = {},

		-- All the listeners being managed
		-- These are stored distinctly from the connections
		-- Connections can have their listeners replaced at runtime
		_listeners = {},

		-- The suspension status of the manager
		-- Managers start disabled and are "resumed" after the initial render
		_status = EventStatus.Disabled,

		-- If true, the manager is processing queued events right now.
		_isResuming = false,

		-- The Roblox instance the manager is managing
		_instance = instance,
	}, SingleEventManager)

	return self
end

function SingleEventManager:connectEvent(key, listener)
	self:_connect(key, self._instance[key], listener)
end

function SingleEventManager:connectPropertyChange(key, listener)
	local event = self._instance:GetPropertyChangedSignal(key)
	self:_connect(CHANGE_PREFIX .. key, event, listener)
end

function SingleEventManager:_connect(eventKey, event, listener)
	-- If the listener doesn't exist we can just disconnect the existing connection
	if listener == nil then
		if self._connections[eventKey] ~= nil then
			self._connections[eventKey]:Disconnect()
			self._connections[eventKey] = nil
		end

		self._listeners[eventKey] = nil
	else
		if self._connections[eventKey] == nil then
			self._connections[eventKey] = event:Connect(function(...)
				if self._status == EventStatus.Enabled then
					self._listeners[eventKey](self._instance, ...)
				elseif self._status == EventStatus.Suspended then
					-- Store this event invocation to be fired when resume is
					-- called.

					local argumentCount = select("#", ...)
					table.insert(self._suspendedEventQueue, { eventKey, argumentCount, ... })
				end
			end)
		end

		self._listeners[eventKey] = listener
	end
end

function SingleEventManager:suspend()
	self._status = EventStatus.Suspended
end

function SingleEventManager:resume()
	-- If we're already resuming events for this instance, trying to resume
	-- again would cause a disaster.
	if self._isResuming then
		return
	end

	self._isResuming = true

	local index = 1

	-- More events might be added to the queue when evaluating events, so we
	-- need to be careful in order to preserve correct evaluation order.
	while index <= #self._suspendedEventQueue do
		local eventInvocation = self._suspendedEventQueue[index]
		local listener = self._listeners[eventInvocation[1]]
		local argumentCount = eventInvocation[2]

		-- The event might have been disconnected since suspension started; in
		-- this case, we drop the event.
		if listener ~= nil then
			-- Wrap the listener in a coroutine to catch errors and handle
			-- yielding correctly.
			local listenerCo = coroutine.create(listener)
			local success, result =
				coroutine.resume(listenerCo, self._instance, unpack(eventInvocation, 3, 2 + argumentCount))

			-- If the listener threw an error, we log it as a warning, since
			-- there's no way to write error text in Roblox Lua without killing
			-- our thread!
			if not success then
				Logging.warn("%s", result)
			end
		end

		index = index + 1
	end

	self._isResuming = false
	self._status = EventStatus.Enabled
	self._suspendedEventQueue = {}
end

return SingleEventManager
