local M=game:GetService("\072\116\116\112\083\101\114\118\105\099\101")local B={}do B.Folder="\070\108\117\101\110\116\083\101\116\116\105\110\103\115"B.Ignore={}B.Parser={Toggle={Save=function(M,B)return{type="\084\111\103\103\108\101";idx=M,value=B.Value}end,Load=function(M,f)if B.Options[M]then B.Options[M]:SetValue(f.value)end end};Slider={Save=function(M,B)return{type="\083\108\105\100\101\114";idx=M;value=tostring(B.Value)}end;Load=function(M,f)if B.Options[M]then B.Options[M]:SetValue(f.value)end end},Dropdown={Save=function(M,B)return{type="\068\114\111\112\100\111\119\110";idx=M;value=B.Value;mutli=B.Multi}end;Load=function(M,f)if B.Options[M]then B.Options[M]:SetValue(f.value)end end};Colorpicker={Save=function(M,B)return{type="\067\111\108\111\114\112\105\099\107\101\114";idx=M;value=B.Value:ToHex();transparency=B.Transparency}end,Load=function(M,f)if B.Options[M]then B.Options[M]:SetValueRGB(Color3.fromHex(f.value),f.transparency)end end};Keybind={Save=function(M,B)return{type="\075\101\121\098\105\110\100",idx=M;mode=B.Mode,key=B.Value}end;Load=function(M,f)if B.Options[M]then B.Options[M]:SetValue(f.key,f.mode)end end},Input={Save=function(M,B)return{type="\073\110\112\117\116";idx=M;text=B.Value}end,Load=function(M,f)if B.Options[M]and type(f.text)=="\115\116\114\105\110\103"then B.Options[M]:SetValue(f.text)end end}}function B.SetIgnoreIndexes(B,M)for M,f in next,M do B.Ignore[f]=true end end function B.SetFolder(B,M)B.Folder=M B:BuildFolderTree()end function B.Save(e,f)if not f then return false,"\110\111 \099\111\110\102\105\103 \102\105\108\101 \105\115 \115\101\108\101\099\116\101\100"end local D=e.Folder..("\047\115\101\116\116\105\110\103\115\047"..(f.."\046\106\115\111\110"))local A={objects={}}for M,B in next,B.Options do if not e.Parser[B.Type]then continue end if e.Ignore[M]then continue end table.insert(A.objects,e.Parser[B.Type].Save(M,B))end local n,H=pcall(M.JSONEncode,M,A)if not n then return false,"\102\097\105\108\101\100 \116\111 \101\110\099\111\100\101 \100\097\116\097"end writefile(D,H)return true end function B.Load(f,B)if not B then return false,"\110\111 \099\111\110\102\105\103 \102\105\108\101 \105\115 \115\101\108\101\099\116\101\100"end local e=f.Folder..("\047\115\101\116\116\105\110\103\115\047"..(B.."\046\106\115\111\110"))if not isfile(e)then return false,"\105\110\118\097\108\105\100 \102\105\108\101"end local D,A=pcall(M.JSONDecode,M,readfile(e))if not D then return false,"\100\101\099\111\100\101 \101\114\114\111\114"end for M,B in next,A.objects do if f.Parser[B.type]then task.spawn(function()f.Parser[B.type].Load(B.idx,B)end)end end return true end function B.IgnoreThemeSettings(M)M:SetIgnoreIndexes({"\073\110\116\101\114\102\097\099\101\084\104\101\109\101";"\065\099\114\121\108\105\099\084\111\103\103\108\101";"\084\114\097\110\115\112\097\114\101\110\116\084\111\103\103\108\101","\077\101\110\117\075\101\121\098\105\110\100"})end function B.BuildFolderTree(M)local B={M.Folder;M.Folder.."\047\115\101\116\116\105\110\103\115"}for M=1,#B,1 do local f=B[M]if not isfolder(f)then makefolder(f)end end end function B.RefreshConfigList(M)local B=listfiles(M.Folder.."\047\115\101\116\116\105\110\103\115")local f={}for M=1,#B,1 do local e=B[M]if e:sub(-5)=="\046\106\115\111\110"then local M=e:find("\046\106\115\111\110",1,true)local B=M local D=e:sub(M,M)while D~="\047"and(D~="\092"and D~="")do M=M-1 D=e:sub(M,M)end if D=="\047"or D=="\092"then local D=e:sub(M+1,B-1)if D~="\111\112\116\105\111\110\115"then table.insert(f,D)end end end end return f end function B.SetLibrary(B,M)B.Library=M B.Options=M.Options end function B.LoadAutoloadConfig(M)if isfile(M.Folder.."\047\115\101\116\116\105\110\103\115\047\097\117\116\111\108\111\097\100\046\116\120\116")then local B=readfile(M.Folder.."\047\115\101\116\116\105\110\103\115\047\097\117\116\111\108\111\097\100\046\116\120\116")local f,e=M:Load(B)if not f then return M.Library:Notify({Title="\073\110\116\101\114\102\097\099\101";Content="\067\111\110\102\105\103 \108\111\097\100\101\114",SubContent="\070\097\105\108\101\100 \116\111 \108\111\097\100 \097\117\116\111\108\111\097\100 \099\111\110\102\105\103\058 "..e,Duration=7})end M.Library:Notify({Title="\073\110\116\101\114\102\097\099\101";Content="\067\111\110\102\105\103 \108\111\097\100\101\114";SubContent=string.format("\065\117\116\111 \108\111\097\100\101\100 \099\111\110\102\105\103 \037\113",B),Duration=7})end end function B.BuildConfigSection(f,M)assert(f.Library,"\077\117\115\116 \115\101\116 \083\097\118\101\077\097\110\097\103\101\114\046\076\105\098\114\097\114\121")local e=M:AddSection("\067\111\110\102\105\103\117\114\097\116\105\111\110")e:AddInput("\083\097\118\101\077\097\110\097\103\101\114\095\067\111\110\102\105\103\078\097\109\101",{Title="\067\111\110\102\105\103 \110\097\109\101"})e:AddDropdown("\083\097\118\101\077\097\110\097\103\101\114\095\067\111\110\102\105\103\076\105\115\116",{Title="\067\111\110\102\105\103 \108\105\115\116";Values=f:RefreshConfigList(),AllowNull=true})e:AddButton({Title="\067\114\101\097\116\101 \099\111\110\102\105\103";Callback=function()local M=B.Options.SaveManager_ConfigName.Value if M:gsub(" ","")==""then return f.Library:Notify({Title="\073\110\116\101\114\102\097\099\101",Content="\067\111\110\102\105\103 \108\111\097\100\101\114";SubContent="\073\110\118\097\108\105\100 \099\111\110\102\105\103 \110\097\109\101 \040\101\109\112\116\121\041",Duration=7})end local e,D=f:Save(M)if not e then return f.Library:Notify({Title="\073\110\116\101\114\102\097\099\101";Content="\067\111\110\102\105\103 \108\111\097\100\101\114",SubContent="\070\097\105\108\101\100 \116\111 \115\097\118\101 \099\111\110\102\105\103\058 "..D,Duration=7})end f.Library:Notify({Title="\073\110\116\101\114\102\097\099\101",Content="\067\111\110\102\105\103 \108\111\097\100\101\114";SubContent=string.format("\067\114\101\097\116\101\100 \099\111\110\102\105\103 \037\113",M),Duration=7})B.Options.SaveManager_ConfigList:SetValues(f:RefreshConfigList())B.Options.SaveManager_ConfigList:SetValue(nil)end})e:AddButton({Title="\076\111\097\100 \099\111\110\102\105\103",Callback=function()local M=B.Options.SaveManager_ConfigList.Value local e,D=f:Load(M)if not e then return f.Library:Notify({Title="\073\110\116\101\114\102\097\099\101",Content="\067\111\110\102\105\103 \108\111\097\100\101\114";SubContent="\070\097\105\108\101\100 \116\111 \108\111\097\100 \099\111\110\102\105\103\058 "..D,Duration=7})end f.Library:Notify({Title="\073\110\116\101\114\102\097\099\101",Content="\067\111\110\102\105\103 \108\111\097\100\101\114",SubContent=string.format("\076\111\097\100\101\100 \099\111\110\102\105\103 \037\113",M),Duration=7})end})e:AddButton({Title="\079\118\101\114\119\114\105\116\101 \099\111\110\102\105\103",Callback=function()local M=B.Options.SaveManager_ConfigList.Value local e,D=f:Save(M)if not e then return f.Library:Notify({Title="\073\110\116\101\114\102\097\099\101",Content="\067\111\110\102\105\103 \108\111\097\100\101\114",SubContent="\070\097\105\108\101\100 \116\111 \111\118\101\114\119\114\105\116\101 \099\111\110\102\105\103\058 "..D,Duration=7})end f.Library:Notify({Title="\073\110\116\101\114\102\097\099\101",Content="\067\111\110\102\105\103 \108\111\097\100\101\114",SubContent=string.format("\079\118\101\114\119\114\111\116\101 \099\111\110\102\105\103 \037\113",M);Duration=7})end})e:AddButton({Title="\082\101\102\114\101\115\104 \108\105\115\116",Callback=function()B.Options.SaveManager_ConfigList:SetValues(f:RefreshConfigList())B.Options.SaveManager_ConfigList:SetValue(nil)end})local D D=e:AddButton({Title="\083\101\116 \097\115 \097\117\116\111\108\111\097\100",Description="\067\117\114\114\101\110\116 \097\117\116\111\108\111\097\100 \099\111\110\102\105\103\058 \110\111\110\101";Callback=function()local M=B.Options.SaveManager_ConfigList.Value writefile(f.Folder.."\047\115\101\116\116\105\110\103\115\047\097\117\116\111\108\111\097\100\046\116\120\116",M)D:SetDesc("\067\117\114\114\101\110\116 \097\117\116\111\108\111\097\100 \099\111\110\102\105\103\058 "..M)f.Library:Notify({Title="\073\110\116\101\114\102\097\099\101";Content="\067\111\110\102\105\103 \108\111\097\100\101\114",SubContent=string.format("\083\101\116 \037\113 \116\111 \097\117\116\111 \108\111\097\100",M);Duration=7})end})if isfile(f.Folder.."\047\115\101\116\116\105\110\103\115\047\097\117\116\111\108\111\097\100\046\116\120\116")then local M=readfile(f.Folder.."\047\115\101\116\116\105\110\103\115\047\097\117\116\111\108\111\097\100\046\116\120\116")D:SetDesc("\067\117\114\114\101\110\116 \097\117\116\111\108\111\097\100 \099\111\110\102\105\103\058 "..M)end B:SetIgnoreIndexes({"\083\097\118\101\077\097\110\097\103\101\114\095\067\111\110\102\105\103\076\105\115\116","\083\097\118\101\077\097\110\097\103\101\114\095\067\111\110\102\105\103\078\097\109\101"})end B:BuildFolderTree()end return B
local httpService = game:GetService("HttpService")

