local function u(u,r)local m={}VisitAllInstances(u,r,function(u,r)for r,S in pairs(r)do local H=S.Keyframes local P=0 for S,H in ipairs(H)do local n=TweenInfo.new(H.Time-P,H.EasingStyle,H.EasingDirection,0,false,0)local e={[r]=H.Value}local C=TweenService:Create(u,n,e)table.insert(m,{Tween=C,DelayTime=P})P=H.Time end end end)return m end local r={}r.__index=r function r.new(m,S)local H={running=false;root=m;tweenTable=S;tweens=u(m,S)}setmetatable(H,r)return H end function r.ResetValues(u)VisitAllInstances(u.root,u.tweenTable,function(u,r)for r,m in pairs(r)do local S=m.InitialValue u[r]=S end end)end function r.PlayTweens(r,u)if r.running then r:PauseTweens()end r.running=true local m={}for u,r in pairs(r.tweens)do local S=r.Tween local H=r.DelayTime if H==0 then S:Play()else table.insert(m,{Tween=S;DelayTime=H;Playing=false})end end local S local H=tick()local P=false S=RunService.Heartbeat:Connect(function()local n=tick()-H if not r.running then r.running=false S:Disconnect()if u then u()end return end if P then local m=true for u,r in pairs(r.tweens)do local S=r.Tween if S.PlaybackState==Enum.PlaybackState.Playing then m=false end end if m then r.running=false S:Disconnect()if u then u()end return end else local u=true for r,m in pairs(m)do if not m.Playing then if n>m.DelayTime then m.Playing=true m.Tween:Play()else u=false end end end P=u end end)end function r.PauseTweens(u)u.running=false for u,r in pairs(u.tweens)do local m=r.Tween m:Cancel()end end return r
-- Builds a set of runnable Tweens from a table of instances and values
local function BuildTweens(root, tweenTable)
	local tweens = {}

	VisitAllInstances(root, tweenTable, function(instance, props)
		for prop, values in pairs(props) do
			local keyframes = values.Keyframes
			local lastTime = 0
			for _, keyframe in ipairs(keyframes) do
				local tweenInfo =
					TweenInfo.new(keyframe.Time - lastTime, keyframe.EasingStyle, keyframe.EasingDirection, 0, false, 0)
				local propTable = {
					[prop] = keyframe.Value,
				}
				local tween = TweenService:Create(instance, tweenInfo, propTable)
				table.insert(tweens, {
					Tween = tween,
					DelayTime = lastTime,
				})
				lastTime = keyframe.Time
			end
		end
	end)

	return tweens
end

local TweenUtilities = {}
TweenUtilities.__index = TweenUtilities

function TweenUtilities.new(root, tweenTable)
	local self = {
		running = false,
		root = root,
		tweenTable = tweenTable,
		tweens = BuildTweens(root, tweenTable),
	}
	setmetatable(self, TweenUtilities)
	return self
end

function TweenUtilities:ResetValues()
	VisitAllInstances(self.root, self.tweenTable, function(instance, props)
		for prop, values in pairs(props) do
			local initialValue = values.InitialValue
			instance[prop] = initialValue
		end
	end)
end

function TweenUtilities:PlayTweens(callback)
	if self.running then
		self:PauseTweens()
	end
	self.running = true

	local delayedTweens = {}
	for _, props in pairs(self.tweens) do
		local tween = props.Tween
		local delayTime = props.DelayTime
		if delayTime == 0 then
			tween:Play()
		else
			table.insert(delayedTweens, {
				Tween = tween,
				DelayTime = delayTime,
				Playing = false,
			})
		end
	end

	local heartbeatConnection
	local start = tick()
	local tweensDone = false
	heartbeatConnection = RunService.Heartbeat:Connect(function()
		local now = tick() - start
		if not self.running then
			self.running = false
			heartbeatConnection:Disconnect()
			if callback then
				callback()
			end
			return
		end
		if tweensDone then
			local allDone = true
			for _, props in pairs(self.tweens) do
				local tween = props.Tween
				if tween.PlaybackState == Enum.PlaybackState.Playing then
					allDone = false
				end
			end
			if allDone then
				self.running = false
				heartbeatConnection:Disconnect()
				if callback then
					callback()
				end
				return
			end
		else
			local allPlaying = true
			for _, props in pairs(delayedTweens) do
				if not props.Playing then
					if now > props.DelayTime then
						props.Playing = true
						props.Tween:Play()
					else
						allPlaying = false
					end
				end
			end
			tweensDone = allPlaying
		end
	end)
end

function TweenUtilities:PauseTweens()
	self.running = false
	for _, props in pairs(self.tweens) do
		local tween = props.Tween
		tween:Cancel()
	end
end

return TweenUtilities
