ClientOrchestration.registerClientComponents=function(W)if W.orchestrationTime<ClientOrchestration.lastCalledOrchestrationTime then ClientOrchestration._warn("\065\116\116\101\109\112\116\101\100 \116\111 \111\114\099\104\101\115\116\114\097\116\101 \097\110 \111\108\100\101\114 \115\099\101\110\101 \111\110 \116\104\101 \099\108\105\101\110\116\058 "..W.name)return end if W.orchestrationTime==ClientOrchestration.lastCalledOrchestrationTime then ClientOrchestration._warn("\065\116\116\101\109\112\116\101\100 \116\111 \111\114\099\104\101\115\116\114\097\116\101 \097 \115\099\101\110\101 \119\105\116\104 \115\097\109\101 \116\105\099\107 \116\105\109\101\046 \068\111\117\098\108\101 \099\104\101\099\107 \116\104\097\116 \121\111\117\'\114\101 \110\111\116 \108\111\097\100\105\110\103 \097 \115\099\101\110\101 \050\120")ClientOrchestration._warn(W.name)return end if W.schema.Client==nil then ClientOrchestration._warn("\078\111 \115\099\104\101\109\097 \067\108\105\101\110\116 \102\105\108\101 \112\114\111\118\105\100\101\100 \111\110 \116\104\101 \099\108\105\101\110\116 \102\111\114 \115\099\101\110\101\058 "..(W.name.."\046 \073\115 \116\104\105\115 \105\110\116\101\110\100\101\100\063"))return end ClientOrchestration.isSeekable=false ClientOrchestration.lastCalledOrchestrationTime=W.orchestrationTime ClientOrchestration.cleanUp()local k=ClientOrchestration._require(W.schema.Client)local w=ClientOrchestration._require(W.schemaProcessor)ClientOrchestration.SchemaProcessorInstance=w.new(k,W.timePosition)local m=ClientOrchestration.setupSchema()if W.orchestrationTime<ClientOrchestration.lastCalledOrchestrationTime then ClientOrchestration._warn("\078\111 \108\111\110\103\101\114 \112\114\111\099\101\115\115\105\110\103 \111\117\116\100\097\116\101\100 \115\099\101\110\101\058 "..W.name)return end if m then ClientOrchestration.processSchema()end if W.orchestrationTime<ClientOrchestration.lastCalledOrchestrationTime then ClientOrchestration._warn("\078\111 \108\111\110\103\101\114 \112\114\111\099\101\115\115\105\110\103 \111\117\116\100\097\116\101\100 \115\099\101\110\101\058 "..W.name)return end local z=W.name or"\068\101\102\097\117\108\116"ClientOrchestration.ClientSceneMetadataEvent:Fire("\080\097\115\115",z,ClientOrchestration.SchemaProcessorInstance.timePosition,W.timeLength)if ClientOrchestration.ClientSceneMetadataRequestConnection then ClientOrchestration.ClientSceneMetadataRequestConnection:Disconnect()end ClientOrchestration.ClientSceneMetadataRequestConnection=ClientOrchestration.ClientSceneMetadataEvent.Event:Connect(function(k)if k=="\082\101\113\117\101\115\116"then ClientOrchestration.ClientSceneMetadataEvent:Fire("\080\097\115\115",z,ClientOrchestration.SchemaProcessorInstance.timePosition,W.timeLength)end end)end ClientOrchestration.seek=function(W)local k=ClientOrchestration.lastCalledOrchestrationTime while not ClientOrchestration.isSeekable do local W=k~=ClientOrchestration.lastCalledOrchestrationTime if W then ClientOrchestration._warn("\078\101\119 \115\099\101\110\101 \111\114\099\104\101\115\116\114\097\116\105\110\103\044 \099\097\110\099\101\108\108\105\110\103 \115\101\101\107")return end ClientOrchestration.taskLib.wait()end if ClientOrchestration.SchemaProcessorInstance==nil then ClientOrchestration._warn("\083\099\104\101\109\097 \080\114\111\099\101\115\115\111\114 \073\110\115\116\097\110\099\101 \104\097\115\110\'\116 \098\101\101\110 \099\114\101\097\116\101\100 \111\110 \099\108\105\101\110\116")return end ClientOrchestration.cleanUp()ClientOrchestration.SchemaProcessorInstance.timePosition.Value=W ClientOrchestration.processSchema()end local W=false return function(k)if W then error("\067\108\105\101\110\116 \079\114\099\104\101\115\116\114\097\116\105\111\110 \104\097\115 \097\108\114\101\097\100\121 \098\101\101\110 \099\097\108\108\101\100")return end if k then for W,k in pairs(k)do ClientOrchestration[W]=k end end ClientOrchestration.SendMediaControllerConfig.OnClientEvent:Connect(ClientOrchestration.registerClientComponents)ClientOrchestration.SeekPermissionsEvent.OnClientEvent:Connect(ClientOrchestration.handleSeekPermissionsEvent)ClientOrchestration.Seek.OnClientEvent:Connect(ClientOrchestration.seek)ClientOrchestration.SceneEnded.OnClientEvent:Connect(ClientOrchestration.handleSceneEnded)W=true return ClientOrchestration end
-- note: maybe use playfab to capture errors
ClientOrchestration.registerClientComponents = function(config)
	if config.orchestrationTime < ClientOrchestration.lastCalledOrchestrationTime then
		ClientOrchestration._warn("Attempted to orchestrate an older scene on the client: " .. config.name)
		return
	end

	if config.orchestrationTime == ClientOrchestration.lastCalledOrchestrationTime then
		ClientOrchestration._warn(
			"Attempted to orchestrate a scene with same tick time. Double check that you're not loading a scene 2x"
		)
		ClientOrchestration._warn(config.name)
		return
	end

	if config.schema.Client == nil then
		ClientOrchestration._warn(
			"No schema Client file provided on the client for scene: " .. config.name .. ". Is this intended?"
		)
		return
	end

	ClientOrchestration.isSeekable = false

	ClientOrchestration.lastCalledOrchestrationTime = config.orchestrationTime
	ClientOrchestration.cleanUp()

	local schema = ClientOrchestration._require(config.schema.Client)
	local SchemaProcessor = ClientOrchestration._require(config.schemaProcessor)
	ClientOrchestration.SchemaProcessorInstance = SchemaProcessor.new(schema, config.timePosition)

	-- Run setup before processing
	local setupSuccess = ClientOrchestration.setupSchema()

	if config.orchestrationTime < ClientOrchestration.lastCalledOrchestrationTime then
		ClientOrchestration._warn("No longer processing outdated scene: " .. config.name)
		return
	end

	if setupSuccess then
		ClientOrchestration.processSchema()
	end

	if config.orchestrationTime < ClientOrchestration.lastCalledOrchestrationTime then
		ClientOrchestration._warn("No longer processing outdated scene: " .. config.name)
		return
	end

	local name = config.name or "Default"
	-- If the player has seek permissions, then update the seek UI
	ClientOrchestration.ClientSceneMetadataEvent:Fire(
		"Pass",
		name,
		ClientOrchestration.SchemaProcessorInstance.timePosition,
		config.timeLength
	)

	if ClientOrchestration.ClientSceneMetadataRequestConnection then
		ClientOrchestration.ClientSceneMetadataRequestConnection:Disconnect()
	end

	ClientOrchestration.ClientSceneMetadataRequestConnection = ClientOrchestration.ClientSceneMetadataEvent.Event:Connect(
		function(Type)
			if Type == "Request" then
				ClientOrchestration.ClientSceneMetadataEvent:Fire(
					"Pass",
					name,
					ClientOrchestration.SchemaProcessorInstance.timePosition,
					config.timeLength
				)
			end
		end
	)
