--[=[
	Whenever all returned brios are dead, emits this value wrapped
	in a brio.

	@param valueToEmitWhileAllDead T
	@return (source: Observable<Brio<U>>) -> Observable<Brio<U | T>>
]=]

function RxBrioUtils.emitWhileAllDead(valueToEmitWhileAllDead)
	return function(source)
		return Observable.new(function(sub)
			local topMaid = Maid.new()

			local subscribed = true
			topMaid:GiveTask(function()
				subscribed = false
			end)
			local aliveBrios = {}
			local fired = false

			local function updateBrios()
				if not subscribed then -- No work if we don't need to.
					return
				end

				aliveBrios = BrioUtils.aliveOnly(aliveBrios)
				if next(aliveBrios) then
					topMaid._lastBrio = nil
				else
					local newBrio = Brio.new(valueToEmitWhileAllDead)
					topMaid._lastBrio = newBrio
					sub:Fire(newBrio)
				end

				fired = true
			end

			local function handleNewBrio(brio)
				-- Could happen due to throttle or delay...
				if brio:IsDead() then
					return
				end

				local maid = Maid.new()
				topMaid[maid] = maid -- Use maid as key so it's unique (reemitted brio)

				maid:GiveTask(function() -- GC properly
					topMaid[maid] = nil
					updateBrios()
				end)
				maid:GiveTask(brio:GetDiedSignal():Connect(function()
					topMaid[maid] = nil
				end))

				table.insert(aliveBrios, brio)
				updateBrios()
			end

			topMaid:GiveTask(source:Subscribe(
				function(brio)
					if not Brio.isBrio(brio) then
						warn(("[RxBrioUtils.emitWhileAllDead] - Not a brio, %q"):format(tostring(brio)))
						topMaid._lastBrio = nil
						sub:Fail("Not a brio")
						return
					end

					handleNewBrio(brio)
				end,
				function(...)
					sub:Fail(...)
				end,
				function(...)
					sub:Complete(...)
				end))

			-- Make sure we emit an empty list if we discover nothing
			if not fired then
				updateBrios()
			end

			return topMaid
		end)
	end
end
