local PA = _G.ProjectAzilroka
local AR = PA:NewModule('AuraReminder', 'AceEvent-3.0', 'AceTimer-3.0')

PA.AR = AR

AR.Title = PA.ACL['|cFF16C3F2Aura|r |cFFFFFFFFReminder|r']
AR.Description = PA.ACL['Reminder for Buffs / Debuffs']
AR.Authors = 'Azilroka'
AR.isEnabled = false

local _G = _G
local pairs = pairs
local ipairs = ipairs
local tonumber = tonumber
local tostring = tostring
local select = select
local format = format
local strmatch = strmatch
local unpack = unpack
local tinsert = tinsert
local wipe = wipe

local GetSpellCooldown = GetSpellCooldown
local GetSpellInfo = GetSpellInfo
local IsSpellKnownOrOverridesKnown = IsSpellKnownOrOverridesKnown
local IsUsableSpell = IsUsableSpell
local IsInInstance = IsInInstance
local UnitAffectingCombat = UnitAffectingCombat
local UnitIsDeadOrGhost = UnitIsDeadOrGhost
local UnitLevel = UnitLevel
local UnitInVehicle = UnitInVehicle
local GetInventoryItemID = GetInventoryItemID
local GetWeaponEnchantInfo = GetWeaponEnchantInfo
local GetInventoryItemTexture = GetInventoryItemTexture
local PlaySoundFile = PlaySoundFile
local GetSpecialization = GetSpecialization
local GetSpecializationRole = GetSpecializationRole
local GetSpecializationInfo = GetSpecializationInfo
local GetItemInfoInstant = GetItemInfoInstant

local MAX_PLAYER_LEVEL = MAX_PLAYER_LEVEL

local CreateFrame = CreateFrame
local UIParent = UIParent

_G.AuraReminder = AR

AR.CreatedReminders = {}

local selectedFilter, selectedGroup, filters, spellList, filterTypeList, DefaultFilters = nil, PA.MyClass, {}, {}, {}

do
	local function SpellIDPredicate(spellIDToFind, casterToFind, _, _, _, _, _, _, _, caster, _, _, spellID)
		return (casterToFind and caster == casterToFind and spellIDToFind == spellID) or (spellIDToFind == spellID)
	end

	function AR:FindAuraBySpellID(spellID, unit, filter, caster)
		return _G.AuraUtil.FindAura(SpellIDPredicate, unit, filter, spellID, caster);
	end
end

function AR:FindPlayerAura(db, checkPersonal, filter)
	if db then
		for spellID, value in pairs(db) do
			if value == true then
				return AR:FindAuraBySpellID(spellID, 'player', filter, checkPersonal and 'player')
			end
		end
	end
end

function AR:IsSpellOnCooldown(id)
	local start, duration = GetSpellCooldown(id)
	if start > 0 and duration > 1.5 then
		return true
	end
end

function AR:IsSpellUsable(id)
	local isUsable = IsUsableSpell(id)
	if isUsable then
		return true
	end
end

function AR:UpdateColors(frame, id)
	local isUsable, notEnoughMana = IsUsableSpell(id)
	if isUsable then
		frame.icon:SetVertexColor(1, 1, 1)
	elseif notEnoughMana then
		frame.icon:SetVertexColor(.5, .5, 1)
	else
		frame.icon:SetVertexColor(.4, .4, .4)
	end
end

function AR:HasOffHandWeapon(itemID)
	if not itemID then return end
	local itemEquipLoc = select(4, GetItemInfoInstant(itemID))
	return (itemEquipLoc == "INVTYPE_WEAPON" or itemEquipLoc == "INVTYPE_2HWEAPON" or itemEquipLoc == "INVTYPE_WEAPONOFFHAND")
end

