-------------------------------------------------------------------------------
-- Module Declaration
--

local oldPlugin = BigWigs:NewPlugin("Super Emphasize") -- XXX temp 9.0.2
oldPlugin.defaultDB = {
	Countdown = {},
}

local plugin = BigWigs:NewPlugin("Countdown")
if not plugin then return end

local voiceMap = {
	enUS = {"English: Default (%s)", "Male", "Female"},
	deDE = {"Deutsch: Standard (%s)", "Männlich", "Weiblich"},
	esES = {"Español (es): Predeterminado (%s)", "Masculino", "Femenino"},
	esMX = {"Español (mx): Predeterminado (%s)", "Masculino", "Femenino"},
	frFR = {"Français : Défaut (%s)", "Homme", "Femme"},
	itIT = {"Italiano: Predefinito (%s)", "Maschio", "Femmina"},
	koKR = {"한국어 : 기본 (%s)", "남성", "여성"},
	ptBR = {"Português: Padrão (%s)", "Masculino", "Feminino"},
	ruRU = {"Русский: По умолчанию (%s)", "Мужской", "Женский"},
	zhCN = {"简体中文:默认(%s)", "男性", "女性"},
	zhTW = {"繁體中文:預設值(%s)", "男性", "女性"},
}
local defaultVoice = "English: Amy"
do
	local locale = GetLocale()
	if locale ~= "enUS" and voiceMap[locale] then
		defaultVoice = ("%s: Default (Female)"):format(locale)
	end
end

plugin.defaultDB = {
	textEnabled = true,
	fontName = plugin:GetDefaultFont(),
	outline = "THICKOUTLINE",
	fontSize = 48,
	monochrome = false,
	fontColor = { r = 1, g = 0, b = 0 },
	voice = defaultVoice,
	countdownTime = 5,
	position = {"TOP", "TOP", 0, -300},
	bossCountdowns = {},
}

-------------------------------------------------------------------------------
-- Locals
--

local media = LibStub("LibSharedMedia-3.0")
local FONT = media.MediaType and media.MediaType.FONT or "font"
local BigWigsAPI = BigWigsAPI
local L = BigWigsAPI:GetLocale("BigWigs: Plugins")
plugin.displayName = L.countdown
local PlaySoundFile = PlaySoundFile

local countdownAnchor = nil
local countdownFrame = nil
local countdownText = nil
local inConfigMode = false

-------------------------------------------------------------------------------
-- Countdown Registration
--

BigWigsAPI:RegisterCountdown(L.none, { false, false, false, false, false })
BigWigsAPI:RegisterCountdown("English: Amy", {
	"Interface\\AddOns\\BigWigs\\Media\\Sounds\\Amy\\1.ogg",
	"Interface\\AddOns\\BigWigs\\Media\\Sounds\\Amy\\2.ogg",
	"Interface\\AddOns\\BigWigs\\Media\\Sounds\\Amy\\3.ogg",
	"Interface\\AddOns\\BigWigs\\Media\\Sounds\\Amy\\4.ogg",
	"Interface\\AddOns\\BigWigs\\Media\\Sounds\\Amy\\5.ogg",
	"Interface\\AddOns\\BigWigs\\Media\\Sounds\\Amy\\6.ogg",
	"Interface\\AddOns\\BigWigs\\Media\\Sounds\\Amy\\7.ogg",
	"Interface\\AddOns\\BigWigs\\Media\\Sounds\\Amy\\8.ogg",
	"Interface\\AddOns\\BigWigs\\Media\\Sounds\\Amy\\9.ogg",
	"Interface\\AddOns\\BigWigs\\Media\\Sounds\\Amy\\10.ogg",
})
BigWigsAPI:RegisterCountdown("English: David", {
	"Interface\\AddOns\\BigWigs\\Media\\Sounds\\David\\1.ogg",
	"Interface\\AddOns\\BigWigs\\Media\\Sounds\\David\\2.ogg",
	"Interface\\AddOns\\BigWigs\\Media\\Sounds\\David\\3.ogg",
	"Interface\\AddOns\\BigWigs\\Media\\Sounds\\David\\4.ogg",
	"Interface\\AddOns\\BigWigs\\Media\\Sounds\\David\\5.ogg",
	"Interface\\AddOns\\BigWigs\\Media\\Sounds\\David\\6.ogg",
	"Interface\\AddOns\\BigWigs\\Media\\Sounds\\David\\7.ogg",
	"Interface\\AddOns\\BigWigs\\Media\\Sounds\\David\\8.ogg",
	"Interface\\AddOns\\BigWigs\\Media\\Sounds\\David\\9.ogg",
	"Interface\\AddOns\\BigWigs\\Media\\Sounds\\David\\10.ogg",
})
BigWigsAPI:RegisterCountdown("English: Jim", {
	"Interface\\AddOns\\BigWigs\\Media\\Sounds\\Jim\\1.ogg",
	"Interface\\AddOns\\BigWigs\\Media\\Sounds\\Jim\\2.ogg",
	"Interface\\AddOns\\BigWigs\\Media\\Sounds\\Jim\\3.ogg",
	"Interface\\AddOns\\BigWigs\\Media\\Sounds\\Jim\\4.ogg",
	"Interface\\AddOns\\BigWigs\\Media\\Sounds\\Jim\\5.ogg",
	"Interface\\AddOns\\BigWigs\\Media\\Sounds\\Jim\\6.ogg",
	"Interface\\AddOns\\BigWigs\\Media\\Sounds\\Jim\\7.ogg",
	"Interface\\AddOns\\BigWigs\\Media\\Sounds\\Jim\\8.ogg",
	"Interface\\AddOns\\BigWigs\\Media\\Sounds\\Jim\\9.ogg",
	"Interface\\AddOns\\BigWigs\\Media\\Sounds\\Jim\\10.ogg",
})

