local AddName, Ns = ...

------------------------------
--		Options Equates		--
------------------------------
local Damage, Percent, Live = 1, 1, 1
local DamageTaken, Rating, Static = 2, 2, 2
local Both = 3
-- SpeedBar droplist equates (match ListData.SpeedBar values)
local Auto, SteadyFlying, Skyriding, OnFoot, Custom = 1, 2, 3, 4, 5

------------------------------
--	  Diminishing Returns	--
------------------------------
-- Secondary stat DR brackets (% from rating) introduced in Shadowlands
-- Unrolled for OnUpdate performance — no loops
-- Brackets: 0-30 = 100%, 30-39 = 90%, 39-47 = 80%, 47-54 = 70%, 54-66 = 60%, 66+ = 50%

local function ReverseDR(ratingBonus)
	if ratingBonus <= 0 then return 0 end
	if ratingBonus <= 30 then return ratingBonus end
	if ratingBonus <= 39 then return 30 + (ratingBonus - 30) / 0.9 end
	if ratingBonus <= 47 then return 30 + 9 / 0.9 + (ratingBonus - 39) / 0.8 end
	if ratingBonus <= 54 then return 30 + 9 / 0.9 + 8 / 0.8 + (ratingBonus - 47) / 0.7 end
	if ratingBonus <= 66 then return 30 + 9 / 0.9 + 8 / 0.8 + 7 / 0.7 + (ratingBonus - 54) / 0.6 end
	return 30 + 9 / 0.9 + 8 / 0.8 + 7 / 0.7 + 12 / 0.6 + (ratingBonus - 66) / 0.5
end

local function GetDRColor(ratingBonus)
	if ratingBonus <= 30 then return "|cff71ffc9" end
	if ratingBonus <= 39 then return "|cff66ff00" end   --  90%: green-yellow
	if ratingBonus <= 47 then return "|cffffff00" end   --  80%: yellow
	if ratingBonus <= 54 then return "|cffff8800" end   --  70%: orange
	if ratingBonus <= 66 then return "|cffff4400" end   --  60%: red-orange
	return "|cffff0000"                                  --  50%: red
end

------------------------------
--		Enhancements		--
------------------------------
-- Critical
function Ns.FunctionList.CritChance(HUD, data, options, ...)

	local EB = options.Percent_Rating
	local totalCrit, rating = 0, 0
	local returnText, decimals, statusbar
	local critChance = GetCritChance()
	local critSpellChance = GetSpellCritChance()
	local critRangedChance = GetRangedCritChance()
	local meleeRating, rangedRating, spellRating = GetCombatRating(CR_CRIT_MELEE), GetCombatRating(CR_CRIT_RANGED), GetCombatRating(CR_CRIT_SPELL)
	totalCrit = max(critChance, critSpellChance, critRangedChance)
	local baseCrit = totalCrit
	rating = max(meleeRating, rangedRating, spellRating)
	local capColor, endColor, rateColor = "", "", ""

	totalCrit = totalCrit + Ns.combustion + Ns.betweenEyes + Ns.overflowEnergy

	if options.Display_Basic then totalCrit = baseCrit end

	-- DR calculations
	local drLoss, drColor = 0, ""
	if options.DR_Display or options.DR_Gradient then
		local ratingBonus = max(GetCombatRatingBonus(CR_CRIT_MELEE), GetCombatRatingBonus(CR_CRIT_RANGED), GetCombatRatingBonus(CR_CRIT_SPELL))
		local noDRBonus = ReverseDR(ratingBonus)
		drLoss = noDRBonus - ratingBonus
		if options.DR_Gradient then drColor = GetDRColor(ratingBonus) end
	end

	decimals = Ns.DecimalFormat(options.Decimals_Two, "%%")

	if totalCrit >= options.Cap_Percent then capColor = Ns.greenText end
	if capColor ~= "" then endColor = "|r" end

	if rating >= options.Cap_Rating then rateColor = Ns.greenText end

	rating = BreakUpLargeNumbers(rating)

	if options.Cap_Percent == 0 then capColor = "" end
	if options.Cap_Rating == 0 then rateColor = "" end

	if drColor ~= "" and capColor == "" then capColor = drColor; endColor = "|r" end

	if Ns.Band(EB, Percent) then returnText = capColor .. (decimals):format(totalCrit); statusbar = totalCrit end
	if Ns.Band(EB, Rating) then returnText = rateColor .. rating; statusbar = rating end
	if Ns.Band(EB, Both) then returnText = capColor .. (decimals):format(totalCrit) .. endColor .. "/" .. rateColor .. rating .. endColor; statusbar = totalCrit end

	if options.DR_Display and drLoss > 0 and Ns.Band(EB, Percent) then
		returnText = returnText .. endColor .. " (" .. Ns.redText .. "-" .. (decimals):format(drLoss) .. "|r)"
	end

	HUD:UpdateText(data, returnText)
	HUD:UpdateBar(data, statusbar)
