Quantcast
local L = LibStub("AceLocale-3.0"):GetLocale("Skada", false)

local Skada = Skada

local media = LibStub("LibSharedMedia-3.0")

-- This mode is a bit special.
local mod = Skada:NewModule(L["Threat"])

local opts = {
	options = {
		type="group",
		name=L["Threat"],
		args={
			warnings = {
				type="group",
				name=L["Threat warning"],
				inline=true,
				order=1,
				args={

					flash = {
						type="toggle",
						name=L["Flash screen"],
						desc=L["This will cause the screen to flash as a threat warning."],
						get=function() return Skada.db.profile.modules.threatflash end,
						set=function(self, val) Skada.db.profile.modules.threatflash = val end,
						order=2,
					},

					shake = {
						type="toggle",
						name=L["Shake screen"],
						desc=L["This will cause the screen to shake as a threat warning."],
						get=function() return Skada.db.profile.modules.threatshake end,
						set=function(self, val) Skada.db.profile.modules.threatshake = not Skada.db.profile.modules.threatshake end,
						order=3,
					},

					playsound = {
						type="toggle",
						name=L["Play sound"],
						desc=L["This will play a sound as a threat warning."],
						get=function() return Skada.db.profile.modules.threatsound end,
						set=function(self, val) Skada.db.profile.modules.threatsound = not Skada.db.profile.modules.threatsound end,
						order=4,
					},

					sound = {
				         type = 'select',
				         dialogControl = 'LSM30_Sound',
				         name = L["Threat sound"],
				         desc = L["The sound that will be played when your threat percentage reaches a certain point."],
				         values = AceGUIWidgetLSMlists.sound,
				         get = function() return Skada.db.profile.modules.threatsoundname end,
				         set = function(self,val) Skada.db.profile.modules.threatsoundname = val end,
						order=5,
				    },

					treshold = {
				         type = 'range',
				         name = L["Threat threshold"],
				         desc = L["When your threat reaches this level, relative to tank, warnings are shown."],
				         min=0,
				         max=130,
				         step=1,
				         get = function() return Skada.db.profile.modules.threattreshold end,
				         set = function(self,val) Skada.db.profile.modules.threattreshold = val end,
						order=6,
				    },

					notankwarnings = {
						type = "toggle",
						name = L["Do not warn while tanking"],
						get = function() return Skada.db.profile.modules.notankwarnings end,
						set = function() Skada.db.profile.modules.notankwarnings = not Skada.db.profile.modules.notankwarnings end,
						order=2,
					},

				},
			},

			rawthreat = {
				type = "toggle",
				name = L["Show raw threat"],
				desc = L["Shows raw threat percentage relative to tank instead of modified for range."],
				get = function() return Skada.db.profile.modules.threatraw end,
				set = function() Skada.db.profile.modules.threatraw = not Skada.db.profile.modules.threatraw end,
				order=2,
			},

			focustarget = {
				type = "toggle",
				name = L["Use focus target"],
				desc = L["Shows threat on focus target, or focus target's target, when available."],
				get = function() return Skada.db.profile.modules.threatfocustarget end,
				set = function() Skada.db.profile.modules.threatfocustarget = not Skada.db.profile.modules.threatfocustarget end,
				order=2,
			},

		},
	}
}

function mod:OnInitialize()
	-- Add our options.
	table.insert(Skada.options.plugins, opts)
end

function mod:OnEnable()
	mod.metadata = {showspots = 1, wipestale = 1, columns = {Threat = true, TPS = false, Percent = true}}

	-- Add our feed.
	Skada:AddFeed(L["Threat: Personal Threat"], function()
								if Skada.current and UnitExists("target") then
									local isTanking, status, threatpct, rawthreatpct, threatvalue = UnitDetailedThreatSituation("player", "target")
									if threatpct then
										return ("%02.1f%%"):format(threatpct)
									end
								end
							end)

	-- Enable us
	Skada:AddMode(self)
end

function mod:OnDisable()
	Skada:RemoveMode(self)
end

local maxthreat = 0

-- Used as index for dataset.
local nr = 1

-- Max threat value.
local max = 0

