--------------------------------------------------------------------------------------------------------
--                                             Localized global                                       --
--------------------------------------------------------------------------------------------------------
local _G = getfenv(0)

--------------------------------------------------------------------------------------------------------
--                                         AceAddon init                                              --
--------------------------------------------------------------------------------------------------------
local MODNAME = "ErrorFilter"
local addon = LibStub("AceAddon-3.0"):NewAddon(MODNAME, "AceEvent-3.0", "LibSink-2.0")
_G.ErrorFilter = addon

local L = LibStub("AceLocale-3.0"):GetLocale(MODNAME)
local AceConfig = LibStub("AceConfig-3.0")
local AceConfigDialog = LibStub("AceConfigDialog-3.0")
local AceDB = LibStub("AceDB-3.0")
local AceDBOptions = LibStub("AceDBOptions-3.0")

--------------------------------------------------------------------------------------------------------
--                                        ErrorFilter variables                                       --
--------------------------------------------------------------------------------------------------------
local profileDB
local DATABASE_DEFAULTS = {
	profile = {
		mode = 1,
		updateOnlyInCombat = false,
		filters = {
			-- Errors
			[INTERRUPTED] = false,
			[ERR_ABILITY_COOLDOWN] = true,
			[ERR_ATTACK_CHANNEL] = false,
			[ERR_ATTACK_CHARMED] = false,
			[ERR_ATTACK_CONFUSED] = false,
			[ERR_ATTACK_DEAD] = false,
			[ERR_ATTACK_FLEEING] = false,
			[ERR_ATTACK_MOUNTED] = true,
			[ERR_ATTACK_PACIFIED] = false,
			[ERR_ATTACK_STUNNED] = false,
			[ERR_AUTOFOLLOW_TOO_FAR] = false,
			[ERR_BADATTACKFACING] = false,
			[ERR_BADATTACKPOS] = false,
			[ERR_CLIENT_LOCKED_OUT] = false,
			[ERR_GENERIC_NO_TARGET] = true,
			[ERR_GENERIC_NO_VALID_TARGETS] = true,
			[ERR_GENERIC_STUNNED] = false,
			[ERR_INVALID_ATTACK_TARGET] = true,
			[ERR_ITEM_COOLDOWN] = true,
			[ERR_NOEMOTEWHILERUNNING] = false,
			[ERR_NOT_IN_COMBAT] = false,
			[ERR_NOT_WHILE_DISARMED] = false,
			[ERR_NOT_WHILE_FALLING] = false,
			[ERR_NOT_WHILE_FATIGUED] = true,
			[ERR_NOT_WHILE_MOUNTED] = false,
			[ERR_NOT_WHILE_SHAPESHIFTED] = false,
			[ERR_NO_ATTACK_TARGET] = true,
			[ERR_NO_PET] = false,
			[ERR_OBJECT_IS_BUSY] = false,

			-- Out of...
			[ERR_OUT_OF_ARCANE_CHARGES] = true,
			[ERR_OUT_OF_BALANCE_NEGATIVE] = true,
			[ERR_OUT_OF_BALANCE_POSITIVE] = true,
			[ERR_OUT_OF_BURNING_EMBERS] = true,
			[ERR_OUT_OF_CHI] = true,
			[ERR_OUT_OF_COMBO_POINTS] = true,
			[ERR_OUT_OF_DARK_FORCE] = false,
			[ERR_OUT_OF_DEMONIC_FURY] = true,
			[ERR_OUT_OF_ENERGY] = true,
			[ERR_OUT_OF_FOCUS] = true,
			[ERR_OUT_OF_HEALTH] = true,
			[ERR_OUT_OF_HOLY_POWER] = true,
			[ERR_OUT_OF_LIGHT_FORCE] = false,
			[ERR_OUT_OF_MANA] = true,
			[ERR_OUT_OF_POWER_DISPLAY] = false,
			[ERR_OUT_OF_RAGE] = true,
			[ERR_OUT_OF_RANGE] = true,
			[ERR_OUT_OF_RUNES] = true,
			[ERR_OUT_OF_RUNIC_POWER] = true,
			[ERR_OUT_OF_SHADOW_ORBS] = true,
			[ERR_OUT_OF_SOUL_SHARDS] = true,
			[ERR_SPELL_COOLDOWN] = true,
			[ERR_SPELL_OUT_OF_RANGE] = false,
			[ERR_TOO_FAR_TO_INTERACT] = false,
			[ERR_USE_BAD_ANGLE] = false,
			[ERR_USE_CANT_IMMUNE] = false,
			[ERR_USE_TOO_FAR] = false,
			[SPELL_FAILED_BAD_IMPLICIT_TARGETS] = true,

			-- Spell failed...
			[SPELL_FAILED_ABOVE_MAXIMUM_SKILL_RANK] = false,
			[SPELL_FAILED_AFFECTING_COMBAT] = false,
			[SPELL_FAILED_AURA_BOUNCED] = false,
			[SPELL_FAILED_BAD_IMPLICIT_TARGETS] = false,
			[SPELL_FAILED_BAD_TARGETS] = true,
			[SPELL_FAILED_CANT_BE_CHARMED] = false,
			[SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW] = true,
			[SPELL_FAILED_CANT_STEALTH] = false,
			[SPELL_FAILED_CASTER_AURASTATE] = true,
			[SPELL_FAILED_CASTER_DEAD] = true,
			[SPELL_FAILED_CASTER_DEAD_FEMALE] = true,
			[SPELL_FAILED_CAST_NOT_HERE] = false,
			[SPELL_FAILED_CHARMED] = true,
			[SPELL_FAILED_CHEST_IN_USE] = false,
			[SPELL_FAILED_CONFUSED] = true,
			[SPELL_FAILED_DAMAGE_IMMUNE] = false,
			[SPELL_FAILED_FALLING] = true,
			[SPELL_FAILED_FLEEING] = true,
			[SPELL_FAILED_IMMUNE] = true,
			[SPELL_FAILED_INCORRECT_AREA] = false,
			[SPELL_FAILED_INTERRUPTED] = false,
			[SPELL_FAILED_INTERRUPTED_COMBAT] = false,
			[SPELL_FAILED_IN_COMBAT_RES_LIMIT_REACHED] = false,
			[SPELL_FAILED_LINE_OF_SIGHT] = false,
			[SPELL_FAILED_LOWLEVEL] = false,
			[SPELL_FAILED_LOW_CASTLEVEL] = false,
			[SPELL_FAILED_MOVING] = true,
			[SPELL_FAILED_NOPATH] = true,
			[SPELL_FAILED_NOTHING_TO_DISPEL] = false,
			[SPELL_FAILED_NOTHING_TO_STEAL] = false,
			[SPELL_FAILED_NOT_BEHIND] = false,
			[SPELL_FAILED_NOT_FISHABLE] = false,
			[SPELL_FAILED_NOT_FLYING] = false,
			[SPELL_FAILED_NOT_INFRONT] = false,
			[SPELL_FAILED_NOT_IN_CONTROL] = false,
			[SPELL_FAILED_NOT_KNOWN] = false,
			[SPELL_FAILED_NOT_MOUNTED] = false,
			[SPELL_FAILED_NOT_ON_DAMAGE_IMMUNE] = false,
			[SPELL_FAILED_NOT_ON_GROUND] = false,
			[SPELL_FAILED_NOT_ON_MOUNTED] = false,
			[SPELL_FAILED_NOT_ON_SHAPESHIFT] = false,
			[SPELL_FAILED_NOT_ON_STEALTHED] = false,
			[SPELL_FAILED_NOT_ON_TAXI] = false,
			[SPELL_FAILED_NOT_ON_TRANSPORT] = false,
			[SPELL_FAILED_NOT_READY] = false,
			[SPELL_FAILED_NOT_SHAPESHIFT] = false,
			[SPELL_FAILED_NOT_STANDING] = false,
			[SPELL_FAILED_NOT_WHILE_FATIGUED] = false,
			[SPELL_FAILED_NOT_WHILE_GHOST] = true,
			[SPELL_FAILED_NOT_WHILE_LOOTING] = false,
			[SPELL_FAILED_NO_ACTIONS] = false,
			[SPELL_FAILED_NO_CHARGES_REMAIN] = false,
			[SPELL_FAILED_NO_COMBO_POINTS] = true,
			[SPELL_FAILED_NO_ITEMS_WHILE_SHAPESHIFTED] = false,
			[SPELL_FAILED_NO_PET] = false,
			[SPELL_FAILED_ONLY_STEALTHED] = false,
			[SPELL_FAILED_OUT_OF_RANGE] = true,
			[SPELL_FAILED_PACIFIED] = true,
			[SPELL_FAILED_POSSESSED] = true,
			[SPELL_FAILED_PREVENTED_BY_MECHANIC] = false,
			[SPELL_FAILED_ROOTED] = true,
			[SPELL_FAILED_SILENCED] = true,
			[SPELL_FAILED_SPELL_IN_PROGRESS] = true,
			[SPELL_FAILED_SPELL_LEARNED] = false,
			[SPELL_FAILED_STUNNED] = true,
			[SPELL_FAILED_SUMMON_PENDING] = false,
			[SPELL_FAILED_TARGETS_DEAD] = true,
			[SPELL_FAILED_TARGET_AFFECTING_COMBAT] = false,
			[SPELL_FAILED_TARGET_AURASTATE] = true,
			[SPELL_FAILED_TARGET_ENRAGED] = false,
			[SPELL_FAILED_TARGET_FRIENDLY] = false,
			[SPELL_FAILED_TOO_CLOSE] = false,
			[SPELL_FAILED_UNIT_NOT_BEHIND] = false,
			[SPELL_FAILED_UNIT_NOT_INFRONT] = false,
			[SPELL_FAILED_VISION_OBSCURED] = false,
		},
		allows = {
			-- Errors
			[INTERRUPTED] = true,
			[ERR_ABILITY_COOLDOWN] = true,
			[ERR_ATTACK_CHANNEL] = true,
			[ERR_ATTACK_CHARMED] = true,
			[ERR_ATTACK_CONFUSED] = true,
			[ERR_ATTACK_DEAD] = true,
			[ERR_ATTACK_FLEEING] = true,
			[ERR_ATTACK_MOUNTED] = true,
			[ERR_ATTACK_PACIFIED] = true,
			[ERR_ATTACK_STUNNED] = true,
			[ERR_AUTOFOLLOW_TOO_FAR] = false,
			[ERR_BADATTACKFACING] = true,
			[ERR_BADATTACKPOS] = true,
			[ERR_CLIENT_LOCKED_OUT] = true,
			[ERR_GENERIC_NO_TARGET] = true,
			[ERR_GENERIC_NO_VALID_TARGETS] = true,
			[ERR_GENERIC_STUNNED] = false,
			[ERR_INVALID_ATTACK_TARGET] = true,
			[ERR_ITEM_COOLDOWN] = true,
			[ERR_NOEMOTEWHILERUNNING] = false,
			[ERR_NOT_IN_COMBAT] = true,
			[ERR_NOT_WHILE_DISARMED] = true,
			[ERR_NOT_WHILE_FALLING] = true,
			[ERR_NOT_WHILE_FATIGUED] = true,
			[ERR_NOT_WHILE_MOUNTED] = true,
			[ERR_NOT_WHILE_SHAPESHIFTED] = false,
			[ERR_NO_ATTACK_TARGET] = true,
			[ERR_NO_PET] = false,
			[ERR_OBJECT_IS_BUSY] = false,

			-- Out of...
			[ERR_OUT_OF_ARCANE_CHARGES] = true,
			[ERR_OUT_OF_BALANCE_NEGATIVE] = true,
			[ERR_OUT_OF_BALANCE_POSITIVE] = true,
			[ERR_OUT_OF_BURNING_EMBERS] = true,
			[ERR_OUT_OF_CHI] = true,
			[ERR_OUT_OF_COMBO_POINTS] = true,
			[ERR_OUT_OF_DARK_FORCE] = false,
			[ERR_OUT_OF_DEMONIC_FURY] = true,
			[ERR_OUT_OF_ENERGY] = true,
			[ERR_OUT_OF_FOCUS] = true,
			[ERR_OUT_OF_HEALTH] = true,
			[ERR_OUT_OF_HOLY_POWER] = true,
			[ERR_OUT_OF_LIGHT_FORCE] = false,
			[ERR_OUT_OF_MANA] = true,
			[ERR_OUT_OF_POWER_DISPLAY] = false,
			[ERR_OUT_OF_RAGE] = true,
			[ERR_OUT_OF_RANGE] = true,
			[ERR_OUT_OF_RUNES] = true,
			[ERR_OUT_OF_RUNIC_POWER] = true,
			[ERR_OUT_OF_SHADOW_ORBS] = true,
			[ERR_OUT_OF_SOUL_SHARDS] = true,
			[ERR_SPELL_COOLDOWN] = true,
			[ERR_SPELL_OUT_OF_RANGE] = true,
			[ERR_TOO_FAR_TO_INTERACT] = true,
			[ERR_USE_BAD_ANGLE] = true,
			[ERR_USE_CANT_IMMUNE] = false,
			[ERR_USE_TOO_FAR] = true,

			-- Spell failed...
			[SPELL_FAILED_ABOVE_MAXIMUM_SKILL_RANK] = false,
			[SPELL_FAILED_AFFECTING_COMBAT] = false,
			[SPELL_FAILED_AURA_BOUNCED] = false,
			[SPELL_FAILED_BAD_IMPLICIT_TARGETS] = false,
			[SPELL_FAILED_BAD_TARGETS] = true,
			[SPELL_FAILED_CANT_BE_CHARMED] = false,
			[SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW] = true,
			[SPELL_FAILED_CANT_STEALTH] = false,
			[SPELL_FAILED_CASTER_AURASTATE] = true,
			[SPELL_FAILED_CASTER_DEAD] = true,
			[SPELL_FAILED_CASTER_DEAD_FEMALE] = true,
			[SPELL_FAILED_CAST_NOT_HERE] = false,
			[SPELL_FAILED_CHARMED] = true,
			[SPELL_FAILED_CHEST_IN_USE] = false,
			[SPELL_FAILED_CONFUSED] = true,
			[SPELL_FAILED_DAMAGE_IMMUNE] = false,
			[SPELL_FAILED_FALLING] = true,
			[SPELL_FAILED_FLEEING] = true,
			[SPELL_FAILED_IMMUNE] = true,
			[SPELL_FAILED_INCORRECT_AREA] = false,
			[SPELL_FAILED_INTERRUPTED] = false,
			[SPELL_FAILED_INTERRUPTED_COMBAT] = false,
			[SPELL_FAILED_IN_COMBAT_RES_LIMIT_REACHED] = false,
			[SPELL_FAILED_LINE_OF_SIGHT] = false,
			[SPELL_FAILED_LOWLEVEL] = false,
			[SPELL_FAILED_LOW_CASTLEVEL] = false,
			[SPELL_FAILED_MOVING] = true,
			[SPELL_FAILED_NOPATH] = true,
			[SPELL_FAILED_NOTHING_TO_DISPEL] = false,
			[SPELL_FAILED_NOTHING_TO_STEAL] = false,
			[SPELL_FAILED_NOT_BEHIND] = false,
			[SPELL_FAILED_NOT_FISHABLE] = false,
			[SPELL_FAILED_NOT_FLYING] = false,
			[SPELL_FAILED_NOT_INFRONT] = false,
			[SPELL_FAILED_NOT_IN_CONTROL] = false,
			[SPELL_FAILED_NOT_KNOWN] = false,
			[SPELL_FAILED_NOT_MOUNTED] = false,
			[SPELL_FAILED_NOT_ON_DAMAGE_IMMUNE] = false,
			[SPELL_FAILED_NOT_ON_GROUND] = false,
			[SPELL_FAILED_NOT_ON_MOUNTED] = false,
			[SPELL_FAILED_NOT_ON_SHAPESHIFT] = false,
			[SPELL_FAILED_NOT_ON_STEALTHED] = false,
			[SPELL_FAILED_NOT_ON_TAXI] = false,
			[SPELL_FAILED_NOT_ON_TRANSPORT] = false,
			[SPELL_FAILED_NOT_READY] = false,
			[SPELL_FAILED_NOT_SHAPESHIFT] = false,
			[SPELL_FAILED_NOT_STANDING] = false,
			[SPELL_FAILED_NOT_WHILE_FATIGUED] = false,
			[SPELL_FAILED_NOT_WHILE_GHOST] = true,
			[SPELL_FAILED_NOT_WHILE_LOOTING] = false,
			[SPELL_FAILED_NO_ACTIONS] = false,
			[SPELL_FAILED_NO_CHARGES_REMAIN] = false,
			[SPELL_FAILED_NO_COMBO_POINTS] = true,
			[SPELL_FAILED_NO_ITEMS_WHILE_SHAPESHIFTED] = false,
			[SPELL_FAILED_NO_PET] = false,
			[SPELL_FAILED_ONLY_STEALTHED] = false,
			[SPELL_FAILED_OUT_OF_RANGE] = true,
			[SPELL_FAILED_PACIFIED] = true,
			[SPELL_FAILED_POSSESSED] = true,
			[SPELL_FAILED_PREVENTED_BY_MECHANIC] = false,
			[SPELL_FAILED_ROOTED] = true,
			[SPELL_FAILED_SILENCED] = true,
			[SPELL_FAILED_SPELL_IN_PROGRESS] = false,
			[SPELL_FAILED_SPELL_LEARNED] = false,
			[SPELL_FAILED_STUNNED] = true,
			[SPELL_FAILED_SUMMON_PENDING] = false,
			[SPELL_FAILED_TARGETS_DEAD] = true,
			[SPELL_FAILED_TARGET_AFFECTING_COMBAT] = false,
			[SPELL_FAILED_TARGET_AURASTATE] = false,
			[SPELL_FAILED_TARGET_ENRAGED] = false,
			[SPELL_FAILED_TARGET_FRIENDLY] = false,
			[SPELL_FAILED_TOO_CLOSE] = false,
			[SPELL_FAILED_UNIT_NOT_BEHIND] = false,
			[SPELL_FAILED_UNIT_NOT_INFRONT] = false,
			[SPELL_FAILED_VISION_OBSCURED] = false,
		},
		custom_filters = {},
		custom_allows = {},
		sink20OutputSink = "UIErrorsFrame",
		throttle = true,
	},
}
-- sort by key
local a = {}
local filters_order = {}
for n in pairs(DATABASE_DEFAULTS.profile.filters) do table.insert(a, n) end
table.sort(a)
for i, n in ipairs(a) do filters_order[n] = i end

