--[[

	The MIT License (MIT)

	Copyright (c) 2023 Lars Norberg

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.

--]]
local Addon, ns = ...

local L = LibStub("AceLocale-3.0"):GetLocale((...))
local AceConfigDialog = LibStub("AceConfigDialog-3.0")
local AceConfigRegistry = LibStub("AceConfigRegistry-3.0")

local Options = ns:NewModule("Options", "LibMoreEvents-1.0", "AceConsole-3.0", "AceHook-3.0")
local LEMO = LibStub("LibEditModeOverride-1.0", true)

-- Lua API
local math_max = math.max
local next = next
local ipairs = ipairs
local pairs = pairs
local string_format = string.format
local table_remove = table.remove
local table_sort = table.sort
local type = type

Options.GenerateProfileMenu = function(self)
	local options = {
		type = "group",
		childGroups = "tree",
		args = {
			profiles = {
				name = L["Settings Profile"],
				desc = L["Choose your settings profile. This choice affects all settings including frame placement."],
				type = "select",
				style = "dropdown",
				order = 0,
				values = function(info)
					local values = {}
					for i,profileKey in ipairs(ns:GetProfiles()) do
						values[profileKey] = profileKey
					end
					return values
				end,
				set = function(info, val)
					ns:SetProfile(val)
				end,
				get = function(info)
					return ns:GetProfile()
				end
			},
			space1 = {
				name = "", order = 1, type = "description"
			},
			reset = {
				name = L["Reset"],
				type = "execute",
				order = 2,
				confirm = function(info)
					return _G.CONFIRM_RESET_SETTINGS
				end,
				func = function(info)
					ns:ResetProfile(ns:GetProfile())
				end
			},
			delete = {
				name = L["Delete"],
				type = "execute",
				order = 3,
				confirm = function(info)
					return string_format(L["Are you sure you want to delete the preset '%s'? This cannot be undone."], ns:GetProfile())
				end,
				disabled = function(info)
					return ns:GetProfile() == ns:GetDefaultProfile()
				end,
				func = function(info)
					ns:DeleteProfile(ns:GetProfile())
				end
			},
			space2 = {
				name = "", order = 13, type = "description"
			},
			newprofileheader = {
				name = L["Create New Profile"],
				desc = L["Create a new settings profile."],
				type = "header",
				order = 4
			},
			newprofileName = {
				name = L["Name of new profile:"],
				type = "input",
				order = 5,
				arg = "", -- store the name here
				validate = function(info,val)
					if (not val or val == "") then
						return L["The new profile needs a name."]
					end
					if (ns:ProfileExists(val)) then
						return L["Profile already exists."]
					end
					return true
				end,
				get = function(info)
					return info.option.arg
				end,
				set = function(info,val)
					info.option.arg = val
				end
			},
			space3 = {
				name = "", order = 22, type = "description"
			},
			create = {
				name = L["Create"],
				desc = L["Create a new profile with the chosen name."],
				type = "execute",
				order = 6,
				disabled = function(info)
					local val = info.options.args.newprofileName.arg
					return (not val or val == "" or ns:ProfileExists(val))
				end,
				func = function(info)
					local layoutName = info.options.args.newprofileName.arg
					if (layoutName) then
						ns:SetProfile(layoutName)
						info.options.args.newprofileName.arg = ""
					end
				end
			},
			duplicate = {
				name = L["Duplicate"],
				desc = L["Create a new profile with the chosen name and copy the settings from the currently active one."],
				type = "execute",
				order = 7,
				disabled = function(info)
					local val = info.options.args.newprofileName.arg
					return (not val or val == "" or ns:ProfileExists(val))
				end,
				func = function(info)
					local layoutName = info.options.args.newprofileName.arg
					if (layoutName) then
						ns:DuplicateProfile(layoutName)
						info.options.args.newprofileName.arg = ""
					end
				end
			},
			space4 = {
				name = "", order = 8, type = "description"
			}
		}
	}
	local order = 0
	for i,arg in next,options.args do
		order = math_max(order, arg.order or 0)
	end
	order = order + 10
	return options, order
end

Options.Refresh = function(self)
	if (AceConfigRegistry:GetOptionsTable(Addon)) then
		AceConfigRegistry:NotifyChange(Addon)
	end
end

Options.OpenOptionsMenu = function(self)
	if (AceConfigRegistry:GetOptionsTable(Addon)) then
		AceConfigDialog:SetDefaultSize(Addon, 960, 720)
		AceConfigDialog:Open(Addon)
	end
end

