local C={}C.ClassName="\077\097\105\100"function C.new()return setmetatable({_tasks={}},C)end function C.isMaid(C)return type(C)=="\116\097\098\108\101"and C.ClassName=="\077\097\105\100"end function C.__index(J,Z)if C[Z]then return C[Z]else return J._tasks[Z]end end function C.__newindex(N,Z,J)if C[Z]~=nil then error(("\'\037\115\' \105\115 \114\101\115\101\114\118\101\100"):format(tostring(Z)),2)end local F=N._tasks local z=F[Z]if z==J then return end F[Z]=J if z then if type(z)=="\102\117\110\099\116\105\111\110"then z()elseif typeof(z)=="\082\066\088\083\099\114\105\112\116\067\111\110\110\101\099\116\105\111\110"then z:Disconnect()elseif z.Destroy then z:Destroy()end end end function C.GiveTask(Z,C)if not C then error("\084\097\115\107 \099\097\110\110\111\116 \098\101 \102\097\108\115\101 \111\114 \110\105\108",2)end local J=#Z._tasks+1 Z[J]=C if type(C)=="\116\097\098\108\101"and not C.Destroy then warn("\091\077\097\105\100\046\071\105\118\101\084\097\115\107\093 - \071\097\118\101 \116\097\098\108\101 \116\097\115\107 \119\105\116\104\111\117\116 \046\068\101\115\116\114\111\121\n\n"..debug.traceback())end return J end function C.GivePromise(Z,C)if not C:IsPending()then return C end local J=C.resolved(C)local N=Z:GiveTask(J)J:Finally(function()Z[N]=nil end)return J end function C.DoCleaning(C)local Z=C._tasks for C,J in pairs(Z)do if typeof(J)=="\082\066\088\083\099\114\105\112\116\067\111\110\110\101\099\116\105\111\110"then Z[C]=nil J:Disconnect()end end local J,N=next(Z)while N~=nil do Z[J]=nil if type(N)=="\102\117\110\099\116\105\111\110"then N()elseif typeof(N)=="\082\066\088\083\099\114\105\112\116\067\111\110\110\101\099\116\105\111\110"then N:Disconnect()elseif N.Destroy then N:Destroy()end J,N=next(Z)end end C.Destroy=C.DoCleaning return C
---	Manages the cleaning of events and other things.
-- Useful for encapsulating state and make deconstructors easy
-- @classmod Maid
-- @see Signal

local Maid = {}
Maid.ClassName = "Maid"

--- Returns a new Maid object
-- @constructor Maid.new()
-- @treturn Maid
function Maid.new()
	return setmetatable({
		_tasks = {}
	}, Maid)
end

function Maid.isMaid(value)
	return type(value) == "table" and value.ClassName == "Maid"
end

--- Returns Maid[key] if not part of Maid metatable
-- @return Maid[key] value
function Maid:__index(index)
	if Maid[index] then
		return Maid[index]
	else
		return self._tasks[index]
	end
end

--- Add a task to clean up. Tasks given to a maid will be cleaned when
--  maid[index] is set to a different value.
-- @usage
-- Maid[key] = (function)         Adds a task to perform
-- Maid[key] = (event connection) Manages an event connection
-- Maid[key] = (Maid)             Maids can act as an event connection, allowing a Maid to have other maids to clean up.
-- Maid[key] = (Object)           Maids can cleanup objects with a `Destroy` method
-- Maid[key] = nil                Removes a named task. If the task is an event, it is disconnected. If it is an object,
--                                it is destroyed.
function Maid:__newindex(index, newTask)
	if Maid[index] ~= nil then
		error(("'%s' is reserved"):format(tostring(index)), 2)
	end

	local tasks = self._tasks
	local oldTask = tasks[index]

	if oldTask == newTask then
		return
	end

	tasks[index] = newTask

	if oldTask then
		if type(oldTask) == "function" then
			oldTask()
		elseif typeof(oldTask) == "RBXScriptConnection" then
			oldTask:Disconnect()
		elseif oldTask.Destroy then
			oldTask:Destroy()
		end
	end
end

--- Same as indexing, but uses an incremented number as a key.
-- @param task An item to clean
-- @treturn number taskId
function Maid:GiveTask(task)
	if not task then
		error("Task cannot be false or nil", 2)
	end

	local taskId = #self._tasks+1
	self[taskId] = task

	if type(task) == "table" and (not task.Destroy) then
		warn("[Maid.GiveTask] - Gave table task without .Destroy\n\n" .. debug.traceback())
	end

	return taskId
end

function Maid:GivePromise(promise)
	if not promise:IsPending() then
		return promise
	end

	local newPromise = promise.resolved(promise)
	local id = self:GiveTask(newPromise)

	-- Ensure GC
	newPromise:Finally(function()
		self[id] = nil
	end)

	return newPromise
end

--- Cleans up all tasks.
-- @alias Destroy
function Maid:DoCleaning()
	local tasks = self._tasks

	-- Disconnect all events first as we know this is safe
	for index, task in pairs(tasks) do
		if typeof(task) == "RBXScriptConnection" then
			tasks[index] = nil
			task:Disconnect()
		end
	end

	-- Clear out tasks table completely, even if clean up tasks add more tasks to the maid
	local index, task = next(tasks)
	while task ~= nil do
		tasks[index] = nil
		if type(task) == "function" then
			task()
		elseif typeof(task) == "RBXScriptConnection" then
			task:Disconnect()
		elseif task.Destroy then
			task:Destroy()
		end
		index, task = next(tasks)
	end
end

--- Alias for DoCleaning()
-- @function Destroy
Maid.Destroy = Maid.DoCleaning

return Maid