end

-- Haste
function Ns.FunctionList.Haste(HUD, data, options, ...)

	local EB = options.Percent_Rating
	local haste = GetHaste()
	local rating = GetCombatRating(CR_HASTE_MELEE)
	local returnText, decimals, statusbar
	local capColor, endColor, rateColor = "", "", ""

	-- DR calculations
	local drLoss, drColor = 0, ""
	if options.DR_Display or options.DR_Gradient then
		local ratingBonus = GetCombatRatingBonus(CR_HASTE_MELEE)
		local noDRBonus = ReverseDR(ratingBonus)
		drLoss = noDRBonus - ratingBonus
		if options.DR_Gradient then drColor = GetDRColor(ratingBonus) end
	end

	decimals = Ns.DecimalFormat(options.Decimals_Two, "%%")

	if haste >= options.Cap_Percent then capColor = Ns.greenText end
	if capColor ~= "" then endColor = "|r" end

	if rating >= options.Cap_Rating then rateColor = Ns.greenText end

	if options.Cap_Percent == 0 then capColor = "" end
	if options.Cap_Rating == 0 then rateColor = "" end

	if drColor ~= "" and capColor == "" then capColor = drColor; endColor = "|r" end

	rating = BreakUpLargeNumbers(rating)

	if Ns.Band(EB, Percent) then returnText = capColor .. (decimals):format(haste); statusbar = haste end
	if Ns.Band(EB, Rating) then returnText = rateColor .. rating; statusbar = rating end
	if Ns.Band(EB, Both) then returnText = capColor .. (decimals):format(haste) .. endColor .. "/" .. rateColor .. rating .. endColor; statusbar = haste end

	if options.DR_Display and drLoss > 0 and Ns.Band(EB, Percent) then
		returnText = returnText .. endColor .. " (" .. Ns.redText .. "-" .. (decimals):format(drLoss) .. "|r)"
	end

	HUD:UpdateText(data, returnText)
	HUD:UpdateBar(data, statusbar)
end

-- Mastery
function Ns.FunctionList.Mastery(HUD, data, options, ...)

	local EB = options.Percent_Rating
	local mastery, coefficient = GetMasteryEffect()
	local rating = GetCombatRating(CR_MASTERY)
	local returnText, decimals, statusbar
	local capColor, endColor, rateColor = "", "", ""

	if options.Display_Basic then mastery = mastery end

	-- DR calculations
	local drLoss, drColor = 0, ""
	if options.DR_Display or options.DR_Gradient then
		local ratingBonus = GetCombatRatingBonus(CR_MASTERY)
		local noDRBonus = ReverseDR(ratingBonus)
		local rawLoss = noDRBonus - ratingBonus
		drLoss = rawLoss * (coefficient or 1)
		if options.DR_Gradient then drColor = GetDRColor(ratingBonus) end
	end

	decimals = Ns.DecimalFormat(options.Decimals_Two, "%%")

	if mastery >= options.Cap_Percent then capColor = Ns.greenText end
	if capColor ~= "" then endColor = "|r" end

	if rating >= options.Cap_Rating then rateColor = Ns.greenText end

	if options.Cap_Percent == 0 then capColor = "" end
	if options.Cap_Rating == 0 then rateColor = "" end

	if drColor ~= "" and capColor == "" then capColor = drColor; endColor = "|r" end

	rating = BreakUpLargeNumbers(rating)

	if Ns.Band(EB, Percent) then returnText = capColor .. (decimals):format(mastery); statusbar = mastery end
	if Ns.Band(EB, Rating) then returnText = rateColor .. rating; statusbar = rating end
	if Ns.Band(EB, Both) then returnText = capColor .. (decimals):format(mastery) .. endColor .. "/" .. rateColor .. rating .. endColor; statusbar = mastery end

	if options.DR_Display and drLoss > 0 and Ns.Band(EB, Percent) then
		returnText = returnText .. endColor .. " (" .. Ns.redText .. "-" .. (decimals):format(drLoss) .. "|r)"
	end

	HUD:UpdateText(data, returnText)
	HUD:UpdateBar(data, statusbar)
