local Gladius = _G.Gladius
if not Gladius then
	DEFAULT_CHAT_FRAME:AddMessage(format("Module %s requires Gladius", "Clicks"))
end
local L = Gladius.L

-- global functions
local strfind = string.find
local pairs = pairs

local Clicks = Gladius:NewModule("Clicks", false, false, {
	clickAttributes = {
		["Left"] = { button = "1", modifier = "", action = "target", macro = ""},
		["Right"] = { button = "2", modifier = "", action = "focus", macro = ""},
	},
})

function Clicks:OnEnable()
	-- Table that holds all of the secure frames to apply click actions to
	self.secureFrames = self.secureFrames or {}
end

function Clicks:OnDisable()
	-- Iterate over all the secure frames and disable any attributes
	for _, t in pairs(self.secureFrames) do
		for frame,_ in pairs(t) do
			for _,attr in pairs(Gladius.dbi.profile.clickAttributes) do
				frame:SetAttribute(attr.modifier.."type"..attr.button, nil)
			end
		end
	end
end

-- Needed to not throw Lua errors <,<
function Clicks:GetAttachTo()
	return ""
end

-- Registers a secure frame and immediately applies
-- click actions to it
function Clicks:RegisterSecureFrame(unit, frame)
	if (not self.secureFrames[unit]) then
		self.secureFrames[unit] = {}
	end
	if (not self.secureFrames[unit][frame]) then
		self.secureFrames[unit][frame] = true
		self:ApplyAttributes(unit, frame)
	end
end

-- Finds all the secure frames belonging to a specific unit and registers them
-- Uses [module:GetFrame()].secure to find the frames
function Clicks:GetSecureFrames(unit)
	-- Add the default secure frame
	self:RegisterSecureFrame(unit, Gladius.buttons[unit].secure)
	-- Find secure frames in other modules
	for m, _ in pairs(Gladius.modules) do
		local frame = Gladius:GetParent(unit, m)
		if (frame and frame.secure) then
			self:RegisterSecureFrame(unit, frame.secure)
		end
	end
end

function Clicks:Update(unit)
	-- Update secure frame table
	self:GetSecureFrames(unit)
	-- Apply attributes to the frames
	for frame, _ in pairs(self.secureFrames[unit]) do
		self:ApplyAttributes(unit, frame)
	end
end

-- Applies attributes to a specific frame
function Clicks:ApplyAttributes(unit, frame)
	frame:SetAttribute("unit", unit)
	for _, attr in pairs(Gladius.dbi.profile.clickAttributes) do
		frame:SetAttribute(attr.modifier.."type"..attr.button, attr.action)
		if (attr.action == "macro" and attr.macro ~= "") then
			frame:SetAttribute(attr.modifier.."macrotext"..attr.button, string.gsub(attr.macro, "*unit", unit))
		elseif (attr.action == "spell" and attr.macro ~= "") then
			frame:SetAttribute(attr.modifier.."spell"..attr.button, attr.macro)
		end
	end
end

function Clicks:Test(unit)
	-- set arena1 to player
	for frame, _ in pairs(self.secureFrames[unit]) do
		frame:SetAttribute("unit", "player")
	end
end

local function getOption(info)
	local key = info[#info - 2]
	return Gladius.dbi.profile.clickAttributes[key][info[#info]]
end

local function setOption(info, value)
	local key = info[#info - 2]
	Gladius.dbi.profile.clickAttributes[key][info[#info]] = value
	Gladius:UpdateFrame()
end

local CLICK_BUTTONS = {["1"] = L["Left"], ["2"] = L["Right"], ["3"] = L["Middle"], ["4"] = L["Button 4"], ["5"] = L["Button 5"]}
local CLICK_MODIFIERS = {[""] = L["None"], ["ctrl-"] = L["ctrl-"], ["shift-"] = L["shift-"], ["alt-"] = L["alt-"]}

function Clicks:GetOptions()
	local addAttrButton = "1"
	local addAttrMod = ""
	local options = {
		attributeList = {
			type = "group",
			name = L["Click Actions"],
			order = 1,
				args = {
					add = {
					type = "group",
					name = L["Add click action"],
					inline = true,
					order = 1,
					args = {
						button = {
							type = "select",
							name = L["Mouse button"],
							desc = L["Select which mouse button this click action uses"],
							values = CLICK_BUTTONS,
							get = function(info)
								return addAttrButton
							end,
							set = function(info, value)
								addAttrButton = value
							end,
							disabled = function()
								return not Gladius.dbi.profile.modules[self.name]
							end,
							order = 10,
						},
						modifier = {
							type = "select",
							name = L["Modifier"],
							desc = L["Select a modifier for this click action"],
							values = CLICK_MODIFIERS,
							get = function(info)
								return addAttrMod
							end,
							set = function(info,
								value) addAttrMod = value
							end,
							disabled = function()
								return not Gladius.dbi.profile.modules[self.name]
							end,
							order = 20,
						},
						add = {
							type = "execute",
							name = L["Add"],
							func = function()
								local attr = addAttrMod ~= "" and CLICK_MODIFIERS[addAttrMod]..CLICK_BUTTONS[addAttrButton] or CLICK_BUTTONS[addAttrButton]
								if (not Gladius.db.clickAttributes[attr]) then
									-- add to db
									Gladius.db.clickAttributes[attr] = {
										button = addAttrButton, 
										modifier = addAttrMod, 
										action = "target", 
										macro = ""
									}
									-- add to options
									Gladius.options.args[self.name].args.attributeList.args[attr] = self:GetAttributeOptionTable(attr, order)
									-- update
									Gladius:UpdateFrame()
								end
							end,
							order = 30,
						},
					},
				},
			},
		},
	}
	-- attributes
	local order = 1
	for attr, _ in pairs(Gladius.dbi.profile.clickAttributes) do
		options.attributeList.args[attr] = self:GetAttributeOptionTable(attr, order)
		order = order + 1
	end
	return options
end

function Clicks:GetAttributeOptionTable(attribute, order)
	return {
		type = "group",
		name = attribute,
		childGroups = "tree",
		order = order,
		disabled = function()
			return not Gladius.dbi.profile.modules[self.name]
		end,
		args = {
			delete = {
				type = "execute",
				name = L["Delete Click Action"],
				func = function()
					-- remove from db
					Gladius.db.clickAttributes[attribute] = nil
					-- remove from options
					Gladius.options.args[self.name].args.attributeList.args[attribute] = nil
					-- update
					Gladius:UpdateFrame()
				end,
				order = 1,
			},
			action = {
				type = "group",
				name = L["Action"],
				inline = true,
				get = getOption,
				set = setOption,
				order = 2,
				args = {
					action = {
						type = "select",
						name = L["Action"],
						desc = L["Select what this Click Action does"],
						values={["macro"] = MACRO, ["target"] = TARGET, ["focus"] = FOCUS, ["spell"] = L["Cast Spell"]},
						order = 10,
					},
					sep = {
						type = "description",
						name = "",
						width = "full",
						order = 15,
					},
					macro = {
						type = "input",
						multiline = true,
						name = L["Spell Name / Macro Text"],
						desc = L["Select what this Click Action does"],
						width = "double",
						order = 20,
					},
				},
			},
		},
	}
end