function AR:FilterCheck(db, isReverse)
	local _, instanceType = IsInInstance()
	local roleCheck, treeCheck, combatCheck, instanceCheck, PVPCheck = true, true, true, true, true

	if PA.Retail then
		local spec = GetSpecialization()

		if db.role and (db.role ~= 'ANY' and db.role ~= GetSpecializationRole(spec)) then
			roleCheck = false
		end

		if not isReverse and db.tree and (db.tree ~= 'ANY' and db.tree ~= spec) then
			treeCheck = false
		end

		if isReverse and db.talentTreeException and db.talentTreeException == spec then
			treeCheck = false
		end
	end

	if db.combat and not UnitAffectingCombat('player') then
		combatCheck = false
	end

	if db.instance and (instanceType ~= 'party' and instanceType ~= 'raid') then
		instanceCheck = false
	end

	if db.pvp and (instanceType ~= 'arena' and instanceType ~= 'pvp') then
		PVPCheck = false
	end

	if (isReverse or roleCheck and treeCheck) and combatCheck and instanceCheck and PVPCheck then
		return true
	else
		return false
	end
end

function AR:Reminder_Update()
	if UnitIsDeadOrGhost('player') or UnitInVehicle('player') then return end

	for _, button in ipairs(AR.CreatedReminders) do
		button:Hide()
		button:SetAlpha(1)
	end

	local Position = 1
	for _, filter in pairs({PA.MyClass, 'Global'}) do
		if AR.db.Filters then
			for _, db in pairs(AR.db.Filters[filter]) do
				if db.enable and (not db.level or db.level and UnitLevel('player') > db.level) then
					local Button = AR.CreatedReminders[Position]
					if (not Button) or (Button:IsVisible()) then
						Button = AR:CreateReminder(Position)
						Position = Position + 1
					end

					AR:SetIconPosition(Button, db)

					local filterCheck, reverseCheck = AR:FilterCheck(db), AR:FilterCheck(db, true)

					if db.filterType == 'COOLDOWN' and db.cooldownSpellID and filterCheck then
						local start, duration = GetSpellCooldown(db.cooldownSpellID)
						if (duration and duration > 1.5) then
							Button.cooldown:SetCooldown(start, duration)
						end

						Button.cooldown:SetShown((duration and duration > 0))
						Button.icon:SetTexture(select(3, GetSpellInfo(db.cooldownSpellID)))
						Button:SetShown((duration and duration == 0) or db.onCooldown)

						AR:UpdateColors(Button, db.cooldownSpellID)

						if (duration and duration > 1.5) and filterCheck and db.onCooldown then
							Button:SetAlpha(db.cooldownAlpha or .5)
						end
					elseif (db.filterType == 'WEAPON' or (db.filterType == 'SPELL' and db.spellGroup and PA:CountTable(db.spellGroup) > 0)) and filterCheck then
						if db.filterType == 'SPELL' then
							local hasBuff, hasDebuff = AR:FindPlayerAura(db.spellGroup, db.personal), AR:FindPlayerAura(db.spellGroup, nil, 'HARMFUL')
							local negate = AR:FindPlayerAura(db.negateGroup, db.personal)
							local skip = false
							if not (negate or hasBuff or hasDebuff) then
								for buff, value in pairs(db.spellGroup) do
									if value then
										local usable = IsUsableSpell(buff);
										if db.strictFilter then
											usable = usable and IsSpellKnownOrOverridesKnown(buff);
											skip = not usable;
										end
										if usable and AR:IsSpellOnCooldown(buff) then
											break
										end

										if usable or not db.strictFilter then
											Button.icon:SetTexture(select(3, GetSpellInfo(buff)))
											AR:UpdateColors(Button, buff)
											break
										end
									end
								end

								Button:SetShown(not skip and (((not hasBuff) and (not hasDebuff)) and not db.reverseCheck) or (reverseCheck and db.reverseCheck and ((hasBuff or hasDebuff) or ((not hasBuff) and (not hasDebuff)))))
							end
						end

						if db.filterType == 'WEAPON' then
							local hasOffhandWeapon = AR:HasOffHandWeapon(GetInventoryItemID('player', 17))
							local hasMainHandEnchant, _, _, hasOffHandEnchant = GetWeaponEnchantInfo()

							if (not hasMainHandEnchant) then
								Button.icon:SetTexture(GetInventoryItemTexture('player', 16))
							elseif (hasOffhandWeapon and not hasOffHandEnchant) then
								Button.icon:SetTexture(GetInventoryItemTexture('player', 17))
							end

							Button:SetShown(not (hasMainHandEnchant or hasOffHandEnchant))
							Button.icon:SetVertexColor(1, 1, 1)
						end

						if Button:IsShown() and not db.disableSound then
							AR:PlaySoundFile()
						end
					end
				end
			end
		end
	end