for locale, info in next, voiceMap do
	local name, male, female = unpack(info)

	BigWigsAPI:RegisterCountdown(("%s: Default (Male)"):format(locale), name:format(male), {
		"Interface\\AddOns\\BigWigs\\Media\\Sounds\\Heroes\\"..locale.."\\male\\1.ogg",
		"Interface\\AddOns\\BigWigs\\Media\\Sounds\\Heroes\\"..locale.."\\male\\2.ogg",
		"Interface\\AddOns\\BigWigs\\Media\\Sounds\\Heroes\\"..locale.."\\male\\3.ogg",
		"Interface\\AddOns\\BigWigs\\Media\\Sounds\\Heroes\\"..locale.."\\male\\4.ogg",
		"Interface\\AddOns\\BigWigs\\Media\\Sounds\\Heroes\\"..locale.."\\male\\5.ogg",
	})

	local id = ("%s: Default (Female)"):format(locale)
	if locale == "esMX" then
		-- never extracted the esMX female announcer and it's gone now, so just use esES
		locale = "esES"
	end
	BigWigsAPI:RegisterCountdown(id, name:format(female), {
		"Interface\\AddOns\\BigWigs\\Media\\Sounds\\Heroes\\"..locale.."\\female\\1.ogg",
		"Interface\\AddOns\\BigWigs\\Media\\Sounds\\Heroes\\"..locale.."\\female\\2.ogg",
		"Interface\\AddOns\\BigWigs\\Media\\Sounds\\Heroes\\"..locale.."\\female\\3.ogg",
		"Interface\\AddOns\\BigWigs\\Media\\Sounds\\Heroes\\"..locale.."\\female\\4.ogg",
		"Interface\\AddOns\\BigWigs\\Media\\Sounds\\Heroes\\"..locale.."\\female\\5.ogg",
	})
end

--------------------------------------------------------------------------------
-- Anchors & Frames
--

local function showAnchors()
	inConfigMode = true
	countdownAnchor:Show()
	countdownFrame:Show()
	countdownText:SetText("5")
end

local function hideAnchors()
	inConfigMode = false
	countdownAnchor:Hide()
	countdownFrame:Hide()
end

