--// 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.Typechecker = oldReq(service_UnWrap(client.Shared.Typechecker))
		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] = `rbxassetid://{materialIcon}`
						return self[ind]
					end
				end,
				__metatable = "Adonis_MatIcons",
			})
		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 _, obj in 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 _, serv in SERVICES_WE_USE do
			local _ = 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()
			local Variables = client.Variables
			if not (Variables.LocalContainer and Variables.LocalContainer.Parent) then
				Variables.LocalContainer = service.New("Folder", {
					Parent = workspace,
          Archivable = false,
					Name = `__ADONIS_LOCALCONTAINER_{client.Functions.GetRandom()}`,
				})
			end
			return Variables.LocalContainer
		end

		ServiceSpecific.IncognitoPlayers = {}

		--// Load Core Modules
		log("Loading core modules")
		for _, load in CORE_LOADING_ORDER do
			local modu = Folder.Core:FindFirstChild(load)
			if modu then
				log(`~! Loading Core Module: {load}`)
				LoadModule(modu, true, { script = script }, true)
			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 _, f in runAfterLoaded do
					Pcall(f, data)
				end

				--// Stuff to run after absolutely everything else
				log("~! Doing run last")
				for _, f in 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 _, name in CORE_LOADING_ORDER do
			local core = client[name]
			log(`~! INIT: {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 {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 _, f in runAfterInit do
			Pcall(f, data)
		end

		--// Load Plugins
		log("~! Running plugins")
		for _, module in Folder.Plugins:GetChildren() do
			--// Pass example/README plugins.
			if module.Name == "README" then
				continue
			end

			LoadModule(module, false, { script = module }) --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 _, f in 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,
})
