--// Init


log("Return init function");
return service.NewProxy({
	__call = function(self, data)
		log("Begin init");

		local remoteName,depsName = string.match(data.Name, "(.*)\\(.*)")
		Folder = service.Wrap(data.Folder --[[or folder and folder:Clone()]] or Folder)

		setfenv(1,setmetatable({}, {__metatable = unique}))
		client.Folder = Folder;
		client.UIFolder = Folder:WaitForChild("UI",9e9);
		client.Shared = Folder:WaitForChild("Shared",9e9);
		client.Loader = data.Loader
		client.Module = data.Module
		client.DepsName = depsName
		client.TrueStart = data.Start
		client.LoadingTime = data.LoadingTime
		client.RemoteName = remoteName

		client.Changelog = oldReq(service_UnWrap(client.Shared.Changelog))
		do
			local MaterialIcons = oldReq(service_UnWrap(client.Shared.MatIcons))
			client.MatIcons = setmetatable({}, {
				__index = function(self, ind)
					local materialIcon = MaterialIcons[ind]
					if materialIcon then
						self[ind] = string.format("rbxassetid://%d", materialIcon)
						return self[ind]
					end
				end,
				__metatable = "Adonis"
			})
		end

		--// Toss deps into a table so we don't need to directly deal with the Folder instance they're in
		log("Get dependencies")
		for ind,obj in ipairs(Folder:WaitForChild("Dependencies",9e9):GetChildren()) do client.Deps[obj.Name] = obj end

		--// Do this before we start hooking up events
		log("Destroy script object")
		--folder:Destroy()
		script.Parent = nil --script:Destroy()

		--// Intial setup
		log("Initial services caching")
		for ind, serv in ipairs(ServicesWeUse) do local temp = service[serv] end

		--// Client specific service variables/functions
		log("Add service specific")
		ServiceSpecific.Player = service.Players.LocalPlayer or (function()
			service.Players:GetPropertyChangedSignal("LocalPlayer"):Wait()
			return service.Players.LocalPlayer
		end)();
		ServiceSpecific.PlayerGui = ServiceSpecific.Player:FindFirstChildWhichIsA("PlayerGui");
		if not ServiceSpecific.PlayerGui then
			Routine(function()
				local PlayerGui = ServiceSpecific.Player:WaitForChild("PlayerGui", 120)
				if not PlayerGui then
					logError("PlayerGui unable to be fetched? [Waited 120 Seconds]")
					return;
				end
				ServiceSpecific.PlayerGui = PlayerGui
			end)
		end
		--[[
		-- // Doesn't seem to be used anymore

		ServiceSpecific.SafeTweenSize = function(obj, ...)
			pcall(obj.TweenSize, obj, ...)
		end;
		ServiceSpecific.SafeTweenPos = function(obj, ...)
			pcall(obj.TweenPosition, obj, ...)
		end;
		]]

		ServiceSpecific.Filter = function(str,from,to)
			return client.Remote.Get("Filter",str,(to and from) or service.Player,to or from)
		end;
		ServiceSpecific.LaxFilter = function(str,from)
			return service.Filter(str,from or service.Player,from or service.Player)
		end;

		ServiceSpecific.BroadcastFilter = function(str,from)
			return client.Remote.Get("BroadcastFilter",str,from or service.Player)
		end;

		ServiceSpecific.IsMobile = function()
			return service.UserInputService.TouchEnabled and not service.UserInputService.MouseEnabled and not service.UserInputService.KeyboardEnabled
		end;

		ServiceSpecific.LocalContainer = function()
			if not client.Variables.LocalContainer or not client.Variables.LocalContainer.Parent then
				client.Variables.LocalContainer = service.New("Folder")
				client.Variables.LocalContainer.Name = "__ADONIS_LOCALCONTAINER_" .. client.Functions.GetRandom()
				client.Variables.LocalContainer.Parent = workspace
			end
			return client.Variables.LocalContainer
		end;

		--// Load Core Modules
		log("Loading core modules")
		for ind,load in ipairs(LoadingOrder) do
			local modu = Folder.Core:FindFirstChild(load)
			if modu then
				log("~! Loading Core Module: ".. tostring(load))
				LoadModule(modu, true, {script = script})
			end
		end

		--// Start of module loading and server connection process
		local runLast = {}
		local runAfterInit = {}
		local runAfterLoaded = {}
		local runAfterPlugins = {}

		--// Loading Finisher
		client.Finish_Loading = function()
			log("Client fired finished loading")
			if client.Core.Key then
				--// Run anything from core modules that needs to be done after the client has finished loading
				log("~! Doing run after loaded")
				for i,f in pairs(runAfterLoaded) do
					Pcall(f, data);
				end

				--// Stuff to run after absolutely everything else
				log("~! Doing run last")
				for i,f in pairs(runLast) do
					Pcall(f, data);
				end

				--// Finished loading
				log("Finish loading")
				clientLocked = true
				client.Finish_Loading = function() end
				client.LoadingTime() --origWarn(tostring(time()-(client.TrueStart or startTime)))
				service.Events.FinishedLoading:Fire(os.time())

				log("~! FINISHED LOADING!")
			else
				log("Client missing remote key")
				client.Kill()("Missing remote key")
			end
		end

		--// Initialize Cores
		log("~! Init cores");
		for i,name in ipairs(LoadingOrder) do
			local core = client[name]
			log("~! INIT: ".. tostring(name))

			if core then
				if type(core) == "table" or (type(core) == "userdata" and getmetatable(core) == "ReadOnly_Table") then
					if core.RunLast then
						table.insert(runLast, core.RunLast);
						core.RunLast = nil;
					end

					if core.RunAfterInit then
						table.insert(runAfterInit, core.RunAfterInit);
						core.RunAfterInit = nil;
					end

					if core.RunAfterPlugins then
						table.insert(runAfterPlugins, core.RunAfterPlugins);
						core.RunAfterPlugins = nil;
					end

					if core.RunAfterLoaded then
						table.insert(runAfterLoaded, core.RunAfterLoaded);
						core.RunAfterLoaded = nil;
					end

					if core.Init then
						log("Run init for ".. tostring(name))
						Pcall(core.Init, data);
						core.Init = nil;
					end
				end
			end
		end

		--// Load any afterinit functions from modules (init steps that require other modules to have finished loading)
		log("~! Running after init")
		for i,f in pairs(runAfterInit) do
			Pcall(f, data);
		end

		--// Load Plugins
		log("~! Running plugins")
		for index,plugin in ipairs(Folder.Plugins:GetChildren()) do
			LoadModule(plugin, false, {script = plugin}); --noenv
		end

		--// We need to do some stuff *after* plugins are loaded (in case we need to be able to account for stuff they may have changed before doing something, such as determining the max length of remote commands)
		log("~! Running after plugins")
		for i,f in pairs(runAfterPlugins) do
			Pcall(f, data);
		end

		log("Initial loading complete")

		--// Below can be used to determine when all modules and plugins have finished loading; service.Events.AllModulesLoaded:Connect(function() doSomething end)
		client.AllModulesLoaded = true;
		service.Events.AllModulesLoaded:Fire(os.time());

		--[[client = service.ReadOnly(client, {
			[client.Variables] = true;
			[client.Handlers] = true;
			G_API = true;
			G_Access = true;
			G_Access_Key = true;
			G_Access_Perms = true;
			Allowed_API_Calls = true;
			HelpButtonImage = true;
			Finish_Loading = true;
			RemoteEvent = true;
			ScriptCache = true;
			Returns = true;
			PendingReturns = true;
			EncodeCache = true;
			DecodeCache = true;
			Received = true;
			Sent = true;
			Service = true;
			Holder = true;
			GUIs = true;
			LastUpdate = true;
			RateLimits = true;

			Init = true;
			RunAfterInit = true;
			RunAfterLoaded = true;
			RunAfterPlugins = true;
		}, true)--]]

		service.Events.ClientInitialized:Fire();

		log("~! Return success");
		return "SUCCESS"
	end;
	__metatable = "Adonis";
	__tostring = function() return "Adonis" end;
})