end

function AR:ResetSoundPlayback()
	AR.IsPlaying = false
end

function AR:PlaySoundFile()
	if AR.IsPlaying then return end

	AR.IsPlaying = true

	PlaySoundFile(PA.LSM:Fetch('sound', AR.db.Sound))

	AR:ScheduleTimer('ResetSoundPlayback', 10)
end

function AR:SetIconPosition(button, db)
	if not (button or db) then return end

	local xOffset = db.xOffset or 0
	local yOffset = db.yOffset or 200
	local size = db.size or 40

	button:ClearAllPoints()
	button:SetPoint('CENTER', UIParent, 'CENTER', xOffset, yOffset);
	button:SetSize(size, size)
end

function AR:CreateReminder(index)
	local frame = CreateFrame('Frame', 'AuraReminder'..index, UIParent)
	frame:Hide()
	frame:SetClampedToScreen(true)
	frame.icon = frame:CreateTexture(nil, 'OVERLAY')
	frame.icon:SetTexCoord(unpack(PA.TexCoords))
	frame.cooldown = CreateFrame('Cooldown', nil, frame, 'CooldownFrameTemplate')
	frame.cooldown:SetAllPoints(frame.icon)

	PA:SetTemplate(frame)
	PA:SetInside(frame.icon)

	tinsert(AR.CreatedReminders, frame)
	return frame
end

function AR:UpdateFilterGroup(group)
	for option, optionTable in pairs(PA.Options.args.AuraReminder.args.filterGroup.args[group].args) do
		if strmatch(option, "^AR") then
			optionTable.hidden = true
		end
	end

	if AR.db.Filters[selectedGroup][selectedFilter] and AR.db.Filters[selectedGroup][selectedFilter][group] then
		local i = 1
		for spell in pairs(AR.db.Filters[selectedGroup][selectedFilter][group]) do
			if spell and GetSpellInfo(spell) then
				local name = format('AR%s', i + 2)
				local optionName = PA.Options.args.AuraReminder.args.filterGroup.args[group].args[name]
				if not optionName then
					PA.Options.args.AuraReminder.args.filterGroup.args[group].args[name] = { type = 'toggle', width = 'double' }
					optionName = PA.Options.args.AuraReminder.args.filterGroup.args[group].args[name]
				end

				optionName.name = function() return format('%s (%s)', GetSpellInfo(spell), spell) end
				optionName.get = function() return spell and AR.db.Filters[selectedGroup][selectedFilter][group][spell] end
				optionName.set = function(_, value) AR.db.Filters[selectedGroup][selectedFilter][group][spell] = value end
				optionName.hidden = false

				i = i + 1
			end
		end
	end
end

function AR:CleanDB()
	-- Cleanup DB
	for _, filter in pairs({PA.MyClass, 'Global'}) do
		if AR.db.Filters and AR.db.Filters[filter] then
			for _, db in pairs(AR.db.Filters[filter]) do
				if db.role == 'ANY' then db.role = nil end
				if db.tree == 'ANY' then db.tree = nil end
				if db.talentTreeException == 'NONE' then db.talentTreeException = nil end
				if (db.level and db.level == 0) then db.level = nil end
				if not db.combat then db.combat = nil end
				if not db.instance then db.instance = nil end
				if not db.pvp then db.pvp = nil end
				if not db.onCooldown then db.onCooldown = nil end
				if not db.disableSound then db.disableSound = nil end
			end
		end
	end
end

local addGroupTemplate = { name = '', template = ''}

