local o={}o.Version=script.Parent.Version.Value o.Player=(game:GetService("\080\108\097\121\101\114\115")).LocalPlayer o.Controllers={}o.Util=script.Parent.Util local Z=require(o.Util.Promise)local h=require(o.Util.Loader)local K=require(o.Util.Ser)local H=require(o.Util.Remote.ClientRemoteSignal)local V=require(o.Util.Remote.ClientRemoteProperty)local e=require(o.Util.TableUtil)local I={}local c=script.Parent:WaitForChild("\083\101\114\118\105\099\101\115")local w=false local g=false local l=Instance.new("\066\105\110\100\097\098\108\101\069\118\101\110\116")local function b(o,h)local e={}if h:FindFirstChild("\082\070")then for o,h in ipairs(h.RF:GetChildren())do if h:IsA("\082\101\109\111\116\101\070\117\110\099\116\105\111\110")then e[h.Name]=function(o,...)return K.DeserializeArgsAndUnpack(h:InvokeServer(K.SerializeArgsAndUnpack(...)))end e[h.Name.."\080\114\111\109\105\115\101"]=function(o,...)local H=K.SerializeArgs(...)return Z.new(function(o)o(K.DeserializeArgsAndUnpack(h:InvokeServer(table.unpack(H,1,H.n))))end)end end end end if h:FindFirstChild("\082\069")then for o,Z in ipairs(h.RE:GetChildren())do if Z:IsA("\082\101\109\111\116\101\069\118\101\110\116")then e[Z.Name]=H.new(Z)end end end if h:FindFirstChild("\082\080")then for o,Z in ipairs(h.RP:GetChildren())do if Z:IsA("\086\097\108\117\101\066\097\115\101")or Z:IsA("\082\101\109\111\116\101\069\118\101\110\116")then e[Z.Name]=V.new(Z)end end end I[o]=e return e end function o.CreateController(Z)assert(type(Z)=="\116\097\098\108\101","\067\111\110\116\114\111\108\108\101\114 \109\117\115\116 \098\101 \097 \116\097\098\108\101\059 \103\111\116 "..type(Z))assert(type(Z.Name)=="\115\116\114\105\110\103","\067\111\110\116\114\111\108\108\101\114\046\078\097\109\101 \109\117\115\116 \098\101 \097 \115\116\114\105\110\103\059 \103\111\116 "..type(Z.Name))assert(#Z.Name>0,"\067\111\110\116\114\111\108\108\101\114\046\078\097\109\101 \109\117\115\116 \098\101 \097 \110\111\110-\101\109\112\116\121 \115\116\114\105\110\103")assert(o.Controllers[Z.Name]==nil,"\067\111\110\116\114\111\108\108\101\114 \""..(Z.Name.."\" \097\108\114\101\097\100\121 \101\120\105\115\116\115"))Z=e.Assign(Z,{_knit_is_controller=true})o.Controllers[Z.Name]=Z return Z end function o.AddControllers(o)return h.LoadChildren(o)end function o.AddControllersDeep(o)return h.LoadDescendants(o)end function o.GetService(o)assert(type(o)=="\115\116\114\105\110\103","\083\101\114\118\105\099\101\078\097\109\101 \109\117\115\116 \098\101 \097 \115\116\114\105\110\103\059 \103\111\116 "..type(o))local Z=c:FindFirstChild(o)assert(Z~=nil,"\067\111\117\108\100 \110\111\116 \102\105\110\100 \115\101\114\118\105\099\101 \""..(o.."\""))return I[o]or b(o,Z)end function o.GetController(Z)return o.Controllers[Z]end function o.Start()if w then return Z.Reject("\075\110\105\116 \097\108\114\101\097\100\121 \115\116\097\114\116\101\100")end w=true local h=o.Controllers return(Z.new(function(o)local K={}for o,h in pairs(h)do if type(h.KnitInit)=="\102\117\110\099\116\105\111\110"then table.insert(K,Z.new(function(o)h:KnitInit()o()end))end end o(Z.All(K))end)):Then(function()for o,Z in pairs(h)do if type(Z.KnitStart)=="\102\117\110\099\116\105\111\110"then task.spawn(Z.KnitStart,Z)end end g=true l:Fire()task.defer(function()l:Destroy()end)end)end function o.OnStart()if g then return Z.Resolve()else return Z.FromEvent(l.Event)end end return o



local KnitClient = {}

KnitClient.Version = script.Parent.Version.Value
KnitClient.Player = game:GetService("Players").LocalPlayer
KnitClient.Controllers = {}
KnitClient.Util = script.Parent.Util

local Promise = require(KnitClient.Util.Promise)
local Loader = require(KnitClient.Util.Loader)
local Ser = require(KnitClient.Util.Ser)
local ClientRemoteSignal = require(KnitClient.Util.Remote.ClientRemoteSignal)
local ClientRemoteProperty = require(KnitClient.Util.Remote.ClientRemoteProperty)
local TableUtil = require(KnitClient.Util.TableUtil)

local services = {}
local servicesFolder = script.Parent:WaitForChild("Services")

local started = false
local startedComplete = false
local onStartedComplete = Instance.new("BindableEvent")


local function BuildService(serviceName, folder)
	local service = {}
	if (folder:FindFirstChild("RF")) then
		for _,rf in ipairs(folder.RF:GetChildren()) do
			if (rf:IsA("RemoteFunction")) then
				service[rf.Name] = function(_self, ...)
					return Ser.DeserializeArgsAndUnpack(rf:InvokeServer(Ser.SerializeArgsAndUnpack(...)))
				end
				service[rf.Name .. "Promise"] = function(_self, ...)
					local args = Ser.SerializeArgs(...)
					return Promise.new(function(resolve)
						resolve(Ser.DeserializeArgsAndUnpack(rf:InvokeServer(table.unpack(args, 1, args.n))))
					end)
				end
			end
		end
	end
	if (folder:FindFirstChild("RE")) then
		for _,re in ipairs(folder.RE:GetChildren()) do
			if (re:IsA("RemoteEvent")) then
				service[re.Name] = ClientRemoteSignal.new(re)
			end
		end
	end
	if (folder:FindFirstChild("RP")) then
		for _,rp in ipairs(folder.RP:GetChildren()) do
			if (rp:IsA("ValueBase") or rp:IsA("RemoteEvent")) then
				service[rp.Name] = ClientRemoteProperty.new(rp)
			end
		end
	end
	services[serviceName] = service
	return service
end


function KnitClient.CreateController(controller)
	assert(type(controller) == "table", "Controller must be a table; got " .. type(controller))
	assert(type(controller.Name) == "string", "Controller.Name must be a string; got " .. type(controller.Name))
	assert(#controller.Name > 0, "Controller.Name must be a non-empty string")
	assert(KnitClient.Controllers[controller.Name] == nil, "Controller \"" .. controller.Name .. "\" already exists")
	controller = TableUtil.Assign(controller, {
		_knit_is_controller = true;
	})
	KnitClient.Controllers[controller.Name] = controller
	return controller
end


function KnitClient.AddControllers(folder)
	return Loader.LoadChildren(folder)
end


function KnitClient.AddControllersDeep(folder)
	return Loader.LoadDescendants(folder)
end


function KnitClient.GetService(serviceName)
	assert(type(serviceName) == "string", "ServiceName must be a string; got " .. type(serviceName))
	local folder = servicesFolder:FindFirstChild(serviceName)
	assert(folder ~= nil, "Could not find service \"" .. serviceName .. "\"")
	return services[serviceName] or BuildService(serviceName, folder)
end


function KnitClient.GetController(controllerName)
	return KnitClient.Controllers[controllerName]
end


function KnitClient.Start()

	if (started) then
		return Promise.Reject("Knit already started")
	end

	started = true

	local controllers = KnitClient.Controllers

	return Promise.new(function(resolve)

		-- Init:
		local promisesStartControllers = {}
		for _,controller in pairs(controllers) do
			if (type(controller.KnitInit) == "function") then
				table.insert(promisesStartControllers, Promise.new(function(r)
					controller:KnitInit()
					r()
				end))
			end
		end

		resolve(Promise.All(promisesStartControllers))

	end):Then(function()

		-- Start:
		for _,controller in pairs(controllers) do
			if (type(controller.KnitStart) == "function") then
				task.spawn(controller.KnitStart, controller)
			end
		end

		startedComplete = true
		onStartedComplete:Fire()

		task.defer(function()
			onStartedComplete:Destroy()
		end)

	end)

end


function KnitClient.OnStart()
	if (startedComplete) then
		return Promise.Resolve()
	else
		return Promise.FromEvent(onStartedComplete.Event)
	end
end


return KnitClient
