--## Version: 0.32.29 ## Author: Ermad
if GetLocale() == "zhCN" then
  AngryKeystonesLocal = "|cff8000ff[战斗]|r秘境助手";
elseif GetLocale() == "zhTW" then
  AngryKeystonesLocal = "|cff8000ff[战斗]|r秘境助手";
else
  AngryKeystonesLocal = "Angry Keystones";
end
-- Core v1.4
local AngryKeystones = {}

local Listener = CreateFrame('Frame', 'AngryKeystonesListener')
local EventListeners = {}
local function Addon_OnEvent(frame, event, ...)
	if EventListeners[event] then
		for callback, func in pairs(EventListeners[event]) do
			if func == 0 then
				callback[event](callback, ...)
			else
				callback[func](callback, event, ...)
			end
		end
	end
end
Listener:SetScript('OnEvent', Addon_OnEvent)
function AngryKeystones:RegisterEvent(event, callback, func)
	if func == nil then func = 0 end
	if EventListeners[event] == nil then
		Listener:RegisterEvent(event)
		EventListeners[event] = { [callback]=func }
	else
		EventListeners[event][callback] = func
	end
end

function AngryKeystones:UnregisterEvent(event, callback)
	local listeners = EventListeners[event]
	if listeners then
		local count = 0
		for index,_ in pairs(listeners) do
			if index == callback then
				listeners[index] = nil
			else
				count = count + 1
			end
		end
		if count == 0 then
			EventListeners[event] = nil
			Listener:UnregisterEvent(event)
		end
	end
end

local AddOnListeners = {}
function AngryKeystones:ADDON_LOADED(name)
	if AddOnListeners[name] then
		for callback, func in pairs(AddOnListeners[name]) do
			if func == 0 then
				callback[name](callback)
			else
				callback[func](callback, name)
			end
		end
	end
end

function AngryKeystones:RegisterAddOnLoaded(name, callback, func)
	if func == nil then func = 0 end
	if C_AddOns.IsAddOnLoaded(name) then
		if func == 0 then
			callback[name](callback)
		else
			callback[func](callback, name)
		end
	else
		self:RegisterEvent('ADDON_LOADED', self)
		if AddOnListeners[name] == nil then
			AddOnListeners[name] = { [callback]=func }
		else
			AddOnListeners[name][callback] = func
		end
	end
end

function AngryKeystones:UnregisterAddOnLoaded(name, callback)
	local listeners = AddOnListeners[name]
	if listeners then
		local count = 0
		for index,_ in pairs(listeners) do
			if index == callback then
				listeners[index] = nil
			else
				count = count + 1
			end
		end
		if count == 0 then
			AddOnListeners[name] = nil
		end
	end
end

function AngryKeystones:UnregisterEvent(event, callback)
	local listeners = EventListeners[event]
	if listeners then
		local count = 0
		for index,_ in pairs(listeners) do
			if index == callback then
				listeners[index] = nil
			else
				count = count + 1
			end
		end
		if count == 0 then
			EventListeners[event] = nil
			Listener:UnregisterEvent(event)
		end
	end
end

local CommStarted = false
local CommListeners = {}
function AngryKeystones:RegisterAddOnComm(prefix, callback, func)
	if func == nil then func = 0 end

	if not CommStarted then
		C_ChatInfo.RegisterAddonMessagePrefix(AngryKeystones.ShortName)
		self:RegisterEvent('CHAT_MSG_ADDON', self)
		CommStarted = true
	end
	if CommListeners[prefix] == nil then
		CommListeners[prefix] = { [callback]=func }
	else
		CommListeners[prefix][callback] = func
	end
end

function AngryKeystones:UnregisterAddOnComm(prefix, callback)
	local listeners = CommListeners[prefix]
	if listeners then
		local count = 0
		for index,_ in pairs(listeners) do
			if index == callback then
				listeners[index] = nil
			else
				count = count + 1
			end
		end
		if count == 0 then
			CommListeners[prefix] = nil
		end
	end
end

function AngryKeystones:SendAddOnComm(prefix, message, type, target)
	if (type == "PARTY" and not IsInGroup(LE_PARTY_CATEGORY_HOME))
			or (type == "INSTANCE" and not IsInGroup(LE_PARTY_CATEGORY_INSTANCE)) then
		return false
	end
	local data = prefix.."|"..message
	C_ChatInfo.SendAddonMessage(AngryKeystones.ShortName, data, type, target)
	return true
end