end

ClientOrchestration.seek = function(timeToPlay)
	local lastCalledOrchestrationTime = ClientOrchestration.lastCalledOrchestrationTime

	while not ClientOrchestration.isSeekable do
		local wasNewSceneCalled = lastCalledOrchestrationTime ~= ClientOrchestration.lastCalledOrchestrationTime
		if wasNewSceneCalled then
			ClientOrchestration._warn("New scene orchestrating, cancelling seek")
			return
		end
		ClientOrchestration.taskLib.wait()
	end

	if ClientOrchestration.SchemaProcessorInstance == nil then
		ClientOrchestration._warn("Schema Processor Instance hasn't been created on client")
		return
	end

	ClientOrchestration.cleanUp()

	-- This is in Process fires faster than setTime has replicated for the client
	ClientOrchestration.SchemaProcessorInstance.timePosition.Value = timeToPlay
	ClientOrchestration.processSchema()
end

local hasBeenCalled = false
return function(stubs)
	if hasBeenCalled then
		error("Client Orchestration has already been called")
		return
	end

	-- Used for testing only
	if stubs then
		for i, v in pairs(stubs) do
			ClientOrchestration[i] = v
		end
	end

	ClientOrchestration.SendMediaControllerConfig.OnClientEvent:Connect(ClientOrchestration.registerClientComponents)
	ClientOrchestration.SeekPermissionsEvent.OnClientEvent:Connect(ClientOrchestration.handleSeekPermissionsEvent)
	ClientOrchestration.Seek.OnClientEvent:Connect(ClientOrchestration.seek)
	ClientOrchestration.SceneEnded.OnClientEvent:Connect(ClientOrchestration.handleSceneEnded)

	hasBeenCalled = true

	return ClientOrchestration
end