local function add_to_threattable(win, name, target)
	if name and UnitExists(name) then
		local isTanking, status, threatpct, rawthreatpct, threatvalue = UnitDetailedThreatSituation(name, target)

		if Skada.db.profile.modules.threatraw then
			if threatvalue then

				local d = win.dataset[nr] or {}
				win.dataset[nr] = d
				d.label = name
				d.class = select(2, UnitClass(name))
				d.id = name
				d.threat = threatvalue
				d.isTanking = isTanking
				if threatvalue < 0 then
					-- Show real threat.
					d.value = threatvalue + 410065408
					d.threat = threatvalue + 410065408
				else
					d.value = threatvalue
				end

				if threatvalue > maxthreat then
					maxthreat = threatvalue
				end
			end
		else
			if threatpct then

				local d = win.dataset[nr] or {}
				win.dataset[nr] = d
				d.label = name
				d.class = select(2, UnitClass(name))
				d.id = name
				d.value = threatpct
				d.isTanking = isTanking
				d.threat = threatvalue

			end
		end

		nr = nr + 1
	end
end

local function format_threatvalue(value)
	if value == nil then
		return "0"
	elseif value >= 100000 then
		return ("%2.1fk"):format(value / 100000)
	else
		return ("%d"):format(value / 100)
	end
end

local function getTPS(threatvalue)
	if Skada.current then
		local totaltime = time() - Skada.current.starttime

		return format_threatvalue(threatvalue / math.max(1,totaltime))
	else
		-- If we are not in combat and have no active set, we are screwed, since we have no time reference.
		return "0"
	end
end

local last_warn = time()

function mod:Update(win, set)
	local target = nil
	if UnitExists("target") and not UnitIsFriend("player", "target") then
		target = "target"
	elseif Skada.db.profile.modules.threatfocustarget and UnitExists("focus") and not UnitIsFriend("player", "focus") then
		target = "focus"
	elseif Skada.db.profile.modules.threatfocustarget and UnitExists("focustarget") and not UnitIsFriend("player", "focustarget") then
		target = "focustarget"
	elseif UnitExists("target") and UnitIsFriend("player", "target") and UnitExists("targettarget") and not UnitIsFriend("player", "targettarget") then
		target = "targettarget"
	end

	if target then
		-- Set window title.
		win.metadata.title = UnitName(target)

		-- Reset our counter which we use to keep track of current index in the dataset.
		nr = 1

		-- Reset out max threat value.
		maxthreat = 0

		if GetNumRaidMembers() > 0 then
			-- We are in a raid.
			for i = 1, 40, 1 do
				local name, rank, subgroup, level, class, fileName, zone, online, isDead, role, isML = GetRaidRosterInfo(i);
				if name then
					add_to_threattable(win, name, target)

					if UnitExists("raid"..i.."pet") then
						add_to_threattable(win, select(1, UnitName("raid"..i.."pet")), target)
					end
				end
			end
		elseif GetNumPartyMembers() > 0 then
			-- We are in a party.
			for i = 1, 5, 1 do
				local name = (UnitName("party"..tostring(i)))
				if name then
					add_to_threattable(win, name, target)

					if UnitExists("party"..i.."pet") then
						add_to_threattable(win, select(1, UnitName("party"..i.."pet")), target)
					end
				end
			end

			-- Don't forget ourselves.
			add_to_threattable(win, UnitName("player"), target)

			-- Maybe we have a pet?
			if UnitExists("pet") then
				add_to_threattable(win, UnitName("pet"), target)
			end
		else
			-- We are all alone.
			add_to_threattable(win, UnitName("player"), target)

			-- Maybe we have a pet?
			if UnitExists("pet") then
				add_to_threattable(win, UnitName("pet"), target)
			end
		end

		-- If we are going by raw threat we got the max threat from above; otherwise it's always 100.
		if not Skada.db.profile.modules.threatraw then
			maxthreat = 100
		end

		win.metadata.maxvalue = maxthreat

		local we_should_warn = false

		-- We now have a a complete threat table.
		-- Now we need to add valuetext.
		for i, data in ipairs(win.dataset) do
			if data.id then

				if data.threat and data.threat > 0 then
					-- Warn if this is ourselves and we are over the treshold.
					local percent = data.value / maxthreat * 100
					if data.label == UnitName("player") then
						if Skada.db.profile.modules.threattreshold and Skada.db.profile.modules.threattreshold < percent and (not data.isTanking or not Skada.db.profile.modules.notankwarnings) then
							we_should_warn = true
						end
					end

					data.valuetext = Skada:FormatValueText(
													format_threatvalue(data.threat), self.metadata.columns.Threat,
													getTPS(data.threat), self.metadata.columns.TPS,
													string.format("%02.1f%%", percent), self.metadata.columns.Percent
												)
				else
					data.id = nil
				end

			end
		end

		-- Warn
		if we_should_warn and time() - last_warn > 2 then
			if Skada.db.profile.modules.threatflash then
				self:Flash()
			end
			if Skada.db.profile.modules.threatshake then
				self:Shake()
			end
			if Skada.db.profile.modules.threatsound then
				PlaySoundFile(media:Fetch("sound", Skada.db.profile.modules.threatsoundname))
			end

			last_warn = time()
		end
	else
		win.metadata.title = self:GetName()
	end