do
	local function OnDragStart(self)
		self:StartMoving()
	end
	local function OnDragStop(self)
		self:StopMovingOrSizing()
		local point, _, relPoint, x, y = self:GetPoint()
		plugin.db.profile.position = {point, relPoint, x, y}
		plugin:UpdateGUI() -- Update X/Y if GUI is open.
	end
	local function RefixPosition(self)
		self:ClearAllPoints()
		local point, relPoint = plugin.db.profile.position[1], plugin.db.profile.position[2]
		local x, y = plugin.db.profile.position[3], plugin.db.profile.position[4]
		self:SetPoint(point, UIParent, relPoint, x, y)
	end

	countdownAnchor = CreateFrame("Frame", nil, UIParent)
	countdownAnchor:EnableMouse(true)
	countdownAnchor:SetClampedToScreen(true)
	countdownAnchor:SetMovable(true)
	countdownAnchor:RegisterForDrag("LeftButton")
	countdownAnchor:SetWidth(80)
	countdownAnchor:SetHeight(80)
	countdownAnchor:SetFrameStrata("HIGH")
	countdownAnchor:SetFrameLevel(20)
	if countdownAnchor.SetFixedFrameStrata then
		countdownAnchor:SetFixedFrameStrata(true)
		countdownAnchor:SetFixedFrameLevel(true)
	end
	countdownAnchor:SetScript("OnDragStart", OnDragStart)
	countdownAnchor:SetScript("OnDragStop", OnDragStop)
	countdownAnchor.RefixPosition = RefixPosition
	local point, relPoint = plugin.defaultDB.position[1], plugin.defaultDB.position[2]
	local x, y = plugin.defaultDB.position[3], plugin.defaultDB.position[4]
	countdownAnchor:SetPoint(point, UIParent, relPoint, x, y)
	countdownAnchor:Hide()
	local bg = countdownAnchor:CreateTexture()
	bg:SetAllPoints(countdownAnchor)
	bg:SetColorTexture(0, 0, 0, 0.3)
	local header = countdownAnchor:CreateFontString()
	header:SetFont(plugin:GetDefaultFont(12))
	header:SetShadowOffset(1, -1)
	header:SetTextColor(1,0.82,0,1)
	header:SetText(L.textCountdown)
	header:SetPoint("BOTTOM", countdownAnchor, "TOP", 0, 5)
	header:SetJustifyV("MIDDLE")
	header:SetJustifyH("CENTER")
end

-------------------------------------------------------------------------------
-- Options
--