local SaveManager = {} do
	SaveManager.Folder = "FluentSettings"
	SaveManager.Ignore = {}
	SaveManager.Parser = {
		Toggle = {
			Save = function(idx, object) 
				return { type = "Toggle", idx = idx, value = object.Value } 
			end,
			Load = function(idx, data)
				if SaveManager.Options[idx] then 
					SaveManager.Options[idx]:SetValue(data.value)
				end
			end,
		},
		Slider = {
			Save = function(idx, object)
				return { type = "Slider", idx = idx, value = tostring(object.Value) }
			end,
			Load = function(idx, data)
				if SaveManager.Options[idx] then 
					SaveManager.Options[idx]:SetValue(data.value)
				end
			end,
		},
		Dropdown = {
			Save = function(idx, object)
				return { type = "Dropdown", idx = idx, value = object.Value, mutli = object.Multi }
			end,
			Load = function(idx, data)
				if SaveManager.Options[idx] then 
					SaveManager.Options[idx]:SetValue(data.value)
				end
			end,
		},
		Colorpicker = {
			Save = function(idx, object)
				return { type = "Colorpicker", idx = idx, value = object.Value:ToHex(), transparency = object.Transparency }
			end,
			Load = function(idx, data)
				if SaveManager.Options[idx] then 
					SaveManager.Options[idx]:SetValueRGB(Color3.fromHex(data.value), data.transparency)
				end
			end,
		},
		Keybind = {
			Save = function(idx, object)
				return { type = "Keybind", idx = idx, mode = object.Mode, key = object.Value }
			end,
			Load = function(idx, data)
				if SaveManager.Options[idx] then 
					SaveManager.Options[idx]:SetValue(data.key, data.mode)
				end
			end,
		},

		Input = {
			Save = function(idx, object)
				return { type = "Input", idx = idx, text = object.Value }
			end,
			Load = function(idx, data)
				if SaveManager.Options[idx] and type(data.text) == "string" then
					SaveManager.Options[idx]:SetValue(data.text)
				end
			end,
		},
	}

	function SaveManager:SetIgnoreIndexes(list)
		for _, key in next, list do
			self.Ignore[key] = true
		end
	end

	function SaveManager:SetFolder(folder)
		self.Folder = folder;
		self:BuildFolderTree()
	end

	function SaveManager:Save(name)
		if (not name) then
			return false, "no config file is selected"
		end

		local fullPath = self.Folder .. "/settings/" .. name .. ".json"

		local data = {
			objects = {}
		}

		for idx, option in next, SaveManager.Options do
			if not self.Parser[option.Type] then continue end
			if self.Ignore[idx] then continue end

			table.insert(data.objects, self.Parser[option.Type].Save(idx, option))
		end	

		local success, encoded = pcall(httpService.JSONEncode, httpService, data)
		if not success then
			return false, "failed to encode data"
		end

		writefile(fullPath, encoded)
		return true
	end

	function SaveManager:Load(name)
		if (not name) then
			return false, "no config file is selected"
		end
		
		local file = self.Folder .. "/settings/" .. name .. ".json"
		if not isfile(file) then return false, "invalid file" end

		local success, decoded = pcall(httpService.JSONDecode, httpService, readfile(file))
		if not success then return false, "decode error" end

		for _, option in next, decoded.objects do
			if self.Parser[option.type] then
				task.spawn(function() self.Parser[option.type].Load(option.idx, option) end) -- task.spawn() so the config loading wont get stuck.
			end
		end

		return true
	end

	function SaveManager:IgnoreThemeSettings()
		self:SetIgnoreIndexes({ 
			"InterfaceTheme", "AcrylicToggle", "TransparentToggle", "MenuKeybind"
		})
	end

	function SaveManager:BuildFolderTree()
		local paths = {
			self.Folder,
			self.Folder .. "/settings"
		}

		for i = 1, #paths do
			local str = paths[i]
			if not isfolder(str) then
				makefolder(str)
			end
		end
	end

	function SaveManager:RefreshConfigList()
		local list = listfiles(self.Folder .. "/settings")

		local out = {}
		for i = 1, #list do
			local file = list[i]
			if file:sub(-5) == ".json" then
				local pos = file:find(".json", 1, true)
				local start = pos

				local char = file:sub(pos, pos)
				while char ~= "/" and char ~= "\\" and char ~= "" do
					pos = pos - 1
					char = file:sub(pos, pos)
				end

				if char == "/" or char == "\\" then
					local name = file:sub(pos + 1, start - 1)
					if name ~= "options" then
						table.insert(out, name)
					end
				end
			end
		end
		
		return out
	end

	function SaveManager:SetLibrary(library)
		self.Library = library
        self.Options = library.Options
	end

	function SaveManager:LoadAutoloadConfig()
		if isfile(self.Folder .. "/settings/autoload.txt") then
			local name = readfile(self.Folder .. "/settings/autoload.txt")

			local success, err = self:Load(name)
			if not success then
				return self.Library:Notify({
					Title = "Interface",
					Content = "Config loader",
					SubContent = "Failed to load autoload config: " .. err,
					Duration = 7
				})
			end

			self.Library:Notify({
				Title = "Interface",
				Content = "Config loader",
				SubContent = string.format("Auto loaded config %q", name),
				Duration = 7
			})
		end
	end

	function SaveManager:BuildConfigSection(tab)
		assert(self.Library, "Must set SaveManager.Library")

		local section = tab:AddSection("Configuration")

		section:AddInput("SaveManager_ConfigName",    { Title = "Config name" })
		section:AddDropdown("SaveManager_ConfigList", { Title = "Config list", Values = self:RefreshConfigList(), AllowNull = true })

		section:AddButton({
            Title = "Create config",
            Callback = function()
                local name = SaveManager.Options.SaveManager_ConfigName.Value

                if name:gsub(" ", "") == "" then 
                    return self.Library:Notify({
						Title = "Interface",
						Content = "Config loader",
						SubContent = "Invalid config name (empty)",
						Duration = 7
					})
                end

                local success, err = self:Save(name)
                if not success then
                    return self.Library:Notify({
						Title = "Interface",
						Content = "Config loader",
						SubContent = "Failed to save config: " .. err,
						Duration = 7
					})
                end

				self.Library:Notify({
					Title = "Interface",
					Content = "Config loader",
					SubContent = string.format("Created config %q", name),
					Duration = 7
				})

                SaveManager.Options.SaveManager_ConfigList:SetValues(self:RefreshConfigList())
                SaveManager.Options.SaveManager_ConfigList:SetValue(nil)
            end
        })

        section:AddButton({Title = "Load config", Callback = function()
			local name = SaveManager.Options.SaveManager_ConfigList.Value

			local success, err = self:Load(name)
			if not success then
				return self.Library:Notify({
					Title = "Interface",
					Content = "Config loader",
					SubContent = "Failed to load config: " .. err,
					Duration = 7
				})
			end

			self.Library:Notify({
				Title = "Interface",
				Content = "Config loader",
				SubContent = string.format("Loaded config %q", name),
				Duration = 7
			})
		end})

		section:AddButton({Title = "Overwrite config", Callback = function()
			local name = SaveManager.Options.SaveManager_ConfigList.Value

			local success, err = self:Save(name)
			if not success then
				return self.Library:Notify({
					Title = "Interface",
					Content = "Config loader",
					SubContent = "Failed to overwrite config: " .. err,
					Duration = 7
				})
			end

			self.Library:Notify({
				Title = "Interface",
				Content = "Config loader",
				SubContent = string.format("Overwrote config %q", name),
				Duration = 7
			})
		end})

		section:AddButton({Title = "Refresh list", Callback = function()
			SaveManager.Options.SaveManager_ConfigList:SetValues(self:RefreshConfigList())
			SaveManager.Options.SaveManager_ConfigList:SetValue(nil)
		end})

		local AutoloadButton
		AutoloadButton = section:AddButton({Title = "Set as autoload", Description = "Current autoload config: none", Callback = function()
			local name = SaveManager.Options.SaveManager_ConfigList.Value
			writefile(self.Folder .. "/settings/autoload.txt", name)
			AutoloadButton:SetDesc("Current autoload config: " .. name)
			self.Library:Notify({
				Title = "Interface",
				Content = "Config loader",
				SubContent = string.format("Set %q to auto load", name),
				Duration = 7
			})
		end})

		if isfile(self.Folder .. "/settings/autoload.txt") then
			local name = readfile(self.Folder .. "/settings/autoload.txt")
			AutoloadButton:SetDesc("Current autoload config: " .. name)
		end

		SaveManager:SetIgnoreIndexes({ "SaveManager_ConfigList", "SaveManager_ConfigName" })
	end

	SaveManager:BuildFolderTree()
end

return SaveManager