function AngryKeystones:CHAT_MSG_ADDON(prefix, message, type, sender)
	if prefix == AngryKeystones.ShortName then
		for prefix,listeners in pairs(CommListeners) do
			local matcher = prefix.."|"
			if string.sub(message, 1, #matcher) == matcher then
				local data = string.sub(message, #matcher + 1)
				for callback,func in pairs(listeners) do
					if func == 0 then
						callback.ReceiveAddOnComm(callback, data, type, sender)
					else
						callback[func](callback, data, type, sender)
					end
				end
			end
		end
	end
end

local ModulePrototype = {}
function ModulePrototype:RegisterEvent(event, func)
	AngryKeystones:RegisterEvent(event, self, func)
end
function ModulePrototype:UnregisterEvent(event)
	AngryKeystones:UnregisterEvent(event, self)
end
function ModulePrototype:RegisterAddOnComm(func)
	AngryKeystones:RegisterAddOnComm(self.Name, self, func)
end
function ModulePrototype:UnregisterAddOnComm()
	AngryKeystones:UnregisterAddOnComm(self.Name, self)
end
function ModulePrototype:SendAddOnComm(message, type, target)
	AngryKeystones:SendAddOnComm(self.Name, message, type, target)
end
function ModulePrototype:RegisterAddOnLoaded(name, func)
	AngryKeystones:RegisterAddOnLoaded(name, self, func)
end
function ModulePrototype:UnregisterAddOnLoaded(name)
	AngryKeystones:UnregisterAddOnLoaded(name, self)
end
AngryKeystones.ModulePrototype = ModulePrototype

AngryKeystones.Modules = {}
function AngryKeystones:NewModule(name)
	local object = {}
	self.Modules[name] = object
	table.insert(self.Modules, object)
	setmetatable(object, {__index=ModulePrototype})
	object.Name = name
	return object
end
setmetatable(AngryKeystones, {__index = AngryKeystones.Modules})

function AngryKeystones:ForAllModules(event, ...)
	for _, module in ipairs(AngryKeystones.Modules) do
		if type(module) == 'table' and module[event] then
			module[event](module, ...)
		end
	end
end

AngryKeystones:RegisterEvent('PLAYER_LOGIN', AngryKeystones)
function AngryKeystones:PLAYER_LOGIN()
	self:ForAllModules('BeforeStartup')
	self:ForAllModules('Startup')
	self:ForAllModules('AfterStartup')

	self:UnregisterEvent('PLAYER_LOGIN', self)
end

AngryKeystones.Name = "AngryKeystones"  --AngryKeystonesLocal  --C_AddOns.GetAddOnMetadata(ADDON, "Title")
AngryKeystones.ShortName = AngryKeystonesLocal  --C_AddOns.GetAddOnMetadata(ADDON, "X-Short-Name") or string.sub(ADDON, 1, 16)
AngryKeystones.Version = "0.32.29"
--_G[ADDON] = AngryKeystones


local Locale = AngryKeystones:NewModule('Locale')

local default_locale = "enUS"
local current_locale = GetLocale()

local langs = {}
langs.enUS = {
	config_characterConfig = "Per-character configuration",
	config_progressTooltip = "Show progress each enemy gives on their tooltip",
	config_progressTooltipMDT = "Use Mythic Dungeon Tools' data for the tooltip",
	config_progressFormat = "Enemy Forces Format",
	config_progressFormat_1 = "24.19%",
	config_progressFormat_2 = "90/372",
	config_progressFormat_3 = "24.19% - 90/372",
	config_progressFormat_4 = "24.19% (75.81%)",
	config_progressFormat_5 = "90/372 (282)",
	config_progressFormat_6 = "24.19% (75.81%) - 90/372 (282)",
	config_splitsFormat = "Objective Splits Display",
	config_splitsFormat_1 = "Disabled",
	config_splitsFormat_2 = "Time from start",
	config_splitsFormat_3 = "Relative to previous",
	config_autoGossip = "Automatically select gossip entries during Mythic Keystone dungeons (ex: Odyn)",
	config_cosRumors = "Output to party chat clues from \"Chatty Rumormonger\" during Court of Stars",
	config_silverGoldTimer = "Show timer for both 2 and 3 bonus chests at same time",
	config_completionMessage = "Show message with final times on completion of a Mythic Keystone dungeon",
	config_showSplits = "Show split time for each objective in objective tracker",
	keystoneFormat = "[Keystone: %s - Level %d]",
	completion0 = "Timer expired for %s with %s, you were %s over the time limit.",
	completion1 = "Beat the timer for %s in %s. You were %s ahead of the timer, and missed +2 by %s.",
	completion2 = "Beat the timer for +2 %s in %s. You were %s ahead of the +2 timer, and missed +3 by %s.",
	completion3 = "Beat the timer for +3 %s in %s. You were %s ahead of the +3 timer.",
	completionSplits = "Split timings were: %s.",
	timeLost = "Time Lost",
	config_smallAffixes = "Reduce the size of affix icons on timer frame",
	config_deathTracker = "Show death tracker on timer frame",
	scheduleTitle = "Schedule",
	scheduleWeek1 = "This week",
	scheduleWeek2 = "Next week",
	scheduleWeek3 = "In two weeks",
	scheduleWeek4 = "In three weeks",
	scheduleMissingKeystone = "Requires a level 7+ Mythic Keystone in your inventory to display.",
	scheduleUnknown = "The updated affix schedule is currently unknown.",
	config_hideTalkingHead = "Hide Talking Head dialog during a Mythic Keystone dungeon",
	config_resetPopup = "Show popup to reset instances upon leaving a completed Mythic Keystone dungeon",
	partyKeysTitle = "Party Keystones",
	newKeystoneAnnounce = "New Keystone: %s",
	currentKeystoneText = "Current: |cFFFFFFFF%s|r",
	config_announceKeystones = "Announce newly acquired Mythic Keystones to your party",
	config_schedule = "Display keystone schedule and party keystones (requires reload UI)",
	["dungeon_Shadowmoon Burial Grounds"] = "Shadowmoon",
	["dungeon_Temple of the Jade Serpent"] = "Jade Serpent",
	timeLimit = "Time Limit %s",
}

langs.zhCN = {
	["completion0"] = "你超时完成了 %s 的战斗。共耗时 %s，超出规定时间 %s。",
	["completion1"] = "你在规定时间内完成了 %s 的战斗！共耗时 %s，剩余时间 %s，+2奖励超时 %s。",
	["completion2"] = "你在规定时间内获得了 %s 的+2奖励！共耗时 %s，+2奖励剩余时间 %s，+3奖励超时 %s。",
	["completion3"] = "你在规定时间内获得了 %s 的+3奖励！共耗时 %s，+3奖励剩余时间 %s。",
	["completionSplits"] = "分段的时间为：%s。",
	["config_announceKeystones"] = "在队伍里通报获得的新钥石",
	["config_autoGossip"] = "在史诗钥石副本中自动对话交互（如奥丁）",
	["config_characterConfig"] = "为角色进行独立的配置",
	["config_completionMessage"] = "副本完成时在聊天窗口显示总耗时",
	["config_cosRumors"] = "群星庭院造谣者线索发送到队伍频道",
	["config_deathTracker"] = "在进度条上显示死亡统计",
	["config_exclusiveTracker"] = "在副本中隐藏成就和任务追踪（重新加载后生效）",
	["config_hideTalkingHead"] = "在史诗钥石副本中隐藏NPC情景对话窗口",
	["config_persistTracker"] = "副本完成后继续显示任务追踪（重新加载后生效）",
	["config_progressFormat"] = "敌方部队进度格式",
	["config_progressFormat_1"] = "24.19%",
	["config_progressFormat_2"] = "90/372",
	["config_progressFormat_3"] = "24.19% - 90/372",
	["config_progressFormat_4"] = "24.19% (75.81%)",
	["config_progressFormat_5"] = "90/372 (282)",
	["config_progressFormat_6"] = "24.19% (75.81%) - 90/372 (282)",
	["config_progressTooltip"] = "鼠标提示中显示每个敌人给予的进度",
	["config_progressTooltipMDT"] = "鼠标提示中使用 Mythic Dungeon Tools' 的数据",
	["config_resetPopup"] = "离开已完成的副本后提示是否重置",
	["config_schedule"] = "显示钥石日程表和队伍钥石（重新加载后生效）",
	["config_showSplits"] = "在任务列表的进度上显示单独的进度计时",
	["config_silverGoldTimer"] = "同时显示+2和+3的计时",
	["config_smallAffixes"] = "缩小进度条上的词缀图标大小",
	["config_splitsFormat"] = "进度阶段显示方式",
	["config_splitsFormat_1"] = "禁用",
	["config_splitsFormat_2"] = "从头计时",
	["config_splitsFormat_3"] = "相对于之前",
	["currentKeystoneText"] = "当前钥石：|cFFFFFFFF%s|r",
	["forcesFormat"] = " - 敌方部队 %s",
	["keystoneFormat"] = "[%s<%d级>]",
	["newKeystoneAnnounce"] = "新钥石：%s",
	["partyKeysTitle"] = "队伍钥石信息",
	["scheduleMissingKeystone"] = "你需要一把7级以上的钥石才可激活此项功能。",
	["scheduleTitle"] = "日程表",
	["scheduleUnknown"] = "更新后的词缀信息目前未知。",
	["scheduleWeek1"] = "本周",
	["scheduleWeek2"] = "下周",
	["scheduleWeek3"] = "两周后",
	["scheduleWeek4"] = "三周后",
	["timeLimit"] = "时限 %s",
	["timeLost"] = "时间损失",
	["dungeon_Shadowmoon Burial Grounds"] = "影月墓地",
	["dungeon_Temple of the Jade Serpent"] = "青龙寺",
}
langs.zhTW = {
	["completion0"] = "你超時完成了 %s 的戰鬥。共耗時 %s，超出規定時間 %s。",
	["completion1"] = "你在規定時間內完成了 %s 的戰鬥！共耗時 %s，剩餘時間 %s，2箱獎勵超時 %s。",
	["completion2"] = "你在規定時間內獲得了 %s 的2箱獎勵！共耗時 %s，2箱獎勵剩餘時間 %s，3箱獎勵超時 %s。",
	["completion3"] = "你在規定時間內獲得了 %s 的3箱獎勵！共耗時 %s，3箱獎勵剩餘時間 %s。",
	["completionSplits"] = "分段的時間為：%s。",	
	["config_announceKeystones"] = "在隊伍里通報獲得的新鑰石",
	["config_autoGossip"] = "在傳奇鑰石副本中自動進行對話互動（如歐丁）",
	["config_characterConfig"] = "為角色進行獨立的配置",
	["config_completionMessage"] = "副本完成時在聊天窗口顯示總耗時",
	["config_cosRumors"] = "衆星之廷造謠者線索發送到隊伍頻道",
	["config_deathTracker"] = "在計時器上顯示死亡統計",
	["config_exclusiveTracker"] = "在副本中隱藏成就和任務追蹤（重裝插件後生效）",
	["config_hideTalkingHead"] = "在傳奇鑰石副本中隱藏NPC情景對話窗口",
	["config_persistTracker"] = "副本完成後繼續顯示任務追蹤（重載插件後生效）",
	["config_progressFormat"] = "敵方部隊進度格式",
	["config_progressFormat_1"] = "24.19%",
	["config_progressFormat_2"] = "90/372",
	["config_progressFormat_3"] = "24.19% - 90/372",
	["config_progressFormat_4"] = "24.19% (75.81%)",
	["config_progressFormat_5"] = "90/372 (282)",
	["config_progressFormat_6"] = "24.19% (75.81%) - 90/372 (282)",
	["config_progressTooltip"] = "滑鼠提示中顯示每個敵人給予的進度",
	["config_progressTooltipMDT"] = "滑鼠提示中使用 Mythic Dungeon Tools' 的數據",
	["config_resetPopup"] = "離開已完成的副本後提示是否重置",
	["config_schedule"] = "顯示鑰石日程表和隊伍鑰石（重載插件後生效）",
	["config_showSplits"] = "在任務列表的进度上顯示單獨的進度計時",
	["config_silverGoldTimer"] = "同時顯示+2和+3的計時",
	["config_smallAffixes"] = "縮小計時器上的詞綴圖標大小",
	["config_splitsFormat"] = "進度階段顯示方式",
	["config_splitsFormat_1"] = "禁用",
	["config_splitsFormat_2"] = "從頭計時",
	["config_splitsFormat_3"] = "與之前關聯",
	["currentKeystoneText"] = "當前鑰石：|cFFFFFFFF%s|r",
	["forcesFormat"] = " - 敵方部隊 %s",
	["keystoneFormat"] = "[%s<%d級>]",
	["newKeystoneAnnounce"] = "新鑰石：%s",
	["partyKeysTitle"] = "隊伍鑰石信息",
	["scheduleMissingKeystone"] = "你需要一把7級以上的鑰石來激活此項功能。",
	["scheduleTitle"] = "日程表",
	["scheduleUnknown"] = "更新後的詞綴信息目前未知。",
	["scheduleWeek1"] = "本周",
	["scheduleWeek2"] = "下周",
	["scheduleWeek3"] = "兩周後",
	["scheduleWeek4"] = "三周後",
	["timeLimit"] = "時限 %s",
	["timeLost"] = "時間損失",
	["dungeon_Shadowmoon Burial Grounds"] = "影月墓地",
	["dungeon_Temple of the Jade Serpent"] = "玉蛟寺",
}

function Locale:Get(key)
	if langs[current_locale] and langs[current_locale][key] ~= nil then
		return langs[current_locale][key]
	else
		return langs[default_locale][key]
	end
end

function Locale:Local(key)
	return langs[current_locale] and langs[current_locale][key]
end

function Locale:Exists(key)
	return langs[default_locale][key] ~= nil
end

setmetatable(Locale, {__index = Locale.Get})

local clues = {}
local rumors = {}

clues.enUS = {
	male = MALE,
	female = FEMALE,
	lightVest = "Light Vest",
	darkVest = "Dark Vest",
	shortSleeves = "Short Sleeves",
	longSleeves = "Long Sleeves",
	cloak = "Cloak",
	noCloak = "No Cloak",
	gloves = "Gloves",
	noGloves = "No Gloves",
	noPotion = "No Potion",
	book = "Book",
	coinpurse = "Coinpurse",
	potion = "Potion",
}
clues.enGB = clues.enUS

rumors.enUS = {
	["I heard somewhere that the spy isn't female."]="male",
	["I heard the spy is here and he's very good looking."]="male",
	["A guest said she saw him entering the manor alongside the Grand Magistrix."]="male",
	["One of the musicians said he would not stop asking questions about the district."]="male",

	["Someone's been saying that our new guest isn't male."]="female",
	["A guest saw both her and Elisande arrive together earlier."]="female",
	["They say that the spy is here and she's quite the sight to behold."]="female",
	["I hear some woman has been constantly asking about the district..."]="female",

	["The spy definitely prefers the style of light colored vests."]="lightVest",
	["I heard that the spy is wearing a lighter vest to tonight's party."]="lightVest",
	["People are saying the spy is not wearing a darker vest tonight."]="lightVest",

	["The spy definitely prefers darker clothing."]="darkVest",
	["I heard the spy's vest is a dark, rich shade this very night."]="darkVest",
	["The spy enjoys darker colored vests... like the night."]="darkVest",
	["Rumor has it the spy is avoiding light colored clothing to try and blend in more."]="darkVest",

	["Someone told me the spy hates wearing long sleeves."]="shortSleeves",
	["I heard the spy wears short sleeves to keep their arms unencumbered."]="shortSleeves",
	["I heard the spy enjoys the cool air and is not wearing long sleeves tonight."]="shortSleeves",
	["A friend of mine said she saw the outfit the spy was wearing. It did not have long sleeves."]="shortSleeves",

	["I heard the spy's outfit has long sleeves tonight."]="longSleeves",
	["A friend of mine mentioned the spy has long sleeves on."]="longSleeves",
	["Someone said the spy is covering up their arms with long sleeves tonight."]="longSleeves",
	["I just barely caught a glimpse of the spy's long sleeves earlier in the evening."]="longSleeves",

	["Someone mentioned the spy came in earlier wearing a cape."]="cloak",
	["I heard the spy enjoys wearing capes."]="cloak",

	["I heard that the spy left their cape in the palace before coming here."]="noCloak",
	["I heard the spy dislikes capes and refuses to wear one."]="noCloak",

	["There's a rumor that the spy always wears gloves."]="gloves",
	["I heard the spy carefully hides their hands."]="gloves",
	["Someone said the spy wears gloves to cover obvious scars."]="gloves",
	["I heard the spy always dons gloves."]="gloves",

	["You know... I found an extra pair of gloves in the back room. The spy is likely to be bare handed somewhere around here."]="noGloves",
	["There's a rumor that the spy never has gloves on."]="noGloves",
	["I heard the spy avoids having gloves on, in case some quick actions are needed."]="noGloves",
	["I heard the spy dislikes wearing gloves."]="noGloves",

	["Rumor has is the spy loves to read and always carries around at least one book."]="book",
	["I heard the spy always has a book of written secrets at the belt."]="book",

	["A musician told me she saw the spy throw away their last potion and no longer has any left."]="noPotion",
	["I heard the spy is not carrying any potions around."]="noPotion",

	["I'm pretty sure the spy has potions at the belt."]="potion",
	["I heard the spy brought along potions, I wonder why?"]="potion",
	["I heard the spy brought along some potions... just in case."]="potion",
	["I didn't tell you this... but the spy is masquerading as an alchemist and carrying potions at the belt."]="potion",

	["I heard the spy's belt pouch is lined with fancy threading."]="coinpurse",
	["A friend said the spy loves gold and a belt pouch filled with it."]="coinpurse",
	["I heard the spy's belt pouch is filled with gold to show off extravagance."]="coinpurse",
	["I heard the spy carries a magical pouch around at all times."]="coinpurse",
}
rumors.enGB = rumors.enUS

clues.zhCN = {
	lightVest = "浅色上衣",
	darkVest = "深色上衣",
	shortSleeves = "短袖",
	longSleeves = "长袖",
	cloak = "有斗篷",
	noCloak = "无斗篷",
	gloves = "有手套",
	noGloves = "无手套",
	noPotion = "无药水",
	book = "有书",
	coinpurse = "有钱袋",
	potion = "有药水",
}

rumors.zhCN = {
	["我在别处听说那个密探不是女性。"]="male",
	["我听说那个密探已经来了，而且他很英俊。"]="male",
	["有个客人说她看见他和大魔导师一起走进了庄园。"]="male",
	["有个乐师说，他一直在打听这一带的消息。"]="male",
	["有人说我们的新客人不是男性。"]="female",
	["他们说那个密探已经来了，而且她是个大美人。"]="female",
	["我听说有个女人一直打听贵族区的情况……"]="female",
	["有个客人先前看到她是和艾利桑德一起到达的。"]="female",
	["那个间谍肯定更喜欢浅色的上衣。"]="lightVest",
	["我听说那个密探穿着一件浅色上衣来参加今晚的聚会。"]="lightVest",
	["大家都在说那个密探今晚没有穿深色的上衣。"]="lightVest",
	["那个间谍肯定更喜欢深色的服装。"]="darkVest",
	["我听说那个密探今晚所穿的外衣是浓密的暗深色。"]="darkVest",
	["那个密探喜欢深色的上衣……就像夜空一样深沉。"]="darkVest",
	["传说那个密探会避免穿浅色的服装，以便更好地混入人群。"]="darkVest",
	["有人告诉我那个密探讨厌长袖的衣服。"]="shortSleeves",
	["我听说密探喜欢穿短袖服装，以免妨碍双臂的活动。"]="shortSleeves",
	["我听说那个密探喜欢清凉的空气，所以今晚没有穿长袖衣服。"]="shortSleeves",
	["我的一个朋友说，她看到了密探穿的衣服，是一件短袖上衣。"]="shortSleeves",
	["我听说那个密探今天穿着长袖外套。"]="longSleeves",
	["我的一个朋友说那个密探穿着长袖衣服。"]="longSleeves",
	["有人说，那个密探今晚穿了一件长袖的衣服。"]="longSleeves",
	["上半夜的时候，我正巧瞥见那个密探穿着长袖衣服。"]="longSleeves",
	["有人提到那个密探之前是穿着斗篷来的。"]="cloak",
	["我听说那个密探喜欢穿斗篷。"]="cloak",
	["我听说那个密探在来这里之前，把斗篷忘在王宫里了。"]="noCloak",
	["我听说那个密探讨厌斗篷，所以没有穿。"]="noCloak",
	["有传言说那个密探总是带着手套。"]="gloves",
	["我听说密探都会小心隐藏自己的双手。"]="gloves",
	["有人说那个密探带着手套，以掩盖手上明显的疤痕。"]="gloves",
	["我听说那个密探总是带着手套。"]="gloves",
	["你知道吗……我在后头的房间里发现了一双多余的手套。那个密探现在可能就赤着双手在这附近转悠呢。"]="noGloves",
	["有传言说那个密探从来不戴手套。"]="noGloves",
	["我听说那个密探会尽量不戴手套，以防在快速行动时受到阻碍。"]="noGloves",
	["我听说那个密探不喜欢戴手套。"]="noGloves",
	["据说那个密探喜欢读书，而且总是随身携带至少一本书。"]="book",
	["我听说那个密探的腰带上，总是挂着一本写满机密的书。"]="book",
	["有个乐师告诉我，她看到那个密探扔掉了身上的最后一瓶药水，已经没有药水了。"]="noPotion",
	["我听说那个密探根本没带任何药水。"]="noPotion",
	["我敢肯定，那个密探的腰带上挂着药水。"]="potion",
	["我听说那个密探随身带着药水，这是为什么呢？"]="potion",
	["可别说是我告诉你的……那个密探伪装成了炼金师，腰带上挂着药水。"]="potion",
	["我听说那个密探买了一些药水……以防万一。"]="potion",
	["我听说那个密探的腰包上绣着精美的丝线。"]="coinpurse",
	["一个朋友说，那个密探喜欢黄金，所以在腰包里装满了金币。"]="coinpurse",
	["我听说那个密探的腰包里装满了摆阔用的金币。"]="coinpurse",
	["我听说那个密探总是带着一个魔法袋。"]="coinpurse",
}

clues.koKR = {
	male = MALE,
	female = FEMALE,
	lightVest = "밝은색 조끼",
	darkVest = "어두운색 조끼",
	shortSleeves = "짧은 소매",
	longSleeves = "긴 소매",
	cloak = "망토 있음",
	noCloak = "망토 없음",
	gloves = "장갑 있음",
	noGloves = "장갑 없음",
	noPotion = "물약 없음",
	book = "책",
	coinpurse = "금화 주머니",
	potion = "물약",
}

rumors.koKR = {
	["첩자가 여성이 아니라는 얘기를 들었습니다."]="male",
	["첩자가 나타났다고 합니다. 그 남자는 대단히 호감형이라고도 하더군요."]="male",
	["한 남자가 대마법학자와 나란히 저택에 들어오는 걸 봤다는 얘기가 있더군요."]="male",
	["한 연주자가 말하길, 그 남자가 끊임없이 그 지구에 관한 질문을 늘어놨다고 합니다."]="male",
	["그 불청객은 남자가 아니라는 말을 들었습니다."]="female",
	["아까 한 방문객이 그녀와 엘리산드가 함께 도착하는 걸 보았답니다."]="female",
	["첩자가 나타났다고 합니다. 그 여자는 아주 미인이라고도 하더군요."]="female",
	["어떤 여자가 귀족 지구에 관해 계속 묻고 다닌다고 하던데..."]="female",
	["그자는 첩자인데도 밝은색 조끼를 즐겨 입는다고 합니다."]="lightVest",
	["오늘 밤 파티에 그 첩자는 밝은색 조끼를 입고 올 거라는 말을 들었습니다."]="lightVest",
	["사람들이 그러는데, 오늘 밤 그 첩자는 어두운 색 조끼를 입지 않았다고 합니다."]="lightVest",
	["그 첩자는 분명 어두운 옷을 선호합니다."]="darkVest",
	["오늘 밤 그 첩자는 어둡고 짙은 색의 조끼를 입었다고 합니다."]="darkVest",
	["그 첩자는 어두운 색 조끼를 즐겨 입어요... 밤과 같은 색이죠."]="darkVest",
	["소문에 그 첩자는 눈에 띄지 않으려고 밝은색 옷은 피한다더군요."]="darkVest",
	["그 첩자는 소매가 긴 옷을 입는 걸 정말 싫어한다고 합니다."]="shortSleeves",
	["그 첩자는 팔을 빠르게 움직이려고 짧은 소매 옷만 고집한다고 합니다."]="shortSleeves",
	["그 첩자는 시원한 걸 좋아해서 오늘 밤 짧은 소매를 입고 왔다고 들었습니다."]="shortSleeves",
	["제 친구가 그 첩자가 입은 옷을 봤는데, 긴 소매는 아니었다는군요!"]="shortSleeves",
	["오늘 밤 첩자는 긴 소매 옷을 입었다고 하더군요."]="longSleeves",
	["제 친구 말로는, 첩자가 긴 소매 옷을 입었다고 합니다."]="longSleeves",
	["오늘 밤 그 첩자는 소매가 긴 옷을 입었다고 들었어요."]="longSleeves",
	["초저녁에 첩자를 언뜻 보았는데... 긴 소매 옷을 입었던 것 같습니다."]="longSleeves",
	["그 첩자가 망토를 걸친 모습을 봤다는 사람이 있었습니다."]="cloak",
	["그 첩자는 망토를 즐겨 입는다고 들었습니다."]="cloak",
	["제가 듣기로는 그 첩자가 궁전에 망토를 벗어두고 여기 왔다고 합니다."]="noCloak",
	["그 첩자는 망토를 싫어해서 절대로 입지 않는다고 합니다."]="noCloak",
	["그 첩자는 항상 장갑을 낀다고 하더군요."]="gloves",
	["제가 듣기로는, 그 첩자는 항상 신경 써서 손을 가린다고 합니다."]="gloves",
	["그 첩자는 손에 있는 선명한 흉터를 가리려고 장갑을 낀다고 합니다."]="gloves",
	["그 첩자는 항상 장갑을 낀다고 들었습니다."]="gloves",
	["안쪽 방에서 장갑 한 켤레를 발견했습니다. 첩자는 분명히 이 주변에 장갑을 끼지 않은 사람중 하나일 거에요."]="noGloves",
	["그 첩자는 장갑을 끼는 일이 없다고 하더군요."]="noGloves",
	["그 첩자는 장갑을 끼지 않는답니다. 위급한 순간에 걸리적거려서 그렇겠지요."]="noGloves",
	["그 첩자는 장갑을 끼는 걸 싫어한다고 들었습니다."]="noGloves",
	["소문을 들어 보니, 그 첩자는 독서를 좋아해서 항상 책을 가지고 다닌다고 합니다."]="book",
	["그 첩자의 허리띠 주머니에는 비밀이 잔뜩 적힌 책이 담겨 있다고 합니다."]="book",
	["한 연주자가 그 첩자가 마지막 물약을 버리는 걸 봤다고 합니다. 그러니 더는 물약이 없겠죠."]="noPotion",
	["그 첩자는 물약을 가지고 다니지 않는다고 합니다."]="noPotion",
	["그 첩자는 허리띠에 물약을 매달고 있을 게 분명합니다. 있는 게 분명해요."]="potion",
	["그 첩자는 물약을 가지고 다닌데요. 이유가 뭘까요?"]="potion",
	["그 첩자는 만약을 대비해... 물약 몇 개를 가져왔다고 합니다."]="potion",
	["이 얘기를 깜빡할 뻔했네요... 그 첩자는 연금술사로 가장해 허리띠에 물약을 달고 다닌다고 합니다."]="potion",
	["그 첩자는 허리띠 주머니도 휘황찬란한 자수로 꾸며져 있다고 합니다."]="coinpurse",
	["제 친구가 말하길, 그 첩자는 금을 너무 좋아해서 허리띠 주머니에도 금이 가득 들어 있다고 합니다."]="coinpurse",
	["그 첩자는 어찌나 사치스러운지 허리띠에 달린 주머니에 금화를 잔뜩 넣어서 다닌다고 합니다."]="coinpurse",
	["그 첩자는 마법의 주머니를 항상 가지고 다닌다고 들었습니다."]="coinpurse",
}

clues.deDE = {
	male = MALE,
	female = FEMALE,
	lightVest = "Helle Weste",
	darkVest = "Dunkle Weste",
	shortSleeves = "Kurze Ärmel",
	longSleeves = "Lange Ärmel",
	cloak = "Umhang",
	noCloak = "Kein Umhang",
	gloves = "Handschuhe",
	noGloves = "Keine Handschuhe",
	noPotion = "Kein Fläschchen",
	book = "Buch",
	coinpurse = "Geldbeutel",
	potion = "Fläschchen",
}

rumors.deDE = {
	["Irgendwo habe ich gehört, dass der Spion nicht weiblich ist."]="male",
	["Ich hörte, dass der Spion ein äußerst gutaussehender Herr ist."]="male",
	["Ein Gast sagte, sie sah, wie ein Herr an der Seite der Großmagistrix das Anwesen betreten hat."]="male",
	["Einer der Musiker sagte, er stellte unablässig Fragen über den Bezirk."]="male",

	["Jemand hat behauptet, dass unser neuester Gast nicht männlich ist."]="female",
	["Ein Gast hat beobachtet, wie sie und Elisande vorhin gemeinsam eingetroffen sind."]="female",
	["Man sagt, die Spionin wäre hier und sie wäre eine wahre Augenweide."]="female",
	["Wie ich höre, hat eine Frau sich ständig nach diesem Bezirk erkundigt..."]="female",

	["Der Spion bevorzugt auf jeden Fall Westen mit hellen Farben."]="lightVest",
	["Wie ich hörte, trägt der Spion auf der Party heute Abend eine helle Weste."]="lightVest",
	["Die Leute sagen, dass der Spion heute Abend keine dunkle Weste trägt."]="lightVest",

	["Der Spion bevorzugt auf alle Fälle dunkle Kleidung."]="darkVest",
	["Ich hörte, dass die Weste des Spions heute Abend von dunkler, kräftiger Farbe ist."]="darkVest",
	["Dem Spion gefallen Westen mit dunklen Farben... dunkel wie die Nacht."]="darkVest",
	["Gerüchten zufolge vermeidet der Spion es, helle Kleidung zu tragen, damit er nicht so auffällt."]="darkVest",

	["Jemand sagte mir, dass der Spion lange Ärmel hasst."]="shortSleeves",
	["Mir ist zu Ohren gekommen, dass der Spion kurze Ärmel trägt, damit er seine Arme ungehindert bewegen kann."]="shortSleeves",
	["Man hat mir zugetragen, dass der Spion die kühle Luft mag und deshalb heute Abend keine langen Ärmel trägt."]="shortSleeves",
	["Eine meiner Freundinnen sagte, dass sie die Kleidung des Spions gesehen hat. Er trägt keine langen Ärmel."]="shortSleeves",

	["Wie ich hörte, trägt der Spion heute Abend Kleidung mit langen Ärmeln."]="longSleeves",
	["Einer meiner Freunde erwähnte, dass der Spion lange Ärmel trägt."]="longSleeves",
	["Jemand sagte, dass der Spion heute Abend seine Arme mit langen Ärmeln bedeckt."]="longSleeves",
	["Ich habe am frühen Abend einen kurzen Blick auf die langen Ärmel des Spions erhascht."]="longSleeves",

	["Jemand erwähnte, dass der Spion vorhin hier hereinkam und einen Umhang trug."]="cloak",
	["Mir ist zu Ohren gekommen, dass der Spion gerne Umhänge trägt."]="cloak",

	["Ich hörte, dass der Spion seinen Umhang im Palast gelassen hat, bevor er hierhergekommen ist."]="noCloak",
	["Ich hörte, dass der Spion keine Umhänge mag und sich weigert, einen zu tragen."]="noCloak",

	["Einem Gerücht zufolge trägt der Spion immer Handschuhe."]="gloves",
	["Wie ich hörte, verbirgt der Spion sorgfältig die Hände."]="gloves",
	["Jemand behauptete, dass der Spion Handschuhe trägt, um sichtbare Narben zu verbergen."]="gloves",
	["Ich hörte, dass der Spion immer Handschuhe anlegt."]="gloves",

	["Wisst Ihr... Ich habe ein zusätzliches Paar Handschuhe im Hinterzimmer gefunden. Wahrscheinlich ist der Spion hier irgendwo mit bloßen Händen unterwegs."]="noGloves",
	["Es gibt Gerüchte, dass der Spion niemals Handschuhe trägt."]="noGloves",
	["Ich hörte, dass der Spion es vermeidet, Handschuhe zu tragen, falls er schnell handeln muss."]="noGloves",
	["Mir ist zu Ohren gekommen, dass der Spion ungern Handschuhe trägt."]="noGloves",

	["Gerüchten zufolge liest der Spion gerne und trägt immer mindestens ein Buch bei sich."]="book",
	["Ich hörte, dass der Spion immer ein Buch mit niedergeschriebenen Geheimnissen am Gürtel trägt."]="book",

	["Eine Musikerin erzählte mir, dass sie gesehen hat, wie der Spion seinen letzten Trank wegwarf und jetzt keinen mehr übrig hat."]="noPotion",
	["Wie ich hörte, hat der Spion keine Tränke bei sich."]="noPotion",

	["Ich bin mir ziemlich sicher, dass der Spion Tränke am Gürtel trägt."]="potion",
	["Ich hörte, dass der Spion Tränke mitgebracht hat... Ich frage mich wieso?"]="potion",
	["Wie ich hörte, hat der Spion einige Tränke mitgebracht... für alle Fälle."]="potion",
	["Von mir habt Ihr das nicht... aber der Spion verkleidet sich als Alchemist und trägt Tränke an seinem Gürtel."]="potion",

	["Ich hörte, dass der Gürtelbeutel des Spions mit ausgefallenem Garn gesäumt wurde."]="coinpurse",
	["Ein Freund behauptet, dass der Spion Gold liebt und einen Gürtelbeutel voll davon hat."]="coinpurse",
	["Mir ist zu Ohren gekommen, dass der Gürtelbeutel des Spions mit Gold gefüllt ist, um besonders extravagant zu erscheinen."]="coinpurse",
	["Ich hörte, dass der Spion immer einen magischen Beutel mit sich herumträgt."]="coinpurse",
}

function Locale:HasRumors()
	return rumors[current_locale] ~= nil and clues[current_locale] ~= nil
end

function Locale:Rumor(gossip)
	if rumors[current_locale] and rumors[current_locale][gossip] then
		return clues[current_locale] and clues[current_locale][rumors[current_locale][gossip]]
	end
end


local Config = AngryKeystones:NewModule('Config')

local configVersion = 1
local configDefaults = {
	progressTooltip = false,
	progressTooltipMDT = false,
	progressFormat = 3,
	autoGossip = false,
	cosRumors = true,
	silverGoldTimer = true,
	splitsFormat = 1,
	completionMessage = true,
	smallAffixes = true,
	deathTracker = true,
	recordSplits = false,
	showLevelModifier = false,
	hideTalkingHead = false,
	resetPopup = true,
	announceKeystones = true,
	schedule = true,
}
local callbacks = {}

local progressFormatValues = { 1, 2, 3, 4, 5, 6 }
local splitsFormatValues = { 0, 1, 2 }

setmetatable(Config, {
	__index = function(self, key)
		if configDefaults[key] ~= nil then
			return self:Get(key)
		else
			return AngryKeystones.ModulePrototype[key]
		end
	end,
	-- __newindex = function(self, key, value)
	-- 	if configDefaults[key] ~= nil then
	-- 		self:Set(key, value)
	-- 	else
	-- 		self[key] = value
	-- 	end
	-- end,
})

function Config:Get(key)
	if self:CharacterConfig() then
		if AngryKeystones_CharacterConfig == nil or AngryKeystones_CharacterConfig[key] == nil then
			return configDefaults[key]
		else
			return AngryKeystones_CharacterConfig[key]
		end
	else
		if AngryKeystones_Config == nil or AngryKeystones_Config[key] == nil then
			return configDefaults[key]
		else
			return AngryKeystones_Config[key]
		end
	end
end

function Config:Set(key, newValue, silent)
	if self:CharacterConfig() then
		if configDefaults[key] == newValue then
			AngryKeystones_CharacterConfig[key] = nil
		else
			AngryKeystones_CharacterConfig[key] = newValue
		end
	else
		if configDefaults[key] == newValue then
			AngryKeystones_Config[key] = nil
		else
			AngryKeystones_Config[key] = newValue
		end
	end
	if callbacks[key] and not silent then
		for _, func in ipairs(callbacks[key]) do
			func(key, newValue)
		end
	end
end

function Config:RegisterCallback(key, func)
	if type(key) == "table" then
		for _, key2 in ipairs(key) do
			if callbacks[key2] then
				table.insert(callbacks, func)
			else
				callbacks[key2] = { func }
			end
		end
	else
		if callbacks[key] then
			table.insert(callbacks, func)
		else
			callbacks[key] = { func }
		end
	end
end

function Config:UnregisterCallback(key, func)
	if callbacks[key] then
		local table = callbacks[key]
		for i=1, #table do
			if table[i] == func then
				table.remove(table, 1)
				i = i - 1
			end
		end
		if #table == 0 then callbacks[key] = nil end
	end
end

function Config:CharacterConfig()
	return AngryKeystones_CharacterConfig and AngryKeystones_CharacterConfig['__enabled']
end

function Config:SetCharacterConfig(enabled)
	AngryKeystones_CharacterConfig['__enabled'] = enabled
	if not AngryKeystones_CharacterConfig['__init'] then
		AngryKeystones_CharacterConfig['__init'] = true
		for key,value in pairs(AngryKeystones_Config) do
			AngryKeystones_CharacterConfig[key] = value
		end
	end
end

local panelOriginalConfig = {}
local optionPanel

local Panel_OnRefresh

local function Panel_OnSave(self)
	wipe(panelOriginalConfig)
end

local function Panel_OnCancel(self)
	-- for key, value in pairs(panelOriginalConfig) do
	-- 	if key == "disabledFilters" then AngryKeystones_Config["selectedFilters"] = nil end
	-- 	Config:Set(key, value)
	-- end
	wipe(panelOriginalConfig)
end

local function Panel_OnDefaults(self)
	AngryKeystones_Config = { __version = configVersion }
	for key,callbacks_key in pairs(callbacks) do
		for _, func in ipairs(callbacks_key) do
			func(key, configDefaults[key])
		end
	end
	wipe(panelOriginalConfig)
end

local function CheckBox_Update(self)
	self:SetChecked( Config:Get(self.configKey) )
end

local function CheckBox_OnClick(self)
	local key = self.configKey
	if panelOriginalConfig[key] == nil then
		panelOriginalConfig[key] = Config[key]
	end
	Config:Set(key, self:GetChecked())
end

local function CharConfigCheckBox_OnClick(self)
	local status = Config:CharacterConfig()
	Config:SetCharacterConfig( not status )

	for key,callbacks_key in pairs(callbacks) do
		for _, func in ipairs(callbacks_key) do
			func(key, Config:Get(key))
		end
	end
	Panel_OnRefresh(optionPanel)
end

local function DropDown_OnClick(self, dropdown)
	local key = dropdown.configKey
	if panelOriginalConfig[key] == nil then
		panelOriginalConfig[key] = Config[key]
	end
	Config:Set(key, self.value)
	UIDropDownMenu_SetSelectedValue( dropdown, self.value )
end

local function DropDown_Initialize(self)
	local key = self.configKey
	local selectedValue = UIDropDownMenu_GetSelectedValue(self)
	local info = UIDropDownMenu_CreateInfo()
	info.func = DropDown_OnClick
	info.arg1 = self

	if key == 'progressFormat' then
		for i, value in ipairs(progressFormatValues) do
			info.text = AngryKeystones.Locale['config_progressFormat_'..i]
			info.value = value
			if ( selectedValue == info.value ) then
				info.checked = 1
			else
				info.checked = nil
			end
			UIDropDownMenu_AddButton(info)
		end
	elseif key == 'splitsFormat' then
		for i, value in ipairs(splitsFormatValues) do
			info.text = AngryKeystones.Locale['config_splitsFormat_'..i]
			info.value = value
			if ( selectedValue == info.value ) then
				info.checked = 1
			else
				info.checked = nil
			end
			UIDropDownMenu_AddButton(info)
		end
	end
end

local DropDown_Index = 0
local function DropDown_Create(self)
	DropDown_Index = DropDown_Index + 1
	local dropdown = CreateFrame("Frame", "AngryKeystonesConfigDropDown"..DropDown_Index, self, "UIDropDownMenuTemplate")
	_G["AngryKeystonesConfigDropDown"..DropDown_Index.."Middle"]:SetWidth(200)
	
	local label = dropdown:CreateFontString("AngryKeystonesConfigDropLabel"..DropDown_Index, "BACKGROUND", "GameFontNormal")
	label:SetPoint("BOTTOMLEFT", dropdown, "TOPLEFT", 16, 3)
	dropdown.Label = label
	
	return dropdown
end

local panelInit, checkboxes, dropdowns, charConfigCheckbox
Panel_OnRefresh = function(self)
	if not panelInit then
		local footer = self:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
		footer:SetPoint('BOTTOMRIGHT', -16, 16)
		footer:SetText( AngryKeystones.Version or "Dev" )

		charConfigCheckbox = CreateFrame("CheckButton", nil, self, "InterfaceOptionsCheckButtonTemplate")
		charConfigCheckbox:SetScript("OnClick", CharConfigCheckBox_OnClick)
		charConfigCheckbox.Text:SetFontObject("GameFontHighlightSmall")
		charConfigCheckbox.Text:SetPoint("LEFT", charConfigCheckbox, "RIGHT", 0, 1)
		charConfigCheckbox.Text:SetText( AngryKeystones.Locale.config_characterConfig )
		charConfigCheckbox:SetPoint("BOTTOMLEFT", 14, 12)

		local label = self:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
		label:SetPoint("TOPLEFT", 16, -16)
		label:SetJustifyH("LEFT")
		label:SetJustifyV("TOP")
		label:SetText( AngryKeystonesLocal )

		checkboxes = {}
		dropdowns = {}

		local checkboxes_order = { "silverGoldTimer", "autoGossip", "progressTooltip", "progressTooltipMDT", "completionMessage", "hideTalkingHead", "resetPopup", "announceKeystones", "schedule" }
		if AngryKeystones.Locale:HasRumors() then table.insert(checkboxes_order, 5, "cosRumors") end

		for i,key in ipairs(checkboxes_order) do
			checkboxes[i] = CreateFrame("CheckButton", nil, self, "InterfaceOptionsCheckButtonTemplate")
			checkboxes[i]:SetScript("OnClick", CheckBox_OnClick)
			checkboxes[i].configKey = key
			checkboxes[i].Text:SetText( AngryKeystones.Locale['config_'..key] )
			if i == 1 then
				checkboxes[i]:SetPoint("TOPLEFT", label, "BOTTOMLEFT", -2, -8)
			else
				checkboxes[i]:SetPoint("TOPLEFT", checkboxes[i-1], "BOTTOMLEFT", 0, -8)
			end
		end

		local dropdowns_order = { "progressFormat", "splitsFormat" }

		for i,key in ipairs(dropdowns_order) do
			dropdowns[i] = DropDown_Create(self)
			dropdowns[i].Label:SetText( AngryKeystones.Locale['config_'..key] )
			dropdowns[i].configKey = key		
			if i == 1 then
				dropdowns[i]:SetPoint("TOPLEFT", checkboxes[#checkboxes], "BOTTOMLEFT", -13, -24)
			else
				dropdowns[i]:SetPoint("TOPLEFT", dropdowns[i-1], "BOTTOMLEFT", 0, -24)
			end
		end

		panelInit = true
	end
	
	charConfigCheckbox:SetChecked( Config:CharacterConfig() )
	
	for _, check in ipairs(checkboxes) do
		CheckBox_Update(check)
	end

	for _, dropdown in ipairs(dropdowns) do
		UIDropDownMenu_Initialize(dropdown, DropDown_Initialize)
		UIDropDownMenu_SetSelectedValue(dropdown, Config:Get(dropdown.configKey))
	end

end

function Config:CreatePanel()
	local panel = CreateFrame("FRAME")
	panel.name = AngryKeystonesLocal  --AngryKeystones.Name
	panel.okay = Panel_OnSave
	panel.cancel = Panel_OnCancel
	panel.OnDefault  = Panel_OnDefaults
	panel.OnRefresh  = Panel_OnRefresh
	local category, layout = Settings.RegisterCanvasLayoutCategory(panel, panel.name);
	Settings.RegisterAddOnCategory(category);
	panel.settingsCategory = category

	return panel
end

function Config:BeforeStartup()
	if AngryKeystones_Config == nil then AngryKeystones_Config = {} end
	if AngryKeystones_CharacterConfig == nil then AngryKeystones_CharacterConfig = {} end

	if not AngryKeystones_Config['__version'] then
		AngryKeystones_Config['__version'] = configVersion
	end
	if not AngryKeystones_CharacterConfig['__version'] then
		AngryKeystones_CharacterConfig['__version'] = configVersion
	end

	AngryKeystones_Config['__version'] = configVersion
	AngryKeystones_CharacterConfig['__version'] = configVersion

	optionPanel = self:CreatePanel(AngryKeystonesLocal)
end

SLASH_AngryKeystones1 = "/akeys"
SLASH_AngryKeystones2 = "/angrykeystones"
function SlashCmdList.AngryKeystones(msg, editbox)
	if optionPanel then
		Settings.OpenToCategory(optionPanel.settingsCategory.ID)
	end
end


local Mod = AngryKeystones:NewModule('ObjectiveTracker')

local challengeMapID

local TIME_FOR_3 = 0.6
local TIME_FOR_2 = 0.8

local function timeFormat(seconds)
	local hours = floor(seconds / 3600)
	local minutes = floor((seconds / 60) - (hours * 60))
	seconds = seconds - hours * 3600 - minutes * 60

	if hours == 0 then
		return format("%d:%.2d", minutes, seconds)
	else
		return format("%d:%.2d:%.2d", hours, minutes, seconds)
	end
end
Mod.timeFormat = timeFormat

local function timeFormatMS(timeAmount)
	local seconds = floor(timeAmount / 1000)
	local ms = timeAmount - seconds * 1000
	local hours = floor(seconds / 3600)
	local minutes = floor((seconds / 60) - (hours * 60))
	seconds = seconds - hours * 3600 - minutes * 60

	if hours == 0 then
		return format("%d:%.2d.%.3d", minutes, seconds, ms)
	else
		return format("%d:%.2d:%.2d.%.3d", hours, minutes, seconds, ms)
	end
end
Mod.timeFormatMS = timeFormatMS

local function GetTimerFrame(block)
	if not block.TimerFrame then
		local TimerFrame = CreateFrame("Frame", nil, block)
		TimerFrame:SetAllPoints(block)
		
		TimerFrame.Text = TimerFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
		TimerFrame.Text:SetPoint("LEFT", block.TimeLeft, "RIGHT", 4, 0)
		
		TimerFrame.Text2 = TimerFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
		TimerFrame.Text2:SetPoint("LEFT", TimerFrame.Text, "RIGHT", 4, 0)

		TimerFrame.Bar3 = TimerFrame:CreateTexture(nil, "OVERLAY")
		TimerFrame.Bar3:SetPoint("TOPLEFT", block.StatusBar, "TOPLEFT", block.StatusBar:GetWidth() * (1 - TIME_FOR_3) - 4, 0)
		TimerFrame.Bar3:SetSize(8, 10)
		TimerFrame.Bar3:SetTexture("Interface\\Addons\\OrzUI\\Media\\Modules\\AngryKeystones")
		TimerFrame.Bar3:SetTexCoord(0, 0.5, 0, 1)

		TimerFrame.Bar2 = TimerFrame:CreateTexture(nil, "OVERLAY")
		TimerFrame.Bar2:SetPoint("TOPLEFT", block.StatusBar, "TOPLEFT", block.StatusBar:GetWidth() * (1 - TIME_FOR_2) - 4, 0)
		TimerFrame.Bar2:SetSize(8, 10)
		TimerFrame.Bar2:SetTexture("Interface\\Addons\\OrzUI\\Media\\Modules\\AngryKeystones")
		TimerFrame.Bar2:SetTexCoord(0.5, 1, 0, 1)

		TimerFrame:Show()

		block.TimerFrame = TimerFrame
	end
	return block.TimerFrame
end

local function UpdateTime(block, elapsedTime)
	local TimerFrame = GetTimerFrame(block)

	local time3 = block.timeLimit * TIME_FOR_3
	local time2 = block.timeLimit * TIME_FOR_2

	TimerFrame.Bar3:SetShown(elapsedTime < time3)
	TimerFrame.Bar2:SetShown(elapsedTime < time2)

	if elapsedTime < time3 then
		TimerFrame.Text:SetText( timeFormat(time3 - elapsedTime) )
		TimerFrame.Text:SetTextColor(1, 0.843, 0)
		TimerFrame.Text:Show()
		if AngryKeystones.Config.silverGoldTimer then
			TimerFrame.Text2:SetText( timeFormat(time2 - elapsedTime) )
			TimerFrame.Text2:SetTextColor(0.78, 0.78, 0.812)
			TimerFrame.Text2:Show()
		else
			TimerFrame.Text2:Hide()
		end
	elseif elapsedTime < time2 then
		TimerFrame.Text:SetText( timeFormat(time2 - elapsedTime) )
		TimerFrame.Text:SetTextColor(0.78, 0.78, 0.812)
		TimerFrame.Text:Show()
		TimerFrame.Text2:Hide()
	else
		TimerFrame.Text:Hide()
		TimerFrame.Text2:Hide()
	end

	if elapsedTime > block.timeLimit then
		block.TimeLeft:SetText(SecondsToClock(elapsedTime - block.timeLimit, false))
	end
end

local function SetUpAffixes(block, affixes)
	local frameWidth, spacing, distance
	if AngryKeystones.Config.smallAffixes then
		frameWidth, spacing, distance = 24, 3, -17
	else
		frameWidth, spacing, distance = 22, 4, -18
	end
	local num = #affixes
	local leftPoint = 28 + (spacing * (num - 1)) + (frameWidth * num)
	block.Affixes[1]:SetPoint("TOPLEFT", block, "TOPRIGHT", -leftPoint, distance)

	for i,affix in pairs(block.Affixes) do
		affix:SetSize(frameWidth, frameWidth)
		affix.Portrait:SetSize(frameWidth - 2, frameWidth - 2)
	end
end

local function ShowBlock(timerID, elapsedTime, timeLimit)
	local block = ScenarioObjectiveTracker.ChallengeModeBlock
	local level, affixes, wasEnergized = C_ChallengeMode.GetActiveKeystoneInfo()
	local dmgPct, healthPct = C_ChallengeMode.GetPowerLevelDamageHealthMod(level)
	if AngryKeystones.Config.showLevelModifier then
		block.Level:SetText( format("%s, +%d%%", CHALLENGE_MODE_POWER_LEVEL:format(level), dmgPct) )
	else
		block.Level:SetText(CHALLENGE_MODE_POWER_LEVEL:format(level))
	end
end

-- if C_AddOns.IsAddOnLoaded("!KalielsTracker") then
--	hooksecurefunc("KT_Scenario_ChallengeMode_UpdateTime", UpdateTime)
--	hooksecurefunc("KT_Scenario_ChallengeMode_ShowBlock", ShowBlock)
-- else
	hooksecurefunc(ScenarioObjectiveTracker.ChallengeModeBlock, "UpdateTime", UpdateTime)
	hooksecurefunc(ScenarioObjectiveTracker.ChallengeModeBlock, "Activate", ShowBlock)
-- end

local keystoneWasCompleted = false
function Mod:PLAYER_ENTERING_WORLD()
	if keystoneWasCompleted and AngryKeystones.Config.resetPopup and IsInGroup() and UnitIsGroupLeader("player") then
		StaticPopup_Show("CONFIRM_RESET_INSTANCES")
	end
	keystoneWasCompleted = false
end

function Mod:CHALLENGE_MODE_START()
	keystoneWasCompleted = false
	challengeMapID = C_ChallengeMode.GetActiveChallengeMapID()
end

function Mod:CHALLENGE_MODE_RESET()
	keystoneWasCompleted = false
end

function Mod:CHALLENGE_MODE_COMPLETED()
	keystoneWasCompleted = true
	if not AngryKeystones.Config.completionMessage then return end
	if not challengeMapID then return end

	local info = C_ChallengeMode.GetChallengeCompletionInfo()
	local name, _, timeLimit = C_ChallengeMode.GetMapUIInfo(info.mapChallengeModeID)
	local time = info.time

	timeLimit = timeLimit * 1000
	local timeLimit2 = timeLimit * TIME_FOR_2
	local timeLimit3 = timeLimit * TIME_FOR_3

	if time <= timeLimit3 then
		print( format("|cff33ff99<%s>|r |cffffd700%s|r", AngryKeystonesLocal, format(AngryKeystones.Locale.completion3, name, timeFormatMS(time), timeFormatMS(timeLimit3 - time))) )
	elseif time <= timeLimit2 then
		print( format("|cff33ff99<%s>|r |cffc7c7cf%s|r", AngryKeystonesLocal, format(AngryKeystones.Locale.completion2, name, timeFormatMS(time), timeFormatMS(timeLimit2 - time), timeFormatMS(time - timeLimit3))) )
	elseif onTime then
		print( format("|cff33ff99<%s>|r |cffeda55f%s|r", AngryKeystonesLocal, format(AngryKeystones.Locale.completion1, name, timeFormatMS(time), timeFormatMS(timeLimit - time), timeFormatMS(time - timeLimit2))) )
	else
		print( format("|cff33ff99<%s>|r |cffff2020%s|r", AngryKeystonesLocal, format(AngryKeystones.Locale.completion0, name, timeFormatMS(time), timeFormatMS(time - timeLimit))) )
	end

	-- local splitMsg = AngryKeystones.Splits:SplitOutput()
	-- if splitMsg then
	-- 	print(  format("%s%s|r", LIGHTYELLOW_FONT_COLOR_CODE, format(AngryKeystones.Locale.completionSplits, splitMsg)) )
	-- end
end

function Mod:Startup()
	self:RegisterEvent("CHALLENGE_MODE_COMPLETED")
	self:RegisterEvent("CHALLENGE_MODE_START")
	self:RegisterEvent("CHALLENGE_MODE_RESET")
	self:RegisterEvent("PLAYER_ENTERING_WORLD")

	AngryKeystones.Config:RegisterCallback('smallAffixes', function()
		local level, affixes, wasEnergized = C_ChallengeMode.GetActiveKeystoneInfo()
		if affixes then
			SetUpAffixes(ScenarioObjectiveTracker.ChallengeModeBlock, affixes)
		end
	end)

	challengeMapID = C_ChallengeMode.GetActiveChallengeMapID()
end



local Mod = AngryKeystones:NewModule('Keystone')

-- local poorColor = select(4, GetItemQualityColor(LE_ITEM_QUALITY_POOR))
-- local epicColor = select(4, GetItemQualityColor(LE_ITEM_QUALITY_EPIC))

-- local events = {
-- 	"CHAT_MSG_SAY",
-- 	"CHAT_MSG_YELL",
-- 	"CHAT_MSG_CHANNEL",
-- 	"CHAT_MSG_TEXT_EMOTE",
-- 	"CHAT_MSG_WHISPER",
-- 	"CHAT_MSG_WHISPER_INFORM",
-- 	"CHAT_MSG_BN_WHISPER",
-- 	"CHAT_MSG_BN_WHISPER_INFORM",
-- 	"CHAT_MSG_BN_CONVERSATION",
-- 	"CHAT_MSG_GUILD",
-- 	"CHAT_MSG_OFFICER",
-- 	"CHAT_MSG_PARTY",
-- 	"CHAT_MSG_PARTY_LEADER",
-- 	"CHAT_MSG_RAID",
-- 	"CHAT_MSG_RAID_LEADER",
-- 	"CHAT_MSG_INSTANCE_CHAT",
-- 	"CHAT_MSG_INSTANCE_CHAT_LEADER",
-- }

-- local function filter(self, event, msg, ...)
-- 	local msg2 = msg:gsub("(|Hkeystone:([0-9:]+)|h(%b[])|h)", function(msg, itemString, itemName)
-- 		local info = { strsplit(":", itemString) }
-- 		local mapID = tonumber(info[1])
-- 		local mapLevel = tonumber(info[2])

-- 		if mapID and mapLevel then
-- 			local mapName = C_ChallengeMode.GetMapInfo(mapID)
-- 			return msg:gsub(itemName:gsub("(%W)","%%%1"), format(AngryKeystones.Locale.keystoneFormat, mapName, mapLevel))
-- 		else
-- 			return msg
-- 		end
-- 	end)
-- 	if msg2 ~= msg then
-- 		return false, msg2, ...
-- 	end
-- end

-- for _, v in pairs(events) do
-- 	ChatFrame_AddMessageEventFilter(v, filter)
-- end

local function SlotKeystone()
	for container=BACKPACK_CONTAINER, NUM_BAG_SLOTS do
		local slots = C_Container.GetContainerNumSlots(container)
		for slot=1, slots do
			local slotLink = C_Container.GetContainerItemLink(container, slot)
			if slotLink and slotLink:match("|Hkeystone:") then
				C_Container.PickupContainerItem(container, slot)
				if (CursorHasItem()) then
					C_ChallengeMode.SlotKeystone()
				end
			end
		end
	end
end

function Mod:Blizzard_ChallengesUI()
	ChallengesKeystoneFrame:HookScript("OnShow", SlotKeystone)
end

function Mod:Startup()
	self:RegisterAddOnLoaded("Blizzard_ChallengesUI")
end


local Mod = AngryKeystones:NewModule('Gossip')

local npcBlacklist = {
	[107435] = true, [112697] = true, [112699] = true, -- Suspicous Noble
	[101462] = true, -- Reaves
	[166663] = true, -- Kyrian Steward
}
local npcWhitelist = {
	[197300] = true, -- Azure Vault, Book of Translocation
}

local cosRumorNPC = 107486

local function IsStaticPopupShown()
	local dialog = nil
	StaticPopup_ForEachShownDialog(function(d)
		if dialog == nil and not d.hasFixedPosition then
			dialog = d
		end
	end)
	return dialog
end

local function IsInActiveChallengeMode()
	local scenarioType = select(10, C_Scenario.GetInfo())
	if scenarioType == LE_SCENARIO_TYPE_CHALLENGE_MODE then
		local timerIDs = {GetWorldElapsedTimers()}
		for i, timerID in ipairs(timerIDs) do
			local _, elapsedTime, type = GetWorldElapsedTime(timerID)
			if type == LE_WORLD_ELAPSED_TIMER_TYPE_CHALLENGE_MODE then
				local mapID = C_ChallengeMode.GetActiveChallengeMapID()
				if mapID then
					return true
				end
			end
		end
	end
	return false
end

function Mod:CoSRumor()
	local clue = C_GossipInfo.GetText()
	local shortClue = AngryKeystones.Locale:Rumor(clue)
	if IsInGroup(LE_PARTY_CATEGORY_INSTANCE) then
		SendChatMessage(shortClue or clue, "INSTANCE_CHAT")
	elseif IsInGroup(LE_PARTY_CATEGORY_HOME) then
		SendChatMessage(shortClue or clue, "PARTY")
	else
		SendChatMessage(shortClue or clue, "SAY")
	end
end

function Mod:GOSSIP_SHOW()
	if not IsInActiveChallengeMode() then return end

	local options = C_GossipInfo.GetOptions()
	local numOptions = #options
	if numOptions ~= 1 then return end -- only automate one gossip option

	local npcId = UnitCreatureID("npc")
	if issecretvalue(npcId) then npcId = 0 end -- occasionally the npc is secret, hopefully blizzard fixes that, cause it's silly -.-

	-- if AngryKeystones.Config.cosRumors and AngryKeystones.Locale:HasRumors() and npcId == cosRumorNPC and numOptions == 0 then
	-- 	self:CoSRumor()
	-- 	C_GossipInfo.CloseGossip()
	-- end

	if AngryKeystones.Config.autoGossip and not npcBlacklist[npcId] then
		if npcWhitelist[npcId] or options[1].icon == 132053 or options[1].icon == 1019848 then -- the gossip icon, prevents auto-opening repair options etc
			local popupWasShown = IsStaticPopupShown()
			C_GossipInfo.SelectOption(options[1].gossipOptionID)
			local popupIsShown = IsStaticPopupShown()
			if popupIsShown then
				if not popupWasShown then
					local button = popupIsShown:GetButton1()
					if button then
						button:Click()
					end
					C_GossipInfo.CloseGossip()
				end
			else
				C_GossipInfo.CloseGossip()
			end
		end
	end	
end

local function PlayCurrent()
	if IsInActiveChallengeMode() and AngryKeystones.Config.hideTalkingHead then
		TalkingHeadFrame:CloseImmediately()
	end
end

function Mod:Blizzard_TalkingHeadUI()
	hooksecurefunc(TalkingHeadFrame, "PlayCurrent", PlayCurrent)
end

function Mod:Startup()
	if not AngryKeystones_Data then AngryKeystones_Data = {} end

	self:RegisterEvent("GOSSIP_SHOW")

	self:RegisterAddOnLoaded("Blizzard_TalkingHeadUI")
end


local Mod = AngryKeystones:NewModule('Splits')

local challengeMapID

local function GetElapsedTime()
	for i = 1, select("#", GetWorldElapsedTimers()) do
		local timerID = select(i, GetWorldElapsedTimers())
		local _, elapsedTime, type = GetWorldElapsedTime(timerID)
		if type == LE_WORLD_ELAPSED_TIMER_TYPE_CHALLENGE_MODE then
			return elapsedTime
		end
	end
end

local function UpdateSplits(self, numCriteria, objectiveBlock, addObjectives)
	local scenarioType = select(10, C_Scenario.GetInfo())
	if not self:ShouldShowCriteria() or not Mod.splits or not Mod.splitNames or scenarioType ~= LE_SCENARIO_TYPE_CHALLENGE_MODE then return end
	if not SCENARIO_TRACKER_MODULE or not objectiveBlock or not objectiveBlock.lines then return end

	for criteriaIndex, elapsed in ipairs(Mod.splits) do
		local criteriaString = Mod.splitNames[criteriaIndex]
		local completed = elapsed ~= false

		if elapsed and elapsed ~= true and criteriaString then
			if AngryKeystones.Config.splitsFormat == 2 and criteriaIndex ~= #Mod.splits then
				local prev = 0
				for i, e in ipairs(Mod.splits) do
					if e and e ~= true and e < elapsed and e > prev and i ~= #Mod.splits then
						prev = e
					end
				end

				local split = elapsed - prev
				criteriaString = string.format("%s, +%s", criteriaString, AngryKeystones.ObjectiveTracker.timeFormat(split))
			elseif AngryKeystones.Config.splitsFormat == 1 or (AngryKeystones.Config.splitsFormat == 2 and criteriaIndex == #Mod.splits) then
				criteriaString = string.format("%s, %s", criteriaString, AngryKeystones.ObjectiveTracker.timeFormat(elapsed))
			end
		end
		if criteriaIndex ~= #Mod.splits then
			criteriaString = string.format("%d/%d %s", completed and 1 or 0, 1, criteriaString)
		end

		if addObjectives then
			SCENARIO_TRACKER_MODULE.lineSpacing = 12;
			if ( completed ) then
				local existingLine = objectiveBlock.lines[criteriaIndex];
				SCENARIO_TRACKER_MODULE:AddObjective(objectiveBlock, criteriaIndex, criteriaString, nil, nil, OBJECTIVE_DASH_STYLE_SHOW, OBJECTIVE_TRACKER_COLOR["Complete"]);
				objectiveBlock.currentLine.Icon:Show();
				objectiveBlock.currentLine.Icon:SetAtlas("Tracker-Check", true);
				objectiveBlock.currentLine.completed = true;
			else
				SCENARIO_TRACKER_MODULE:AddObjective(objectiveBlock, criteriaIndex, criteriaString);
				objectiveBlock.currentLine.Icon:Show();
				objectiveBlock.currentLine.Icon:SetAtlas("Objective-Nub", true);
			end
		else
			local line = objectiveBlock.lines[criteriaIndex]
			if line then
				local old_height = line:GetHeight()
				local height = SCENARIO_TRACKER_MODULE:SetStringText(line.Text, criteriaString, nil, completed and OBJECTIVE_TRACKER_COLOR["Complete"], objectiveBlock.isHighlighted)
				line:SetHeight(height)
				if old_height ~= height then
					objectiveBlock.height = objectiveBlock.height + height - old_height
				end
			end
		end
	end
end
Mod.UpdateSplits = UpdateSplits

function Mod:SplitOutput()
	if AngryKeystones.Config.splitsFormat == 0 then return end

	local splitStrs = {}
	for index, elapsed in ipairs(Mod.splits) do
		if elapsed and elapsed ~= true then
			if AngryKeystones.Config.splitsFormat == 2 then
				local prev = 0
				for i, e in ipairs(Mod.splits) do
					if e and e ~= true and e < elapsed and e > prev and i ~= #Mod.splits then
						prev = e
					end
				end
				local split = elapsed - prev
				table.insert(splitStrs, string.format("%s +%s", Mod.splitNames[index], AngryKeystones.ObjectiveTracker.timeFormat(split)))
			elseif AngryKeystones.Config.splitsFormat == 1 or (AngryKeystones.Config.splitsFormat == 2 and index == #Mod.splits) then
				table.insert(splitStrs, string.format("%s %s", Mod.splitNames[index], AngryKeystones.ObjectiveTracker.timeFormat(elapsed)))
			end
		end
	end
	return table.concat(splitStrs, ", ")
end

function Mod:CHALLENGE_MODE_RESET()
	Mod.splits = nil
	Mod.splitNames = nil
	Mod.mapVariation = nil
	AngryKeystones_Data.state.splits = nil
	AngryKeystones_Data.state.splitNames = nil
	AngryKeystones_Data.state.mapID = nil
end

local function ArcwayMapVariation()
	local ret

	local curMapID, curFloor = GetCurrentMapAreaID(), GetCurrentMapDungeonLevel()
	SetMapToCurrentZone()
	local numPOIs = GetNumMapLandmarks()
	for i=1, numPOIs do
		local _, _, _, _, x, y, _, _, _, _, _, _, atlasIcon = GetMapLandmarkInfo(i)
		x, y = tostring(x), tostring(y)
		if atlasIcon == "map-icon-SuramarDoor.tga" then
			if x == "0.42697441577911" and y == "0.35995090007782" then -- Left Path
				ret = "left"
			end
			if x == "0.53354382514954" and y == "0.35967540740967" then -- Right Path
				ret = "right"
			end
		end
	end
	SetMapByID(curMapID)
	if curFloor then SetDungeonMapLevel(curFloor) end

	return ret
end

-- function Mod:CHALLENGE_MODE_COMPLETED()
-- 	if not challengeMapID then return end
-- 	local info = C_ChallengeMode.GetChallengeCompletionInfo()
-- 	local name, _, timeLimit = C_ChallengeMode.GetMapUIInfo(info.mapChallengeModeID)
-- 	local _, affixes, wasEnergized = C_ChallengeMode.GetActiveKeystoneInfo()
-- 	local splits = Mod.splits
-- 	if not splits then return end

-- 	local missingCount = 0
-- 	for index,elapsed in pairs(splits) do
-- 		if elapsed == false then
-- 			splits[index] = floor(timeElapsed / 1000)
-- 			missingCount = missingCount + 1
-- 		elseif elapsed == true then
-- 			missingCount = missingCount + 1
-- 		end
-- 	end

-- 	splits.date = time()
-- 	splits.level = level
-- 	splits.mapID = mapID
-- 	splits.wasEnergized = wasEnergized
-- 	splits.timeElapsed = timeElapsed / 1000
-- 	splits.timeLimit = timeLimit
-- 	splits.affixes1 = affixes[1]
-- 	splits.affixes2 = affixes[2]
-- 	splits.affixes3 = affixes[3]
-- 	splits.mapVariation = Mod.mapVariation
-- 	splits.patch = GetBuildInfo()

-- 	local unitTokens = { "player", "party1", "party2", "party3", "party4" }
-- 	for i = 1, #unitTokens do
-- 		local u = unitTokens[i]
-- 		if UnitExists(u) then
-- 			splits["party"..i.."Name"] = UnitName(u)
-- 			splits["party"..i.."Class"] = UnitClass(u)
-- 			splits["party"..i.."Role"] = UnitGroupRolesAssigned(u)
-- 			-- splits["party"..i.."Spec"] = u == "player" and GetSpecializationInfo(GetSpecialization() or 0) or GetInspectSpecialization(u)
-- 		end
-- 	end

-- 	if AngryKeystones.Config.recordSplits then
-- 		if not AngryKeystones_Data.splits[mapID] then AngryKeystones_Data.splits[mapID] = {} end
-- 		table.insert(AngryKeystones_Data.splits[mapID], splits)
-- 	end
-- end

function Mod:SCENARIO_UPDATE()
	local scenarioType = select(10, C_Scenario.GetInfo())
	if scenarioType == LE_SCENARIO_TYPE_CHALLENGE_MODE then
		local numCriteria = select(3, C_Scenario.GetStepInfo())
		local mapID = C_ChallengeMode.GetActiveChallengeMapID()
		if not Mod.splits and numCriteria > 0 then
			Mod.splits = {}
			AngryKeystones_Data.state.splits = Mod.splits
			Mod.splitNames = {}
			AngryKeystones_Data.state.splitNames = Mod.splitNames
			AngryKeystones_Data.state.mapID = mapID
			for criteriaIndex = 1, numCriteria do
				-- local criteriaString, criteriaType, completed = C_Scenario.GetCriteriaInfo(criteriaIndex)
				local criteriaInfo = C_ScenarioInfo.GetCriteriaInfo(criteriaIndex)
				if criteriaInfo then
					Mod.splits[criteriaIndex] = criteriaInfo.completed
					Mod.splitNames[criteriaIndex] = criteriaInfo.description
				end
			end
		end
	end
end

function Mod:SCENARIO_CRITERIA_UPDATE()
	local scenarioType = select(10, C_Scenario.GetInfo())
	if scenarioType == LE_SCENARIO_TYPE_CHALLENGE_MODE then
		local mapID = C_ChallengeMode.GetActiveChallengeMapID()
		if mapID == 1516 and AngryKeystones.Config.recordSplits and not Mod.mapVariation then Mod.mapVariation = ArcwayMapVariation() end -- The Arcway

		local fresh = false
		if not Mod.splits then
			Mod.splits = {}
			AngryKeystones_Data.state.splits = Mod.splits
			Mod.splitNames = {}
			AngryKeystones_Data.state.splitNames = Mod.splitNames
			AngryKeystones_Data.state.mapID = mapID
			fresh = true
		end
		local numCriteria = select(3, C_Scenario.GetStepInfo())
		for criteriaIndex = 1, numCriteria do
			-- local criteriaString, criteriaType, completed, quantity, totalQuantity, flags, _, quantityString, criteriaID, _, _, _, isWeightedProgress = C_Scenario.GetCriteriaInfo(criteriaIndex)
			local criteriaInfo = C_ScenarioInfo.GetCriteriaInfo(criteriaIndex)
			if criteriaInfo then
				if not Mod.splitNames[criteriaIndex] then
					Mod.splitNames[criteriaIndex] = criteriaInfo.description
				end
				if Mod.splits[criteriaIndex] == nil then Mod.splits[criteriaIndex] = false end

				if criteriaInfo.completed and not Mod.splits[criteriaIndex] then
					Mod.splits[criteriaIndex] = fresh or GetElapsedTime()
				end
			end
		end
		UpdateSplits(ScenarioObjectiveTracker, numCriteria, ScenarioObjectiveTracker.ObjectivesBlock)
	end
end

function Mod:CHALLENGE_MODE_START()
	challengeMapID = C_ChallengeMode.GetActiveChallengeMapID()
end

function Mod:Startup()
	if not AngryKeystones_Data then AngryKeystones_Data = {} end
	if not AngryKeystones_Data.splits then AngryKeystones_Data.splits = {} end
	if not AngryKeystones_Data.state then AngryKeystones_Data.state = {} end

	local mapID = C_ChallengeMode.GetActiveChallengeMapID()
	if select(10, C_Scenario.GetInfo()) == LE_SCENARIO_TYPE_CHALLENGE_MODE and mapID and mapID == AngryKeystones_Data.state.mapID then
		Mod.splits = AngryKeystones_Data.state.splits
		Mod.splitNames = AngryKeystones_Data.state.splitNames
	else
		AngryKeystones_Data.state.mapID = nil
		AngryKeystones_Data.state.splits = nil
		AngryKeystones_Data.state.splitNames = nil
	end
	challengeMapID = mapID

	self:RegisterEvent("SCENARIO_CRITERIA_UPDATE")
	self:RegisterEvent("CHALLENGE_MODE_START")
	self:RegisterEvent("CHALLENGE_MODE_RESET")
	-- self:RegisterEvent("CHALLENGE_MODE_COMPLETED")
	self:RegisterEvent("SCENARIO_UPDATE")
	self:SCENARIO_UPDATE()
	hooksecurefunc(ScenarioObjectiveTracker, "UpdateCriteria", UpdateSplits)
	AngryKeystones.Config:RegisterCallback('splitsFormat', function()
		UpdateSplits(ScenarioObjectiveTracker, nil, ScenarioObjectiveTracker.ObjectivesBlock)
	end)
end


local Mod = AngryKeystones:NewModule('Schedule')

local rowCount = 3

local requestPartyKeystones

-- 1:Overflowing, 2:Skittish, 3:Volcanic, 4:Necrotic, 5:Teeming, 6:Raging, 7:Bolstering, 8:Sanguine, 9:Tyrannical, 10:Fortified, 11:Bursting, 12:Grievous, 13:Explosive, 14:Quaking, 16:Infested, 117: Reaping, 119:Beguiling 120:Awakened, 121:Prideful, 122:Inspiring, 123:Spiteful, 124:Storming
-- 134:Entangling, 135:Afflicted, 136:Incorporeal
-- TWW 
-- 147:Xal'atath's Guile, 148:Xal'atath's Bargain: Ascendant, 158:Xal'atath's Bargain: Voidbound, 159:Xal'atath's Bargain: Oblivion, 160:Xal'atath's Bargain: Devour, 162:Xal'atath's Bargain: Pulsar, 165:Lindormi's Guidance
-- Midnight Season 1
-- 165, 148, 158, 160, 162, 9, 10, 147 
local affixSchedule = {
	-- Midnight Season 1 (Sort:[1](Level 2+);[2](Level 5+);[3](Level 7+);[4](Level 10+);[5](Level 12+))
	-- Information from(资料来自)：https://www.wowhead.com/guide/midnight/mythic-plus-season-1-overview
	{ [1] = 165, [2] = 162, [3] = 10, [4] = 9 , [5] = 147, }, -- (1) Lindormi's Guidance | Xal'atath's Bargain: Pulsar    | Fortified  | Tyrannical | Xal'atath's Guile
	{ [1] = 165, [2] = 158, [3] = 9 , [4] = 10, [5] = 147, }, -- (2) Lindormi's Guidance | Xal'atath's Bargain: Voidbound | Tyrannical | Fortified  | Xal'atath's Guile
	{ [1] = 165, [2] = 160, [3] = 10, [4] = 9 , [5] = 147, }, -- (3) Lindormi's Guidance | Xal'atath's Bargain: Devour    | Fortified  | Tyrannical | Xal'atath's Guile
	{ [1] = 165, [2] = 162, [3] = 9 , [4] = 10, [5] = 147, }, -- (4) Lindormi's Guidance | Xal'atath's Bargain: Pulsar    | Tyrannical | Fortified  | Xal'atath's Guile
	{ [1] = 165, [2] = 148, [3] = 10, [4] = 9 , [5] = 147, }, -- (5) Lindormi's Guidance | Xal'atath's Bargain: Ascendant | Fortified  | Tyrannical | Xal'atath's Guile
	{ [1] = 165, [2] = 158, [3] = 9 , [4] = 10, [5] = 147, }, -- (6) Lindormi's Guidance | Xal'atath's Bargain: Voidbound | Tyrannical | Fortified  | Xal'atath's Guile
	{ [1] = 165, [2] = 160, [3] = 10, [4] = 9 , [5] = 147, }, -- (7) Lindormi's Guidance | Xal'atath's Bargain: Devour    | Fortified  | Tyrannical | Xal'atath's Guile
	{ [1] = 165, [2] = 148, [3] = 9 , [4] = 10, [5] = 147, }, -- (8) Lindormi's Guidance | Xal'atath's Bargain: Ascendant | Tyrannical | Fortified  | Xal'atath's Guile
}

local scheduleEnabled = true
local affixScheduleUnknown = true
local currentWeek
local currentKeystoneMapID
local currentKeystoneLevel
local unitKeystones = {}
local hookedIconTooltips = false

local function GetNameForKeystone(keystoneMapID, keystoneLevel)
	local keystoneMapName = keystoneMapID and C_ChallengeMode.GetMapUIInfo(keystoneMapID)
	if keystoneMapID and keystoneMapName then
		if AngryKeystones.Locale:Local("dungeon_"..keystoneMapName) then
			keystoneMapName = AngryKeystones.Locale:Get("dungeon_"..keystoneMapName)
		end
		keystoneMapName = gsub(keystoneMapName, ".-%-", "") -- Mechagon
		keystoneMapName = gsub(keystoneMapName, ".-"..HEADER_COLON, "") -- Tazavesh
		return string.format("(%d) %s", keystoneLevel, keystoneMapName)
	end
end

local function UpdatePartyKeystones()
	Mod:CheckCurrentKeystone()
	if requestPartyKeystones then
		Mod:SendPartyKeystonesRequest()
	end

	if not scheduleEnabled then return end
	if not C_AddOns.IsAddOnLoaded("Blizzard_ChallengesUI") then return end

	local playerRealm = select(2, UnitFullName("player")) or ""

	local e = 1
	for i = 1, 4 do
		local entry = Mod.PartyFrame.Entries[e]
		local name, realm = UnitName("party"..i)

		if name then
			local fullName
			if not realm or realm == "" then
				fullName = name.."-"..playerRealm
			else
				fullName = name.."-"..realm
			end

			if unitKeystones[fullName] ~= nil then
				local keystoneName
				if unitKeystones[fullName] == 0 then
					keystoneName = NONE
				else
					keystoneName = GetNameForKeystone(unitKeystones[fullName][1], unitKeystones[fullName][2])
				end
				if keystoneName then
					entry:Show()
					local _, class = UnitClass("party"..i)
					local color = RAID_CLASS_COLORS[class]
					entry.Text:SetText(name)
					entry.Text:SetTextColor(color:GetRGBA())
					entry.Text2:SetText(keystoneName)
					e = e + 1
				end
			end
		end
	end
	if e == 1 then
		Mod.AffixFrame:ClearAllPoints()
		Mod.AffixFrame:SetPoint("LEFT", ChallengesFrame.WeeklyInfo.Child.WeeklyChest, "RIGHT", 130, 0)
		Mod.PartyFrame:Hide()
	else
		Mod.AffixFrame:ClearAllPoints()
		Mod.AffixFrame:SetPoint("TOPLEFT", ChallengesFrame.WeeklyInfo.Child.WeeklyChest, "TOPRIGHT", 130, 55)
		Mod.PartyFrame:Show()
	end
	while e <= 4 do
		Mod.PartyFrame.Entries[e]:Hide()
		e = e + 1
	end
end

local function UpdateFrame()
	if not scheduleEnabled then return end
	
	Mod:CheckAffixes()
	Mod.AffixFrame:Show()
	Mod.PartyFrame:Show()
	Mod.KeystoneText:Show()

	local weeklyChest = ChallengesFrame.WeeklyInfo.Child.WeeklyChest
	weeklyChest:ClearAllPoints()
	weeklyChest:SetPoint("LEFT", 100, 0)

	-- Wordwrap and size of the original frame
	local description = ChallengesFrame.WeeklyInfo.Child.WeeklyChest.RunStatus
	description:SetWordWrap(true)
	description:SetSize(200, 90)

	local currentKeystoneName = GetNameForKeystone(C_MythicPlus.GetOwnedKeystoneChallengeMapID(), C_MythicPlus.GetOwnedKeystoneLevel())
	if currentKeystoneName then
		Mod.KeystoneText:Show()
		Mod.KeystoneText:SetText( string.format(AngryKeystones.Locale.currentKeystoneText, currentKeystoneName) )
	else
		Mod.KeystoneText:Hide()
	end

	if currentWeek and not affixScheduleUnknown then
		for i = 1, rowCount do
			local entry = Mod.AffixFrame.Entries[i]
			entry:Show()

			local scheduleWeek = (currentWeek - 1 + i) % (#affixSchedule) + 1
			local affixes = affixSchedule[scheduleWeek]
			for j = 1, #affixes do
				local affix = entry.Affixes[j]
				affix:SetUp(affixes[j])
			end
		end
		Mod.AffixFrame.Label:Hide()
	else
		for i = 1, rowCount do
			Mod.AffixFrame.Entries[i]:Hide()
		end
		Mod.AffixFrame.Label:Show()
	end
	UpdatePartyKeystones()

	if not hookedIconTooltips then
		hookedIconTooltips = true
		local timeLimitFormat = AngryKeystones.Locale:Get("timeLimit")
		for _, icon in next, ChallengesFrame.DungeonIcons do
			icon:HookScript("OnEnter", function(self)
				local _, _, timeLimit = C_ChallengeMode.GetMapUIInfo(self.mapID)
				GameTooltip_AddBlankLineToTooltip(GameTooltip)
				GameTooltip_AddColoredLine(GameTooltip, timeLimitFormat:format(SecondsToClock(timeLimit)), HIGHLIGHT_FONT_COLOR)
				GameTooltip:Show()
			end)
		end
	end
end

local function makeAffix(parent)
	local frame = CreateFrame("Frame", nil, parent)
	frame:SetSize(16, 16)

	local border = frame:CreateTexture(nil, "OVERLAY")
	border:SetAllPoints()
	border:SetAtlas("ChallengeMode-AffixRing-Sm")
	frame.Border = border

	local portrait = frame:CreateTexture(nil, "ARTWORK")
	portrait:SetSize(14, 14)
	portrait:SetPoint("CENTER", border)
	frame.Portrait = portrait

	frame.SetUp = ScenarioChallengeModeAffixMixin.SetUp
	frame:SetScript("OnEnter", ScenarioChallengeModeAffixMixin.OnEnter)
	frame:SetScript("OnLeave", GameTooltip_Hide)

	return frame
end

function Mod:Blizzard_ChallengesUI()
	if not scheduleEnabled then return end
	
	local frame = CreateFrame("Frame", nil, ChallengesFrame)
	frame:SetSize(246, 92)
	frame:SetPoint("TOPLEFT", ChallengesFrame.WeeklyInfo.Child.WeeklyChest, "TOPRIGHT", -20, 30)
	Mod.AffixFrame = frame

	local bg = frame:CreateTexture(nil, "BACKGROUND")
	bg:SetAllPoints()
	bg:SetAtlas("ChallengeMode-guild-background")
	bg:SetAlpha(0.4)

	local title = frame:CreateFontString(nil, "ARTWORK", "GameFontNormalMed2")
	title:SetText(AngryKeystones.Locale.scheduleTitle)
	title:SetPoint("TOPLEFT", 15, -7)

	local line = frame:CreateTexture(nil, "ARTWORK")
	line:SetSize(232, 9)
	line:SetAtlas("ChallengeMode-RankLineDivider", false)
	line:SetPoint("TOP", 0, -20)

	local entries = {}
	for i = 1, rowCount do
		local entry = CreateFrame("Frame", nil, frame)
		entry:SetSize(216, 18)

		local text = entry:CreateFontString(nil, "ARTWORK", "GameFontNormal")
		text:SetWidth(120)
		text:SetJustifyH("LEFT")
		text:SetWordWrap(false)
		text:SetText( AngryKeystones.Locale["scheduleWeek"..i+1] )
		text:SetPoint("LEFT")
		entry.Text = text

		local affixes = {}
		local prevAffix
		for j = 5, 1, -1 do
			local affix = makeAffix(entry)
			if prevAffix then
				affix:SetPoint("RIGHT", prevAffix, "LEFT", -4, 0)
			else
				affix:SetPoint("RIGHT")
			end
			prevAffix = affix
			affixes[j] = affix
		end
		entry.Affixes = affixes

		if i == 1 then
			entry:SetPoint("TOP", line, "BOTTOM")
		else
			entry:SetPoint("TOP", entries[i-1], "BOTTOM")
		end

		entries[i] = entry
	end
	frame.Entries = entries

	local label = frame:CreateFontString(nil, "ARTWORK", "GameFontNormal")
	label:SetPoint("TOPLEFT", line, "BOTTOMLEFT", 10, 0)
	label:SetPoint("TOPRIGHT", line, "BOTTOMRIGHT", -10, 0)
	label:SetJustifyH("CENTER")
	label:SetJustifyV("MIDDLE")
	label:SetHeight(72)
	label:SetWordWrap(true)
	if affixScheduleUnknown then
		label:SetText(AngryKeystones.Locale.scheduleUnknown)
	end
	frame.Label = label

	local frame2 = CreateFrame("Frame", nil, ChallengesFrame)
	frame2:SetSize(246, 110)
	frame2:SetPoint("TOP", frame, "BOTTOM", 0, -10)
	Mod.PartyFrame = frame2

	local bg2 = frame2:CreateTexture(nil, "BACKGROUND")
	bg2:SetAllPoints()
	bg2:SetAtlas("ChallengeMode-guild-background")
	bg2:SetAlpha(0.4)

	local title2 = frame2:CreateFontString(nil, "ARTWORK", "GameFontNormalMed2")
	title2:SetText(AngryKeystones.Locale.partyKeysTitle)
	title2:SetPoint("TOPLEFT", 15, -7)

	local line2 = frame2:CreateTexture(nil, "ARTWORK")
	line2:SetSize(232, 9)
	line2:SetAtlas("ChallengeMode-RankLineDivider", false)
	line2:SetPoint("TOP", 0, -20)

	local entries2 = {}
	for i = 1, 4 do
		local entry = CreateFrame("Frame", nil, frame2)
		entry:SetSize(216, 18)

		local text = entry:CreateFontString(nil, "ARTWORK", "GameFontNormal")
		text:SetWidth(120)
		text:SetJustifyH("LEFT")
		text:SetWordWrap(false)
		text:SetText()
		text:SetPoint("LEFT")
		entry.Text = text

		local text2 = entry:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
		text2:SetWidth(140)
		text2:SetJustifyH("RIGHT")
		text2:SetWordWrap(false)
		text2:SetText()
		text2:SetPoint("RIGHT")
		entry.Text2 = text2

		if i == 1 then
			entry:SetPoint("TOP", line2, "BOTTOM")
		else
			entry:SetPoint("TOP", entries2[i-1], "BOTTOM")
		end

		entries2[i] = entry
	end
	frame2.Entries = entries2

	local keystoneText = ChallengesFrame.WeeklyInfo.Child:CreateFontString(nil, "ARTWORK", "GameFontNormalMed2")
	keystoneText:SetPoint("TOP", ChallengesFrame.WeeklyInfo.Child.WeeklyChest, "BOTTOM", 0, -80)
	keystoneText:SetWidth(300)
	Mod.KeystoneText = keystoneText

	hooksecurefunc(ChallengesFrame, "Update", UpdateFrame)
end

function Mod:GetInventoryKeystone()
	for container=BACKPACK_CONTAINER, NUM_BAG_SLOTS do
		local slots = C_Container.GetContainerNumSlots(container)
		for slot=1, slots do
			local _, _, _, _, _, _, slotLink = C_Container.GetContainerItemInfo(container, slot)
			local itemString = slotLink and slotLink:match("|Hkeystone:([0-9:]+)|h(%b[])|h")
			if itemString then
				return slotLink, itemString
			end
		end
	end
end

function Mod:CheckAffixes()
	currentWeek = nil
	local currentAffixes = C_MythicPlus.GetCurrentAffixes()

	if currentAffixes then
		for index, affixes in ipairs(affixSchedule) do
			if #affixes == #currentAffixes and affixes[1] == currentAffixes[1].id and affixes[2] == currentAffixes[2].id and affixes[3] == currentAffixes[3].id and affixes[4] == currentAffixes[4].id and affixes[5] == currentAffixes[5].id then
				currentWeek = index
				affixScheduleUnknown = false
				break
			end
		end
	end
end

local bagUpdateTimerStarted = false
function Mod:BAG_UPDATE()
	if not bagUpdateTimerStarted then
		bagUpdateTimerStarted = true
		C_Timer.After(2, function()
			Mod:CheckCurrentKeystone()
			bagUpdateTimerStarted = false
		end)
	end
end

function Mod:CHAT_MSG_LOOT(...)
	local lootString, _, _, _, unit = ...
	if string.match(lootString, "|Hitem:158923:") then
		if UnitName("player") == unit then
			self:CheckCurrentKeystone()
		else
			self:SetPartyKeystoneRequest()
		end
	end
end

function Mod:SetPartyKeystoneRequest()
	requestPartyKeystones = true
	if C_AddOns.IsAddOnLoaded("Blizzard_ChallengesUI") and ChallengesFrame:IsShown() then
		self:SendPartyKeystonesRequest()
		UpdatePartyKeystones()
	end
end

function Mod:SendPartyKeystonesRequest()
	requestPartyKeystones = false
	self:SendAddOnComm("request", "PARTY")
end

local hadKeystone = false
function Mod:CheckCurrentKeystone(announce)
	local keystoneMapID = C_MythicPlus.GetOwnedKeystoneChallengeMapID()
	local keystoneLevel = C_MythicPlus.GetOwnedKeystoneLevel()

	if keystoneMapID ~= currentKeystoneMapID or keystoneLevel ~= currentKeystoneLevel then
		currentKeystoneMapID = keystoneMapID
		currentKeystoneLevel = keystoneLevel

		if hadKeystone and announce ~= false and AngryKeystones.Config.announceKeystones then
			local itemLink = self:GetInventoryKeystone()
			if itemLink and IsInGroup(LE_PARTY_CATEGORY_HOME) then
				SendChatMessage(string.format(AngryKeystones.Locale.newKeystoneAnnounce, itemLink), "PARTY")
			end
		end

		hadKeystone = true
		self:SendCurrentKeystone()
	end
end

function Mod:SendCurrentKeystone()
	local keystoneMapID = C_MythicPlus.GetOwnedKeystoneChallengeMapID()
	local keystoneLevel = C_MythicPlus.GetOwnedKeystoneLevel()
	
	local message = "0"
	if keystoneLevel and keystoneMapID then
		message = string.format("%d:%d", keystoneMapID, keystoneLevel)
	end

	self:SendAddOnComm(message, "PARTY")
end

function Mod:ReceiveAddOnComm(message, type, sender)
	if message == "request" then
		requestPartyKeystones = false
		self:SendCurrentKeystone()
	elseif message == "0" then
		if unitKeystones[sender] ~= 0 then
			unitKeystones[sender] = 0
			UpdatePartyKeystones()
		end
	else
		local arg1, arg2 = message:match("^(%d+):(%d+)$")
		local keystoneMapID = arg1 and tonumber(arg1)
		local keystoneLevel = arg2 and tonumber(arg2)
		if keystoneMapID and keystoneLevel and (unitKeystones[sender] == nil or unitKeystones[sender] == 0
				or not (unitKeystones[sender][1] == keystoneMapID and unitKeystones[sender][2] == keystoneLevel)) then
			unitKeystones[sender] = { keystoneMapID, keystoneLevel }
			UpdatePartyKeystones()
		end
	end
end

function Mod:CHALLENGE_MODE_START()
	self:CheckCurrentKeystone(false)
	C_Timer.After(2, function() self:CheckCurrentKeystone(false) end)
	self:SetPartyKeystoneRequest()
end

function Mod:CHALLENGE_MODE_COMPLETED()
	self:CheckCurrentKeystone()
	C_Timer.After(2, function() self:CheckCurrentKeystone() end)
	self:SetPartyKeystoneRequest()
end

function Mod:CHALLENGE_MODE_UPDATED()
	self:CheckCurrentKeystone()
end

function Mod:Startup()
	scheduleEnabled = AngryKeystones.Config.schedule
	
	self:RegisterAddOnLoaded("Blizzard_ChallengesUI")
	self:RegisterEvent("GROUP_ROSTER_UPDATE", "SetPartyKeystoneRequest")
	self:RegisterEvent("BAG_UPDATE")
	-- self:RegisterEvent("CHAT_MSG_LOOT")
	self:RegisterEvent("CHALLENGE_MODE_COMPLETED")
	self:RegisterEvent("CHALLENGE_MODE_START")
	self:RegisterEvent("CHALLENGE_MODE_MAPS_UPDATE", "CHALLENGE_MODE_UPDATED")
	self:RegisterEvent("CHALLENGE_MODE_LEADERS_UPDATE", "CHALLENGE_MODE_UPDATED")
	self:RegisterEvent("CHALLENGE_MODE_MEMBER_INFO_UPDATED", "CHALLENGE_MODE_UPDATED")
	self:RegisterAddOnComm()
	self:CheckCurrentKeystone()

	C_Timer.After(3, function()
		C_MythicPlus.RequestCurrentAffixes()
		C_MythicPlus.RequestRewards()
	end)

	C_Timer.NewTicker(60, function() self:CheckCurrentKeystone() end)
	
	requestPartyKeystones = true
end