end

-- Shamelessly copied from Omen - thanks!
function mod:Flash()
	if not self.FlashFrame then
		local flasher = CreateFrame("Frame", "SkadaThreatFlashFrame")
		flasher:SetToplevel(true)
		flasher:SetFrameStrata("FULLSCREEN_DIALOG")
		flasher:SetAllPoints(UIParent)
		flasher:EnableMouse(false)
		flasher:Hide()
		flasher.texture = flasher:CreateTexture(nil, "BACKGROUND")
		flasher.texture:SetTexture("Interface\\FullScreenTextures\\LowHealth")
		flasher.texture:SetAllPoints(UIParent)
		flasher.texture:SetBlendMode("ADD")
		flasher:SetScript("OnShow", function(self)
			self.elapsed = 0
			self:SetAlpha(0)
		end)
		flasher:SetScript("OnUpdate", function(self, elapsed)
			elapsed = self.elapsed + elapsed
			if elapsed < 2.6 then
				local alpha = elapsed % 1.3
				if alpha < 0.15 then
					self:SetAlpha(alpha / 0.15)
				elseif alpha < 0.9 then
					self:SetAlpha(1 - (alpha - 0.15) / 0.6)
				else
					self:SetAlpha(0)
				end
			else
				self:Hide()
			end
			self.elapsed = elapsed
		end)
		self.FlashFrame = flasher
	end

	self.FlashFrame:Show()
end

-- Shamelessly copied from Omen (which copied from BigWigs) - thanks!
function mod:Shake()
	local shaker = self.ShakerFrame
	if not shaker then
		shaker = CreateFrame("Frame", "SkadaThreatShaker", UIParent)
		shaker:Hide()
		shaker:SetScript("OnUpdate", function(self, elapsed)
			elapsed = self.elapsed + elapsed
			local x, y = 0, 0 -- Resets to original position if we're supposed to stop.
			if elapsed >= 0.8 then
				self:Hide()
			else
				x, y = random(-8, 8), random(-8, 8)
			end
			if WorldFrame:IsProtected() and InCombatLockdown() then
				if not shaker.fail then
					shaker.fail = true
				end
				self:Hide()
			else
				WorldFrame:ClearAllPoints()
				for i = 1, #self.originalPoints do
					local v = self.originalPoints[i]
					WorldFrame:SetPoint(v[1], v[2], v[3], v[4] + x, v[5] + y)
				end
			end
			self.elapsed = elapsed
		end)
		shaker:SetScript("OnShow", function(self)
			-- Store old worldframe positions, we need them all, people have frame modifiers for it
			if not self.originalPoints then
				self.originalPoints = {}
				for i = 1, WorldFrame:GetNumPoints() do
					tinsert(self.originalPoints, {WorldFrame:GetPoint(i)})
				end
			end
			self.elapsed = 0
		end)
		self.ShakerFrame = shaker
	end

	shaker:Show()
end