function AR:GetOptions()
	local AuraReminder = PA.ACH:Group(AR.Title, nil, nil, 'tree', function(info) return AR.db[info[#info]] end, function(info, value) AR.db[info[#info]] = value end)
	PA.Options.args.AuraReminder = AuraReminder

	AuraReminder.args = {
			Description = {
				order = 0,
				type = 'description',
				name = AR.Description,
			},
			Enable = {
				order = 1,
				type = 'toggle',
				name = PA.ACL['Enable'],
				set = function(info, value)
					AR.db[info[#info]] = value
					if (not AR.isEnabled) then
						AR:Initialize()
					else
						_G.StaticPopup_Show('PROJECTAZILROKA_RL')
					end
				end,
			},
			Sound = {
				order = 2,
				type = 'select', dialogControl = 'LSM30_Sound',
				name = PA.ACL['Sound'],
				desc = PA.ACL['Sound that will play when you have a warning icon displayed.'],
				values = PA.LSM:HashTable('sound'),
			},
			selectGroup = {
				order = 3,
				type = 'select',
				name = PA.ACL['Select Group'],
				get = function()
					if selectedGroup == 'Global' then
						return selectedGroup
					else
						return 'Class'
					end
				end,
				set = function(_, value)
					selectedFilter = nil
					if value == 'Class' then
						selectedGroup = PA.MyClass
					else
						selectedGroup = value
					end
				end,
				values = { Class = 'Class', Global = 'Global'},
			},
			selectFilter = {
				order = 4,
				type = 'select',
				name = PA.ACL['Select Filter'],
				get = function() return selectedFilter ~= '' and selectedFilter or '' end,
				set = function(_, value)
					selectedFilter = value ~= '' and value or nil
					AR:UpdateFilterGroup('spellGroup')
					AR:UpdateFilterGroup('negateGroup')
				end,
				values = function()
					wipe(filters)
					for filter in pairs(AR.db.Filters[selectedGroup]) do
						filters[filter] = filter
					end
					if not next(filters) then
						filters[''] = PA.ACL['None']
					end
					return filters
				end,
			},
			filterControl = {
				order = 5,
				type = 'group',
				name = PA.ACL['Filter Control'],
				inline = true,
				args = {
					addFilter = {
						order = 1,
						type = 'input',
						name = PA.ACL['New Filter Name'],
						get = function() return addGroupTemplate.name end,
						set = function(_, value)
							if AR.db.Filters[selectedGroup][value] then return end
							addGroupTemplate.name = value
						end,
					},
					addFilterTemplate = {
						order = 2,
						type = 'select',
						name = PA.ACL['New Filter Type'],
						get = function() return addGroupTemplate.template end,
						set = function(_, value)
							if AR.db.Filters[selectedGroup][value] then
								return
							end
							addGroupTemplate.template = value
						end,
						values = function()
							wipe(filterTypeList)
							filterTypeList.SPELL = PA.ACL['Spell']
							if selectedGroup ~= 'Global' then
								filterTypeList.WEAPON = PA.ACL['Weapon']
								filterTypeList.COOLDOWN = PA.ACL['Cooldown']
							end
							return filterTypeList
						end,
					},
					addFilterButton = {
						type = 'execute',
						order = 3,
						name = PA.ACL['Add Filter'],
						hidden = function() return addGroupTemplate.name == '' or addGroupTemplate.template == '' end,
						func = function()
							AR.db.Filters[selectedGroup][addGroupTemplate.name] = { enable = true, size = 50, filterType = addGroupTemplate.template }
							if addGroupTemplate.template == 'COOLDOWN' then
								AR.db.Filters[selectedGroup][addGroupTemplate.name].cooldownAlpha = .5
							--elseif addGroupTemplate.template == 'WEAPON' then
							else
								AR.db.Filters[selectedGroup][addGroupTemplate.name].spellGroup = {}
								AR.db.Filters[selectedGroup][addGroupTemplate.name].negateGroup = {}
							end

							selectedFilter = addGroupTemplate.name

							addGroupTemplate.name = ''
							addGroupTemplate.template = ''
						end,
					},
					deleteGroup = {
						order = 5,
						type = 'select',
						name = PA.ACL['Remove Filter'],
						get = function() return '' end,
						confirm = function(_, value) return PA.ACL['Remove Filter']..' - '..value end,
						set = function(_, value)
							selectedFilter = nil
							if DefaultFilters[selectedGroup][value] then
								AR.db.Filters[selectedGroup][value].enable = false;
							else
								AR.db.Filters[selectedGroup][value] = nil;
							end
						end,
						values = function()
							wipe(filters)
							for filter in pairs(AR.db.Filters[selectedGroup]) do
								filters[filter] = filter
							end
							if not next(filters) then
								filters[''] = PA.ACL['None']
							end
							return filters
						end,
					},
				},
			},
			filterGroup = {
				order = 8,
				type = 'group',
				name = function() return selectedFilter end,
				inline = true,
				get = function(info) return AR.db.Filters[selectedGroup][selectedFilter][info[#info]] end,
				set = function(info, value) AR.db.Filters[selectedGroup][selectedFilter][info[#info]] = value end,
				hidden = function() return not selectedFilter end,
				args = {
					enable = {
						order = 1,
						type = 'toggle',
						name = PA.ACL['Enable'],
					},
					filterType = {
						order = 2,
						type = 'select',
						name = PA.ACL['Filter Type'],
						desc = PA.ACL['Change this if you want the Reminder module to check for weapon enchants, setting this will cause it to ignore any spells listed.'],
						values = function()
							wipe(filterTypeList)
							filterTypeList.SPELL = PA.ACL['Spell']
							if selectedGroup ~= 'Global' then
								filterTypeList.WEAPON = PA.ACL['Weapon']
								filterTypeList.COOLDOWN = PA.ACL['Cooldown']
							end
							return filterTypeList
						end,
					},
					xOffset = {
						order = 3,
						type = 'range',
						name = PA.ACL['X Offset'],
						min = -(PA.ScreenWidth / 2), max = (PA.ScreenWidth / 2), step = 1,
					},
					yOffset = {
						order = 4,
						type = 'range',
						name = PA.ACL['Y Offset'],
						min = -(PA.ScreenHeight / 2), max = (PA.ScreenHeight / 2), step = 1,
					},
					size = {
						order = 5,
						type = 'range',
						name = PA.ACL['Size'],
						min = 0, max = 128, step = 1,
					},
					conditions = {
						order = 10,
						type = 'multiselect',
						name = PA.ACL['Conditions'],
						get = function(_, key) return AR.db.Filters[selectedGroup][selectedFilter][key] end,
						set = function(_, key, value) AR.db.Filters[selectedGroup][selectedFilter][key] = value end,
						values = {
							instance = PA.ACL['Inside Raid/Party'],
							pvp = PA.ACL['Inside BG/Arena'],
							combat = PA.ACL['Combat'],
						},
					},
					filterConditions = {
						order = 11,
						type = 'group',
						name = PA.ACL['Filter Conditions'],
						inline = true,
						args = {
							level = {
								order = 4,
								type = 'range',
								name = PA.ACL['Level Requirement'],
								desc = PA.ACL['Level requirement for the icon to be able to display. 0 for disabled.'],
								min = 0, max = MAX_PLAYER_LEVEL, step = 1,
							},
							personal = {
								order = 5,
								type = 'toggle',
								name = PA.ACL['Personal Buffs'],
								desc = PA.ACL['Only check if the buff is coming from you.'],
								hidden = function() return AR.db.Filters[selectedGroup][selectedFilter].filterType ~= 'SPELL' end,
							},
							reverseCheck = {
								order = 6,
								type = 'toggle',
								name = PA.ACL['Reverse Check'],
								desc = PA.ACL['Instead of hiding the frame when you have the buff, show the frame when you have the buff.'],
								hidden = function() return AR.db.Filters[selectedGroup][selectedFilter].filterType ~= 'SPELL' end,
							},
							strictFilter = {
								order = 7,
								type = 'toggle',
								name = PA.ACL['Strict Filter'],
								desc = PA.ACL['This ensures you can only see spells that you actually know. You may want to uncheck this option if you are trying to monitor a spell that is not directly clickable out of your spellbook.'],
								hidden = function() return AR.db.Filters[selectedGroup][selectedFilter].filterType == 'COOLDOWN' end,
							},
							disableSound = {
								order = 8,
								type = 'toggle',
								name = PA.ACL['Disable Sound'],
								hidden = function() return AR.db.Filters[selectedGroup][selectedFilter].filterType == 'COOLDOWN' end,
							},
						},
					},
					cooldownConditions = {
						order = 12,
						type = 'group',
						name = PA.ACL['Cooldown Conditions'],
						inline = true,
						hidden = function() return AR.db.Filters[selectedGroup][selectedFilter].filterType ~= 'COOLDOWN' or selectedGroup == 'Global' end,
						args = {
							dscription = {
								order = 0,
								type = 'description',
								fontSize = 'medium',
								name = function()
									local spellID = AR.db.Filters[selectedGroup][selectedFilter].cooldownSpellID
									if not spellID or spellID == '' then return end
									return format('%s (%s)', GetSpellInfo(spellID), spellID)
								end,
								hidden = function() return AR.db.Filters[selectedGroup][selectedFilter].cooldownSpellID == '' end,
							},
							cooldownSpellID = {
								order = 1,
								type = 'input',
								name = PA.ACL['Spell ID'],
								get = function(info) return tostring(AR.db.Filters[selectedGroup][selectedFilter][info[#info]] or '') end,
								set = function(info, value)
									value = tonumber(value)
									if not value then return end

									AR.db.Filters[selectedGroup][selectedFilter][info[#info]] = value
								end,
							},
							onCooldown = {
								order = 2,
								type = 'toggle',
								name = PA.ACL['Show On Cooldown'],
							},
							cooldownAlpha = {
								order = 3,
								type = 'range',
								name = PA.ACL['Cooldown Alpha'],
								min = 0, max = 1, step = 0.1,
								hidden = function() return not AR.db.Filters[selectedGroup][selectedFilter].onCooldown end,
							},
						},
					},
					spellGroup = {
						order = 13,
						type = 'group',
						name = PA.ACL['Spells'],
						inline = true,
						get = function() AR:UpdateFilterGroup('spellGroup') return '' end,
						hidden = function() return AR.db.Filters[selectedGroup][selectedFilter].filterType ~= 'SPELL' end,
						args = {
							AddSpell = {
								order = 0,
								type = 'input',
								name = PA.ACL['New ID'],
								set = function(_, value)
									value = tonumber(value)
									if not value then return end

									AR.db.Filters[selectedGroup][selectedFilter].spellGroup = AR.db.Filters[selectedGroup][selectedFilter].spellGroup or {}
									AR.db.Filters[selectedGroup][selectedFilter].spellGroup[value] = true
									AR:UpdateFilterGroup('spellGroup')
								end,
							},
							RemoveSpell = {
								order = 1,
								type = 'select',
								name = PA.ACL['Remove ID'],
								get = function() return '' end,
								set = function(_, value)
									AR.db.Filters[selectedGroup][selectedFilter].spellGroup[value] = nil;
									AR:UpdateFilterGroup('spellGroup')
								end,
								values = function()
									wipe(spellList)
									if AR.db.Filters[selectedGroup][selectedFilter].spellGroup then
										for spellID in pairs(AR.db.Filters[selectedGroup][selectedFilter].spellGroup) do
											local name = GetSpellInfo(spellID)
											spellList[spellID] = name and format('%s (%s)', name, spellID) or spellID
										end
									end
									return spellList
								end,
							},
							spacer = PA.ACH:Spacer(2)
						},
					},
					negateGroup = {
						order = 14,
						type = 'group',
						name = PA.ACL['Negate Spells'],
						inline = true,
						get = function() AR:UpdateFilterGroup('negateGroup') return '' end,
						hidden = function() return AR.db.Filters[selectedGroup][selectedFilter].filterType ~= 'SPELL' end,
						args = {
							AddSpell = {
								order = 0,
								type = 'input',
								name = PA.ACL['New ID'],
								set = function(_, value)
									value = tonumber(value)
									if not value then return end

									AR.db.Filters[selectedGroup][selectedFilter].negateGroup = AR.db.Filters[selectedGroup][selectedFilter].negateGroup or {}
									AR.db.Filters[selectedGroup][selectedFilter].negateGroup[value] = true
									AR:UpdateFilterGroup('negateGroup')
								end,
							},
							RemoveSpell = {
								order = 1,
								type = 'select',
								name = PA.ACL['Remove ID'],
								get = function() return '' end,
								set = function(_, value)
									AR.db.Filters[selectedGroup][selectedFilter].negateGroup[value] = nil;
									AR:UpdateFilterGroup('negateGroup')
								end,
								values = function()
									wipe(spellList)
									if AR.db.Filters[selectedGroup][selectedFilter].negateGroup then
										for spellID in pairs(AR.db.Filters[selectedGroup][selectedFilter].negateGroup) do
											local name = GetSpellInfo(spellID)
											spellList[spellID] = name and format('%s (%s)', name, spellID) or spellID
										end
									end
									if not next(spellList) then
										spellList[''] = PA.ACL['None']
									end
									return spellList
								end,
							},
							spacer = PA.ACH:Spacer(2)
						},
					},
				},
			},
		}

	PA.Options.args.AuraReminder.args.AuthorHeader = PA.ACH:Header(PA.ACL['Authors:'], -2)
	PA.Options.args.AuraReminder.args.Authors = PA.ACH:Description(AR.Authors, -1, 'large')

	if PA.Retail then
		local optionGroup = PA.Options.args.AuraReminder.args.filterGroup.args.filterConditions.args

		local Specializations = { ['ANY'] = PA.ACL['Any'] }

		for i = 1, 4 do
			Specializations[tostring(i)] = select(2, GetSpecializationInfo(i))
		end

		optionGroup.role = PA.ACH:Select(PA.ACL['Role'], PA.ACL['You must be a certain role for the icon to appear.'], 1, { TANK = PA.ACL['Tank'], DAMAGER = PA.ACL['Damage'], HEALER = PA.ACL['Healer'], ANY = PA.ACL['Any'] }, nil, nil, function(info) return AR.db.Filters[selectedGroup][selectedFilter][info[#info]] or 'ANY' end, nil, nil, function() return selectedGroup == 'Global' end)
		optionGroup.tree = PA.ACH:Select(PA.ACL['Talent Tree'], PA.ACL['You must be using a certain talent tree for the icon to show.'], 2, Specializations, nil, nil, function() return tostring(AR.db.Filters[PA.MyClass][selectedFilter].tree or 'ANY') end, function(_, value) if value == 'ANY' then AR.db.Filters[PA.MyClass][selectedFilter].tree = 'ANY' else AR.db.Filters[PA.MyClass][selectedFilter].tree = tonumber(value) end end, nil, function() return selectedGroup == 'Global' or AR.db.Filters[PA.MyClass][selectedFilter].reverseCheck end)
		optionGroup.talentTreeException = PA.ACH:Select(PA.ACL['Tree Exception'], PA.ACL['Set a talent tree to not follow the reverse check.'], 2, CopyTable(Specializations), nil, nil, function() return tostring(AR.db.Filters[PA.MyClass][selectedFilter]['talentTreeException'] or 'NONE') end, function(_, value) if value == 'NONE' then AR.db.Filters[PA.MyClass][selectedFilter].talentTreeException = nil else AR.db.Filters[PA.MyClass][selectedFilter]['talentTreeException'] = tonumber(value) end; end, nil, function() return selectedGroup == 'Global' or not AR.db.Filters[PA.MyClass][selectedFilter].reverseCheck end)
		optionGroup.talentTreeException.values.NONE = PA.ACL['None']
	end
end

function AR:BuildProfile()
	PA.Defaults.profile.AuraReminder = {
		Enable = true,
		Sound = 'Warning',
		Filters = { Global = {} },
	}

	for k in pairs(LOCALIZED_CLASS_NAMES_MALE) do PA.Defaults.profile.AuraReminder.Filters[k] = {} end

	DefaultFilters = CopyTable(PA.Defaults.profile.AuraReminder.Filters)
end

function AR:Initialize()
	AR.db = PA.db.AuraReminder

	if AR.db.Enable ~= true then
		return
	end

	AR.isEnabled = true

	AR:ScheduleRepeatingTimer('Reminder_Update', .5)

	AR:CleanDB()
	AR:RegisterEvent("PLAYER_LOGOUT", 'CleanDB')
end