a = {}
local allows_order = {}
for n in pairs(DATABASE_DEFAULTS.profile.allows) do table.insert(a, n) end
table.sort(a)
for i, n in ipairs(a) do allows_order[n] = i end

local DO_NOTHING = 0
local FILTER_ONLY = 1
local ALLOW_ONLY = 2
local FILTER_ALL = 3
local REMOVE_FRAME = 4
local THROTTLE = 5

local IN_COMBAT = 0
local OUT_OF_COMBAT = 1
local state = OUT_OF_COMBAT

local lastDisplay = {}

--------------------------------------------------------------------------------------------------------
--                                   ErrorFilter options panel                                        --
--------------------------------------------------------------------------------------------------------
addon.options = {
	type = "group",
	name = MODNAME,
	args = {
		general = {
			order = 1,
			type = "group",
			name = L["General Settings"],
			cmdInline = true,
			args = {
				unregister = {
					order = 1,
					type = "select",
					style = "dropdown",
					name = L["Operation mode:"],
					desc = L["Choose how do you want ErrorFilter to work."],
					get = function()
						return profileDB.mode
					end,
					set = function(key, value)
						profileDB.mode = value
						addon:UpdateEvents()
					end,
					values = function()
						return {
							[DO_NOTHING] = L["Do nothing"],
							[THROTTLE] = L["Throttle"],
							[FILTER_ONLY] = L["Filter only ..."],
							[ALLOW_ONLY] = L["Allow only ..."],
							[FILTER_ALL] = L["Filter all errors"],
							[REMOVE_FRAME] = L["Remove UIErrorFrame"],
						}
					end,
				},
				combat = {
					order = 2,
					type = "toggle",
					name = L["Filter only in combat."],
					desc = L["Toggle to stop filtering while out of combat."],
					get = function()
						return profileDB.updateOnlyInCombat
					end,
					set = function(key, value)
						profileDB.updateOnlyInCombat = value
					end,
					disabled = function()
						return (profileDB.mode == DO_NOTHING) or (profileDB.mode == REMOVE_FRAME)
					end,
				},
				separator = {
					order = 3,
					type = "description",
					name = "",
				},
				warning1 = {
					order = 10,
					type = "execute",
					name = L["Set filters"],
					desc = L["Open the menu to set custom filters."],
					func = function()
						InterfaceOptionsFrame_OpenToCategory(addon.optionsFrames.filters)
					end,
					hidden = function()
						return not (profileDB.mode == FILTER_ONLY)
					end,
				},
				warning2 = {
					order = 11,
					type = "execute",
					name = L["Set filters"],
					desc = L["Open the menu to set custom filters."],
					func = function()
						InterfaceOptionsFrame_OpenToCategory(addon.optionsFrames.allows)
					end,
					hidden = function()
						return not (profileDB.mode == ALLOW_ONLY)
					end,
				},
				warning3 = {
					order = 12,
					type = "description",
					name = "|cFFFF0202"..L["Warning! This will prevent all error messages from appearing in the UI Error Frame."].."|r",
					hidden = function()
						return not (profileDB.mode == FILTER_ALL)
					end,
				},
				warning4 = {
					order = 13,
					type = "description",
					name = "|cFFFF0202"..L["Warning! This will prevent any message from appearing in the UI Error Frame, including quest updates text."].."|r",
					hidden = function()
						return not (profileDB.mode == REMOVE_FRAME)
					end,
				},
				separator1 = {
					order = 20,
					type = "description",
					name = "\n",
				},
				throttle = {
					order = 21,
					type = "toggle",
					name = L["Throttle messages."],
					desc = L["Toggle to allow each message only once every 5 seconds."],
					get = function()
						return profileDB.throttle
					end,
					set = function(key, value)
						profileDB.throttle = value
					end,
					disabled = function()
						return (profileDB.mode == DO_NOTHING) or (profileDB.mode == REMOVE_FRAME)
					end,
				},
			},
		},
		filters = {
			order = 1,
			type = "group",
			name = L["Filtered errors"],
			args = {
				separator1 = {
					order = 1,
					type = "header",
					name = "|cFF02FF02"..L["Manage custom filters:"].."|r",
				},
				new = {
					order = 2,
					type = "input",
					width = "full",
					name = L["New"],
					desc = L["Add a new string."],
					get = false,
					set = function(key, value)
						tinsert(profileDB.custom_filters, string.lower(value))
					end,
					disabled = function()
						return not (profileDB.mode == FILTER_ONLY)
					end,
				},
				delete = {
					order = 3,
					type = "select",
					width = "full",
					name = L["Delete"],
					desc = L["Delete a string from the list"],
					get = false,
					set = function(key, value)
						tremove(profileDB.custom_filters, value)
					end,
					values = function()
						return profileDB.custom_filters
					end,
					disabled = function()
						return not ((#profileDB.custom_filters > 0) and (profileDB.mode == FILTER_ONLY))
					end,
				},
				separator2 = {
					order = 9,
					type = "description",
					name = "\n",
				},
				separator3 = {
					order = 10,
					type = "header",
					name = "|cFF02FF02"..L["Choose the errors you do not want to see:"].."|r",
				},
			},
		},
		allows = {
			order = 1,
			type = "group",
			name = L["Allowed errors"],
			args = {
				separator1 = {
					order = 1,
					type = "header",
					name = "|cFF02FF02"..L["Manage custom allows:"].."|r",
				},
				new = {
					order = 2,
					type = "input",
					width = "full",
					name = L["New"],
					desc = L["Add a new string."],
					get = false,
					set = function(key, value)
						tinsert(profileDB.custom_allows, string.lower(value))
					end,
					disabled = function()
						return not (profileDB.mode == ALLOW_ONLY)
					end,
				},
				delete = {
					order = 3,
					type = "select",
					width = "full",
					name = L["Delete"],
					desc = L["Delete a string from the list"],
					get = false,
					set = function(key, value)
						tremove(profileDB.custom_allows, value)
					end,
					values = function()
						return profileDB.custom_allows
					end,
					disabled = function()
						return not ((#profileDB.custom_allows > 0) and (profileDB.mode == ALLOW_ONLY))
					end,
				},
				separator2 = {
					order = 9,
					type = "description",
					name = "\n",
				},
				separator3 = {
					order = 10,
					type = "header",
					name = "|cFF02FF02"..L["Choose the errors you want to see:"].."|r",
				},
			},
		},
	},
}

-- generate filters submenu
for k, v in pairs(DATABASE_DEFAULTS.profile.filters) do
	addon.options.args.filters.args[string.format("error"..filters_order[k])] = {
		order = 10 + filters_order[k],
		width = "full",
		type = "toggle",
		name = k,
		desc = L["Toggle to filter this error."],
		get = function()
			return profileDB.filters[k]
		end,
		set = function(key, value)
			profileDB.filters[k] = value
		end,
		disabled = function()
			return not (profileDB.mode == FILTER_ONLY)
		end,
	}
end

-- generate allows submenu
for k, v in pairs(DATABASE_DEFAULTS.profile.allows) do
	addon.options.args.allows.args[string.format("allow"..allows_order[k])] = {
		order = 10 + allows_order[k],
		width = "full",
		type = "toggle",
		name = k,
		desc = L["Toggle to allow this error."],
		get = function()
			return profileDB.allows[k]
		end,
		set = function(key, value)
			profileDB.allows[k] = value
		end,
		disabled = function()
			return not (profileDB.mode == ALLOW_ONLY)
		end,
	}
end

function addon:SetupOptions()
	-- LibSink options
	addon.options.args.output = self:GetSinkAce3OptionsDataTable()

	-- profile options
	addon.options.args.profile = AceDBOptions:GetOptionsTable(self.db)
	addon.options.args.profile.order = -2

	AceConfig:RegisterOptionsTable(MODNAME, addon.options, nil)

	self.optionsFrames = {}
	self.optionsFrames.general = AceConfigDialog:AddToBlizOptions(MODNAME, nil, nil, "general")
	self.optionsFrames.filters = AceConfigDialog:AddToBlizOptions(MODNAME, L["Filter only ..."], MODNAME, "filters")
	self.optionsFrames.allows = AceConfigDialog:AddToBlizOptions(MODNAME, L["Allow only ..."], MODNAME, "allows")
	self.optionsFrames.output = AceConfigDialog:AddToBlizOptions(MODNAME, L["Output"], MODNAME, "output")
	self.optionsFrames.profile = AceConfigDialog:AddToBlizOptions(MODNAME, L["Profiles"], MODNAME, "profile")
end

--------------------------------------------------------------------------------------------------------
--                                            ErrorFilter Init                                        --
--------------------------------------------------------------------------------------------------------
function addon:OnInitialize()
	self.db = AceDB:New("ErrorFilterDB", DATABASE_DEFAULTS, true)
	if not self.db then
		Print("Error: Database not loaded correctly. Please exit out of WoW and delete ErrorFilter.lua found in: \\World of Warcraft\\WTF\\Account\\<Account Name>>\\SavedVariables\\")
	end

	-- LibSink options storage
	self:SetSinkStorage(self.db.profile)

	self.db.RegisterCallback(self, "OnProfileChanged", "OnProfileChanged")
	self.db.RegisterCallback(self, "OnProfileCopied", "OnProfileChanged")
	self.db.RegisterCallback(self, "OnProfileReset", "OnProfileChanged")

	profileDB = self.db.profile
	self:SetupOptions()

	-- Create slash commands
	SLASH_ErrorFilter1 = "/erf"
	SLASH_ErrorFilter2 = "/errorfilter"
	SlashCmdList["ErrorFilter"] = addon.ShowConfig

	-- Register events
	self:UpdateEvents()

	self:RegisterEvent("PLAYER_REGEN_ENABLED","OnRegenEnable")
	self:RegisterEvent("PLAYER_REGEN_DISABLED","OnRegenDisable")
end

--------------------------------------------------------------------------------------------------------
--                                       ErrorFilter event handlers                                   --
--------------------------------------------------------------------------------------------------------
function addon:OnErrorMessage(self, event, msg)
	if profileDB.throttle then
		if lastDisplay[msg] and (lastDisplay[msg] + 5 > GetTime()) then return end
		lastDisplay[msg] = GetTime()
	end
	if ((state == OUT_OF_COMBAT) and profileDB.updateOnlyInCombat) or (profileDB.mode == THROTTLE) then
		--UIErrorsFrame:AddMessage(msg, 1.0, 0.1, 0.1, 1.0);
		addon:Pour(msg, 1.0, 0.1, 0.1)
		return
	end
	if profileDB.mode == FILTER_ONLY then
		-- check default filters
		if profileDB.filters[msg] then
			return
		end
		-- check custom filters
		for k, v in next, profileDB.custom_filters do
			if string.find(string.lower(msg), v) then
				return
			end
		end
		--UIErrorsFrame:AddMessage(msg, 1.0, 0.1, 0.1, 1.0);
		addon:Pour(msg, 1.0, 0.1, 0.1)
	elseif profileDB.mode == ALLOW_ONLY then
		-- check default allows
		if profileDB.allows[msg] then
			--UIErrorsFrame:AddMessage(msg, 1.0, 0.1, 0.1, 1.0);
			addon:Pour(msg, 1.0, 0.1, 0.1)
			return
		end
		-- check custom allows
		for k, v in next, profileDB.custom_allows do
			if string.find(string.lower(msg), v) then
				--UIErrorsFrame:AddMessage(msg, 1.0, 0.1, 0.1, 1.0);
				addon:Pour(msg, 1.0, 0.1, 0.1)
				return
			end
		end
	end
end

function addon:OnRegenEnable()
	state = OUT_OF_COMBAT
	if (profileDB.mode == FILTER_ALL) and profileDB.updateOnlyInCombat then
		UIErrorsFrame:RegisterEvent("UI_ERROR_MESSAGE")
	end
end

function addon:OnRegenDisable()
	state = IN_COMBAT
	if (profileDB.mode == FILTER_ALL) and profileDB.updateOnlyInCombat then
		UIErrorsFrame:UnregisterEvent("UI_ERROR_MESSAGE")
	end
end

--------------------------------------------------------------------------------------------------------
--                                        ErrorFilter functions                                       --
--------------------------------------------------------------------------------------------------------
-- Called after profile changed
function addon:OnProfileChanged(event, database, newProfileKey)
	profileDB = database.profile
end

-- Open config window
function addon:ShowConfig()
	InterfaceOptionsFrame_OpenToCategory(addon.optionsFrames.profile)
	InterfaceOptionsFrame_OpenToCategory(addon.optionsFrames.general)
end

-- Check options and set events
function addon:UpdateEvents(close)
	if close then
		UIErrorsFrame:RegisterEvent("UI_ERROR_MESSAGE")
		self:UnregisterEvent("UI_ERROR_MESSAGE")
	else
		if profileDB.mode == REMOVE_FRAME then
			UIErrorsFrame:Hide()
			self:UnregisterEvent("UI_ERROR_MESSAGE")
		else
			UIErrorsFrame:Show()
			if profileDB.mode == FILTER_ALL then
				if profileDB.updateOnlyInCombat then
					if state == IN_COMBAT then
						UIErrorsFrame:UnregisterEvent("UI_ERROR_MESSAGE")
					else
						UIErrorsFrame:RegisterEvent("UI_ERROR_MESSAGE")
					end
				else
					UIErrorsFrame:UnregisterEvent("UI_ERROR_MESSAGE")
				end
				self:UnregisterEvent("UI_ERROR_MESSAGE")
			elseif profileDB.mode == FILTER_ONLY or profileDB.mode == ALLOW_ONLY then
				UIErrorsFrame:UnregisterEvent("UI_ERROR_MESSAGE")
				self:RegisterEvent("UI_ERROR_MESSAGE","OnErrorMessage", self)
			elseif profileDB.mode == DO_NOTHING then
				UIErrorsFrame:RegisterEvent("UI_ERROR_MESSAGE")
				self:UnregisterEvent("UI_ERROR_MESSAGE")
			elseif profileDB.mode == THROTTLE then
				UIErrorsFrame:UnregisterEvent("UI_ERROR_MESSAGE")
				self:RegisterEvent("UI_ERROR_MESSAGE","OnErrorMessage", self)
			end
		end
	end
end

function ErrorFilter_Toggle(switch)
	if switch then
		addon:UpdateEvents()
	else
		addon:UpdateEvents(1)
	end
end