end

-- Versatility
function Ns.FunctionList.Versatility(HUD, data, options, ...)

	local EB = options.Damage_Taken
	local versRatingBonus = GetCombatRatingBonus(29)
	local dmg = GetVersatilityBonus(29) + versRatingBonus
	local mitigate = GetVersatilityBonus(31) + GetCombatRatingBonus(31)
	local rating = ""
	local returnText, decimals, statusbar
	local capColor, endColor = "", ""

	-- DR calculations
	local dmgDRLoss, mitDRLoss, drColor = 0, 0, ""
	if options.DR_Display or options.DR_Gradient then
		local noDRBonus = ReverseDR(versRatingBonus)
		dmgDRLoss = noDRBonus - versRatingBonus
		mitDRLoss = dmgDRLoss / 2
		if options.DR_Gradient then drColor = GetDRColor(versRatingBonus) end
	end

	if options.Display_Rating then 
		rating = GetCombatRating(29)
		rating = BreakUpLargeNumbers(rating)
		rating = " (" .. rating .. ")"
	end

	decimals = Ns.DecimalFormat(options.Decimals_Two, "%%")

	if dmg >= options.Cap_Percent then capColor = Ns.greenText end
	if capColor ~= "" then endColor = "|r" end

	if options.Cap_Percent == 0 then capColor = "" end

	if drColor ~= "" and capColor == "" then capColor = drColor; endColor = "|r" end

	if Ns.Band(EB, Damage) then returnText = capColor .. (decimals):format(dmg) .. endColor .. rating; statusbar = dmg end
	if Ns.Band(EB, DamageTaken) then returnText = capColor .. (decimals):format(mitigate) .. endColor .. rating; statusbar = mitigate end
	if Ns.Band(EB, Both) then returnText = capColor .. (decimals):format(dmg) .. endColor .. "/" .. (decimals):format(mitigate) .. rating; statusbar = dmg end

	if options.DR_Display and dmgDRLoss > 0 then
		local lossVal
		if Ns.Band(EB, Both) then lossVal = dmgDRLoss
		elseif Ns.Band(EB, Damage) then lossVal = dmgDRLoss
		elseif Ns.Band(EB, DamageTaken) then lossVal = mitDRLoss
		end
		if lossVal then
			returnText = returnText .. " (" .. Ns.redText .. "-" .. (decimals):format(lossVal) .. "|r)"
		end
	end

	HUD:UpdateText(data, returnText)
	HUD:UpdateBar(data, statusbar)
end

-- Avoidance
function Ns.FunctionList.Avoidance(HUD, data, options, ...)

	local EB = options.Percent_Rating
	local avoidance = GetAvoidance()
	local rating = GetCombatRating(CR_AVOIDANCE)
	local returnText, decimals, statusbar
	local capColor, endColor, rateColor = "", "", ""

	avoidance = avoidance + Ns.martial

	if options.Display_Basic then avoidance = avoidance end

	decimals = Ns.DecimalFormat(options.Decimals_Two, "%%")

	if avoidance >= options.Cap_Percent then capColor = Ns.greenText end
	if capColor ~= "" then endColor = "|r" end

	if rating >= options.Cap_Rating then rateColor = Ns.greenText end

	if options.Cap_Percent == 0 then capColor = "" end
	if options.Cap_Rating == 0 then rateColor = "" end

	rating = BreakUpLargeNumbers(rating)

	if Ns.Band(EB, Percent) then returnText = capColor .. (decimals):format(avoidance); statusbar = avoidance end
	if Ns.Band(EB, Rating) then returnText = capColor .. rating; statusbar = rating end
	if Ns.Band(EB, Both) then returnText = capColor .. (decimals):format(avoidance) .. endColor .. "/" .. rateColor .. rating .. endColor; statusbar = avoidance end

	HUD:UpdateText(data, returnText)
	HUD:UpdateBar(data, statusbar)
end