Options.AddGroup = function(self, name, group)
	if (not self.objects) then
		self.objects = {}
	end
	if (group) then
		self.objects[#self.objects + 1] = { name = name, group = group }
	end
end

Options.GenerateOptionsMenu = function(self)
	if (not self.objects) then return end

	-- Generate the menus, remove empty objects.
	for i = #self.objects,1,-1 do
		local data = self.objects[i]
		if (type(data.group) == "function") then
			data.group = data.group()
			if (not data.group) then
				table_remove(self.objects, i)
			end
		end
	end

	-- Sort groups by localized name.
	table_sort(self.objects, function(a,b) return a.group.name < b.group.name end)

	-- Generate the options table.
	local options, orderoffset = self:GenerateProfileMenu()

	-- Add option to auto-load an EditMode layout.
	if (ns.IsRetail) then
		options.args.editmodeheader = {
			name = L["Edit Mode"],
			desc = "",
			type = "header",
			order = orderoffset + 1
		}
		options.args.autoLoadEditModeLayout = {
			name = L["Automatically load an Edit Mode layout."],
			type = "toggle", width = "full",
			set = function(info, val)
				ns.db.profile[info[#info]] = val
			end,
			get = function(info)
				return ns.db.profile[info[#info]]
			end,
			order = orderoffset + 3
		}
		options.args.editModeLayout = {
			name = L["Edit Mode Layout"],
			desc = L["Choose an Edit Mode layout to automaically load when enabling this profile."],
			type = "select",
			style = "dropdown",
			values = function(info)
				if (not LEMO:IsReady()) then return true end
				LEMO:LoadLayouts()
				local values = {}
				for i,layoutName in ipairs(LEMO:GetPresetLayoutNames()) do
					values[layoutName] = layoutName
				end
				for i,layoutName in ipairs(LEMO:GetEditableLayoutNames()) do
					values[layoutName] = layoutName
				end
				return values
			end,
			disabled = function(info)
				if (not ns.db.profile.autoLoadEditModeLayout) then return true end
				if (not LEMO:IsReady()) then return true end
			end,
			set = function(info, val)
				ns.db.profile.editModeLayout = val
				ns:ApplyEditModeLayout()
			end,
			get = function(info)
				return ns.db.profile[info[#info]]
			end,
			order = orderoffset + 4
		}
		options.args.editmodespacer = {
			name = "",
			type = "description",
			fontSize = "medium",
			order = orderoffset + 5
		}

		orderoffset = orderoffset + 10
	end

	local order = orderoffset
	for i,data in ipairs(self.objects) do
		if (data.group) then
			order = order + 10
			data.group.order = order
			data.group.childGroups = data.group.childGroups or "tab"
			options.args[data.name] = data.group
		end
	end

	self.options = options

	AceConfigRegistry:RegisterOptionsTable(Addon, options)
end

Options.GetOptionsObject = function(self)
	return self.options
end

Options.OnEvent = function(self, event, ...)
	if (event == "PLAYER_ENTERING_WORLD") then
		local isInitialLogin, isReloadingUi = ...
		if (isInitialLogin or isReloadingUi) then
			self:GenerateOptionsMenu()
			self:UnregisterEvent("PLAYER_ENTERING_WORLD", "OnEvent")
		end
		if (LEMO and LEMO:IsReady()) then
			LEMO:LoadLayouts()
			self:Refresh()
		end

	elseif (event == "EDIT_MODE_LAYOUTS_UPDATED") then
		if (LEMO and LEMO:IsReady()) then
			LEMO:LoadLayouts()
				self:Refresh()
		end

	elseif (event == "PLAYER_TALENT_UPDATE") then
		self:Refresh()
	end
end

Options.OnInitialize = function(self)
end

Options.OnEnable = function(self)
	self:RegisterChatCommand("az", "OpenOptionsMenu")
	self:RegisterChatCommand("azerite", "OpenOptionsMenu")
	self:RegisterEvent("PLAYER_ENTERING_WORLD", "OnEvent")
	self:RegisterEvent("PLAYER_TALENT_UPDATE", "OnEvent")
	if (ns.IsRetail) then
		self:RegisterEvent("EDIT_MODE_LAYOUTS_UPDATED", "OnEvent")
	end
end

Options.OnDisable = function(self)
	self:UnregisterChatCommand("az")
	self:UnregisterChatCommand("azerite")
	self:UnregisterEvent("PLAYER_ENTERING_WORLD", "OnEvent")
	self:UnregisterEvent("PLAYER_TALENT_UPDATE", "OnEvent")
	if (ns.IsRetail) then
		self:UnregisterEvent("EDIT_MODE_LAYOUTS_UPDATED", "OnEvent")
	end
end