local function voiceSorting()
	local list = BigWigsAPI:GetCountdownList()
	local sorted = {}
	for k in next, list do
		if k ~= L.none then
			sorted[#sorted + 1] = k
		end
	end
	sort(sorted, function(a, b) return list[a] < list[b] end)
	tinsert(sorted, 1, L.none)
	return sorted
end

local function UpdateFont()
	local flags = nil
	if plugin.db.profile.monochrome and plugin.db.profile.outline ~= "NONE" then
		flags = "MONOCHROME," .. plugin.db.profile.outline
	elseif plugin.db.profile.monochrome then
		flags = "MONOCHROME"
	elseif plugin.db.profile.outline ~= "NONE" then
		flags = plugin.db.profile.outline
	end
	countdownText:SetFont(media:Fetch(FONT, plugin.db.profile.fontName), plugin.db.profile.fontSize, flags)
	countdownText:SetTextColor(plugin.db.profile.fontColor.r, plugin.db.profile.fontColor.g, plugin.db.profile.fontColor.b)
end

do
	local checkTextDisabled = function() return not plugin.db.profile.textEnabled end
	plugin.pluginOptions = {
		name = "|TInterface\\AddOns\\BigWigs\\Media\\Icons\\Menus\\Countdown:20|t ".. L.countdown,
		type = "group",
		childGroups = "tab",
		get = function(info) return plugin.db.profile[info[#info]] end,
		set = function(info, value)
			plugin.db.profile[info[#info]] = value
			UpdateFont()
		end,
		order = 5,
		args = {
			general = {
				type = "group",
				name = L.general,
				order = 1,
				args = {
					heading = {
						type = "description",
						name = L.countdownDesc.. "\n\n",
						order = 1,
						width = "full",
						fontSize = "medium",
					},
					countdownTime = {
						name = L.countdownAt,
						desc = L.countdownAt_desc,
						type = "range", min = 3, max = 10, step = 1,
						order = 2,
						width = 2,
					},
					countdownTest = {
						name = L.countdownTest,
						type = "execute",
						handler = plugin,
						func = "TestCountdown",
						order = 3,
					},
					audioSpacer = {
						type = "description",
						name = "\n\n",
						order = 4,
						width = "full",
						fontSize = "medium",
					},
					audioHeader = {
						type = "header",
						name = L.countdownAudioHeader,
						order = 5,
					},
					voice = {
						name = L.countdownVoice,
						type = "select",
						values = BigWigsAPI.GetCountdownList,
						sorting = voiceSorting,
						order = 6,
						width = "full",
					},
					textSpacer = {
						type = "description",
						name = "\n\n",
						order = 7,
						width = "full",
						fontSize = "medium",
					},
					textHeader = {
						type = "header",
						name = L.countdownTextHeader,
						order = 8,
					},
					textEnabled = {
						type = "toggle",
						name = L.textCountdown,
						desc = L.textCountdownDesc,
						order = 9,
						width = "full",
					},
					fontName = {
						type = "select",
						name = L.font,
						order = 10,
						values = media:List(FONT),
						itemControl = "DDI-Font",
						get = function()
							for i, v in next, media:List(FONT) do
								if v == plugin.db.profile.fontName then return i end
							end
						end,
						set = function(_, value)
							local list = media:List(FONT)
							plugin.db.profile.fontName = list[value]
							UpdateFont()
						end,
						width = 2,
						disabled = checkTextDisabled,
					},
					outline = {
						type = "select",
						name = L.outline,
						order = 11,
						values = {
							NONE = L.none,
							OUTLINE = L.thin,
							THICKOUTLINE = L.thick,
						},
						disabled = checkTextDisabled,
					},
					fontColor = {
						type = "color",
						name = L.countdownColor,
						get = function(info)
							return plugin.db.profile.fontColor.r, plugin.db.profile.fontColor.g, plugin.db.profile.fontColor.b
						end,
						set = function(info, r, g, b)
							plugin.db.profile.fontColor.r, plugin.db.profile.fontColor.g, plugin.db.profile.fontColor.b = r, g, b
							UpdateFont()
						end,
						order = 12,
						disabled = checkTextDisabled,
					},
					fontSize = {
						type = "range",
						name = L.fontSize,
						desc = L.fontSizeDesc,
						order = 13,
						softMax = 100, max = 200, min = 20, step = 1,
						disabled = checkTextDisabled,
					},
					monochrome = {
						type = "toggle",
						name = L.monochrome,
						desc = L.monochromeDesc,
						order = 14,
						disabled = checkTextDisabled,
					},
					resetHeader = {
						type = "header",
						name = "",
						order = 15,
					},
					reset = {
						type = "execute",
						name = L.reset,
						desc = L.resetCountdownDesc,
						func = function()
							local restoreCountdowns = plugin.db.profile.bossCountdowns
							plugin.db:ResetProfile()
							plugin.db.profile.bossCountdowns = restoreCountdowns
						end,
						order = 16,
					},
					resetAll = {
						type = "execute",
						name = L.resetAll,
						desc = L.resetAllCountdownDesc,
						func = function() plugin.db:ResetProfile() end,
						order = 17,
					},
				},
			},
			exactPositioning = {
				type = "group",
				name = L.positionExact,
				order = 2,
				disabled = checkTextDisabled,
				args = {
					posx = {
						type = "range",
						name = L.positionX,
						desc = L.positionDesc,
						min = -2048,
						max = 2048,
						step = 1,
						order = 1,
						width = "full",
						get = function()
							return plugin.db.profile.position[3]
						end,
						set = function(_, value)
							plugin.db.profile.position[3] = value
							countdownAnchor:RefixPosition()
						end,
					},
					posy = {
						type = "range",
						name = L.positionY,
						desc = L.positionDesc,
						min = -2048,
						max = 2048,
						step = 1,
						order = 2,
						width = "full",
						get = function()
							return plugin.db.profile.position[4]
						end,
						set = function(_, value)
							plugin.db.profile.position[4] = value
							countdownAnchor:RefixPosition()
						end,
					},
				},
			},
		},
	}
end

local function createOptions()
	local sModule = BigWigs:GetPlugin("Sounds", true)
	if sModule then
		-- ability options
		sModule.soundOptions.args.separator1 = {
			name = "",
			type = "description",
			order = 3,
		}
		sModule.soundOptions.args.countdown = {
			name = "Countdown",
			type = "select",
			values = BigWigsAPI.GetCountdownList,
			sorting = voiceSorting,
			get = function(info)
				local name, key = unpack(info.arg)
				return plugin.db.profile.bossCountdowns[name] and plugin.db.profile.bossCountdowns[name][key] or plugin.db.profile.voice
			end,
			set = function(info, value)
				local name, key = unpack(info.arg)
				if value ~= plugin.db.profile.voice then
					if not plugin.db.profile.bossCountdowns[name] then plugin.db.profile.bossCountdowns[name] = {} end
					plugin.db.profile.bossCountdowns[name][key] = value
				else -- clean up
					if plugin.db.profile.bossCountdowns[name] then
						plugin.db.profile.bossCountdowns[name][key] = nil
					end
					if not next(plugin.db.profile.bossCountdowns[name]) then
						plugin.db.profile.bossCountdowns[name] = nil
					end
				end
			end,
			order = 4,
			width = "full",
		}
	end
end

-------------------------------------------------------------------------------
-- Initialization
--

do
	local LOCALE = GetLocale()
	local KEY = "%s: Default (Female)"
	local function check(voice)
		local lang = voice and voice:match("^(.+): Heroes of the Storm$")
		if not lang then return end

		if lang == "Español" then
			-- Try to pick the correct Spanish locale
			if LOCALE == "esMX" or LOCALE == "esES" then
				return KEY:format(LOCALE)
			end
			return KEY:format(GetCurrentRegion() == 1 and "esMX" or "esES") -- NA or EU
		end

		for locale, info in next, voiceMap do
			if info[1]:sub(1, #lang) == lang then
				return KEY:format(locale)
			end
		end
	end

	local function upgradeDB(sv)
		if not sv or not sv.profiles then return end
		for profile, db in next, sv.profiles do
			local voice = check(db.voice)
			if voice then
				db.voice = voice
			end
			if db.bossCountdowns then
				for moduleName, abilities in next, db.bossCountdowns do
					for k, v in next, abilities do
						local voice = check(v)
						if voice then
							abilities[k] = voice
						end
					end
				end
			end
		end
	end

	function plugin:OnRegister()
		-- XXX temp 9.0.5
		upgradeDB(self.db)
		upgradeDB(BigWigsClassicDB.namespaces["BigWigs_Plugins_Pull"])
	end
end

do
	local function updateProfile()
		local db = plugin.db.profile

		for k, v in next, db do
			local defaultType = type(plugin.defaultDB[k])
			if defaultType == "nil" then
				db[k] = nil
			elseif type(v) ~= defaultType then
				db[k] = plugin.defaultDB[k]
			end
		end

		if db.outline ~= "NONE" and db.outline ~= "OUTLINE" and db.outline ~= "THICKOUTLINE" then
			db.outline = plugin.defaultDB.outline
		end
		if db.fontSize < 20 or db.fontSize > 200 then
			db.fontSize = plugin.defaultDB.fontSize
		end
		if type(db.fontColor.r) ~= "number" or db.fontColor.r < 0 or db.fontColor.r > 1
		or type(db.fontColor.g) ~= "number" or db.fontColor.g < 0 or db.fontColor.g > 1
		or type(db.fontColor.b) ~= "number" or db.fontColor.b < 0 or db.fontColor.b > 1 then
			db.fontColor = plugin.defaultDB.fontColor
		end
		if db.countdownTime < 3 or db.countdownTime > 10 then
			db.countdownTime = plugin.defaultDB.countdownTime
		end

		UpdateFont()
		countdownAnchor:RefixPosition()

		-- Reset invalid voice selections
		if not BigWigsAPI:HasCountdown(db.voice) then
			db.voice = defaultVoice
		end
		for boss, tbl in next, db.bossCountdowns do
			for ability, chosenVoice in next, tbl do
				if not BigWigsAPI:HasCountdown(chosenVoice) then
					db.bossCountdowns[boss][ability] = nil
				end
			end
		end
		-- XXX temp 9.0.2
		oldPlugin.db:ResetProfile(nil, true) -- no callbacks
	end

	function plugin:OnPluginEnable()
		self:RegisterMessage("BigWigs_StartCountdown")
		self:RegisterMessage("BigWigs_StopCountdown")
		self:RegisterMessage("BigWigs_OnBossDisable")
		self:RegisterMessage("BigWigs_OnBossWipe", "BigWigs_OnBossDisable")
		self:RegisterMessage("BigWigs_ProfileUpdate", updateProfile)
		self:RegisterMessage("BigWigs_StartConfigureMode", showAnchors)
		self:RegisterMessage("BigWigs_StopConfigureMode", hideAnchors)
		updateProfile()
		createOptions()
	end
end

-------------------------------------------------------------------------------
-- Event Handlers
--

local latestCountdown = nil
do
	countdownFrame = CreateFrame("Frame", nil, UIParent)
	countdownFrame:SetFrameStrata("FULLSCREEN_DIALOG")
	if countdownFrame.SetFixedFrameStrata then
		countdownFrame:SetFixedFrameStrata(true)
		countdownFrame:SetFixedFrameLevel(true)
	end
	countdownFrame:SetFrameLevel(20) -- Behind GUI (level 100)
	countdownFrame:SetPoint("CENTER", countdownAnchor, "CENTER")
	countdownFrame:SetWidth(80)
	countdownFrame:SetHeight(80)
	countdownFrame:Hide()

	countdownText = countdownFrame:CreateFontString()
	countdownText:SetPoint("CENTER", countdownFrame, "CENTER")

	local updater = countdownFrame:CreateAnimationGroup()
	updater:SetScript("OnFinished", function()
		if inConfigMode then
			countdownText:SetText("5")
		else
			countdownFrame:Hide()
		end
	end)
	local anim = updater:CreateAnimation("Alpha")
	anim:SetFromAlpha(1)
	anim:SetToAlpha(0)
	anim:SetDuration(1)
	anim:SetStartDelay(1.1)

	function plugin:SetText(text, timer)
		latestCountdown = timer
		countdownText:SetText(text)
		updater:Stop()
		countdownFrame:Show()
		updater:Play()
	end
end

do
	local timers = {}
	function plugin:BigWigs_StartCountdown(_, module, key, text, time, customVoice, customStart, audioOnly)
		if module and time > 1.3 then
			self:BigWigs_StopCountdown(nil, module, text)
			if not timers[module] then
				timers[module] = {}
			end
			local count = customStart or self.db.profile.countdownTime
			while count >= time do
				count = count - 1
			end
			local cancelTimer = {false}
			timers[module][text] = cancelTimer

			local voice = customVoice or plugin.db.profile.bossCountdowns[module.name] and plugin.db.profile.bossCountdowns[module.name][key] or plugin.db.profile.voice
			local function printTime()
				if not cancelTimer[1] then
					if not audioOnly and plugin.db.profile.textEnabled then
						plugin:SetText(count, cancelTimer)
					end
					local sound = BigWigsAPI:GetCountdownSound(voice, count)
					if sound then
						PlaySoundFile(sound, "Master")
					end
					count = count - 1
				end
			end
			local startOffset = count + 0.3
			for i = 1.3, startOffset do
				self:SimpleTimer(printTime, time-i)
			end
		end
	end
	function plugin:BigWigs_StopCountdown(_, module, text)
		local moduleTimers = timers[module]
		if moduleTimers and moduleTimers[text] then
			moduleTimers[text][1] = true
			if latestCountdown == moduleTimers[text] then
				self:SetText("") -- Only clear the text if the cancelled countdown was the last to display something
			end
		end
	end
	function plugin:BigWigs_OnBossDisable(_, module)
		if timers[module] then
			for _, timer in next, timers[module] do
				timer[1] = true
				if latestCountdown == timer then
					self:SetText("") -- Only clear the text if the cancelled countdown was the last to display something
				end
			end
			timers[module] = nil
		end
	end
end

function plugin:TestCountdown()
	self:SendMessage("BigWigs_StartCountdown", self, nil, "test countdown", self.db.profile.countdownTime + 0.5)
end