-- Leech
function Ns.FunctionList.Leech(HUD, data, options, ...)

	local EB = options.Percent_Rating
	local leech = GetLifesteal()
	local rating = GetCombatRating(CR_LIFESTEAL)
	local returnText, decimals, statusbar
	local capColor, endColor, rateColor = "", "", ""

	decimals = Ns.DecimalFormat(options.Decimals_Two, "%%")

	if leech >= options.Cap_Percent then capColor = Ns.greenText end
	if capColor ~= "" then endColor = "|r" end

	if rating >= options.Cap_Rating then rateColor = Ns.greenText end

	if options.Cap_Percent == 0 then capColor = "" end
	if options.Cap_Rating == 0 then rateColor = "" end

	rating = BreakUpLargeNumbers(rating)

	if Ns.Band(EB, Percent) then returnText = capColor .. (decimals):format(leech); statusbar = leech end
	if Ns.Band(EB, Rating) then returnText = rateColor .. rating; statusbar = rating end
	if Ns.Band(EB, Both) then returnText = capColor .. (decimals):format(leech) .. endColor .. "/" .. rateColor .. rating .. endColor; statusbar = leech end

	HUD:UpdateText(data, returnText)
	HUD:UpdateBar(data, statusbar)
end

-- Speed
function Ns.FunctionList.Speed(HUD, data, options, ...)

	local EB = options.Speed_Static
	local speedColor, staticColor, endColor = "", "", ""
	local vehicleSpeed = GetUnitSpeed("vehicle")
	local fullSpeed, runSpeed, flightSpeed, swimSpeed = GetUnitSpeed("player")
	local isGliding, canGlide, forwardSpeed = C_PlayerInfo.GetGlidingInfo()
	local staticSpeed
	local returnText, decimals, statusbar

	if IsFlying() then staticSpeed = flightSpeed
	elseif IsSwimming() then staticSpeed = swimSpeed
	elseif vehicleSpeed > 0 then staticSpeed = vehicleSpeed
	elseif UnitOnTaxi("player") then staticSpeed = fullSpeed
	else staticSpeed = runSpeed end

	if fullSpeed == 0 and vehicleSpeed > 0 then fullSpeed = vehicleSpeed end

	fullSpeed = fullSpeed / 0.07
	staticSpeed = staticSpeed / 0.07

	if canGlide then
		local base = isGliding and forwardSpeed or GetUnitSpeed("player")
		local movespeed = Round(base / BASE_MOVEMENT_SPEED * 100)
		fullSpeed = movespeed
		staticSpeed = movespeed
	end

	if fullSpeed == 0 or fullSpeed == 100 then speedColor = ""
	elseif fullSpeed <= 99 then speedColor = Ns.redText
	elseif fullSpeed > 100 then speedColor = Ns.greenText end

	if staticSpeed == 0 or staticSpeed == 100 then staticColor = ""
	elseif staticSpeed <= 99 then staticColor = Ns.redText
	elseif staticSpeed > 100 then staticColor = Ns.greenText end

	if speedColor ~= "" then endColor = "|r" end

	decimals = Ns.DecimalFormat(options.Decimals_Default, "%%")

	if Ns.Band(EB, Live) then
		returnText = speedColor .. (decimals):format(fullSpeed)
		statusbar = fullSpeed
	end
	if Ns.Band(EB, Static) then
		returnText = staticColor .. (decimals):format(staticSpeed)
		statusbar = staticSpeed
	end
	if Ns.Band(EB, Both) then
		returnText = speedColor .. (decimals):format(fullSpeed) .. endColor .. " (" .. staticColor .. (decimals):format(staticSpeed) .. ")"
		statusbar = fullSpeed
	end

	HUD:UpdateText(data, returnText)
	-- SpeedBar mode determines the bar's max value
	local mode = options.SpeedBar or Auto
	local barMax = 107
	if mode == SteadyFlying then barMax = 570
	elseif mode == Skyriding then barMax = 1220
	elseif mode == OnFoot then barMax = 110
	elseif mode == Custom and options.BarMaxCustom and options.BarMaxCustom > 0 then
		barMax = options.BarMaxCustom
	elseif mode == Auto then
		barMax = 107
		if IsFlying() then
			barMax = 570
			if canGlide then barMax = 1220 end
		end
	end
	HUD:UpdateBar(data, statusbar, barMax)
end