--- Buff Information for Micro Spheres
--
local BuffData = {}
local AddOnName, nsVars = ...
local ss = nsVars.SS

local UnitStagger, UnitHealthMax, FindAuraByName, FindAuraById = UnitStagger, UnitHealthMax, AuraUtil.FindAuraByName, ss.APIUtils.FindAuraById
local string, GetTime, tonumber, strlower, select = string, GetTime, tonumber, strlower, select

local timeBuffer = 0.05
local CRASH_LIGHTNING_SPELL_ID = 187878
local CHAIN_LIGHTNING_SPELL_ID = 333964

--- Provides global cooldown information.
--- @param spell string The spell name so that it can be returned if necessary.
--- @return string, number, number, number, number The current progression values.
local function getGCD(spell)
  local duration = ss.gcdWatch.max and ss.gcdWatch.max or 1
  local expires = (ss.gcdWatch.start and ss.gcdWatch.max) and (ss.gcdWatch.start + ss.gcdWatch.max) or 1

  if ss.gcdWatch.start and GetTime() >= expires - timeBuffer then
    ss.gcdWatch.start = nil
  end

  return spell, (duration ~= expires) and 1 or 0, 1, duration, expires
end

--- Provides Incater's Flow information.
--- @param buff string The buff name so that it can be returned if necessary.
--- @return string, number, number, number, number The current progression values.
--- @see UnitIncantersFlow(). (IncantersFlowTracker)
local function getIncantersFlowMinMax(buff)
  local cur, max, prog = select(3, UnitIncantersFlow())
  return buff, (cur and 1 or 0), 1, (max or 1), (prog and (GetTime() + (max * prog) + timeBuffer) or 1)
end

--- Provides buff information.
--- @param buff string The buff identifier.
--- @return string, number, number, number, number The current progression values.
--- @see FindAuraByName().
local function getMinMax(buff)
  local duration, expires = select(5, FindAuraByName(buff, "player"))
  return buff, (duration and 1 or 0), 1, (duration or 1), (expires or 1)
end

--- Provides buff information for the Crash Lightning buffs.
--- There are two buffs with the same name so the spellId must be used.
--- FindAuraById() is based on AuraUtil.FindAuraByName().
--- @param buff string The buff identifier.
--- @return string, number, number, number, number The current progression values.
--- @see FindAuraByName().
local function getCrashLightningMinMax(buff)
  local duration, expires = select(5, FindAuraById(
      buff == 'Crash Lightning' and CRASH_LIGHTNING_SPELL_ID or CHAIN_LIGHTNING_SPELL_ID,
      "player"))

  return buff, (duration and 1 or 0), 1, (duration or 1), (expires or 1)
end

--- Provides buff stack information.
--- @param spell string The spell identifier.
--- @return string, number, number, number, number The current progression values.
--- @see FindAuraByName().
local function getStackingMinMax(spell)
  local buffName = string.gsub(spell, "%d", "")
  local index = strlower(string.gsub(spell, "[ :'%(%)%d]", ""))

  local stacks, _, duration, expires = select(3, FindAuraByName(buffName, "player"))

  if stacks then
    if stacks == 0 then
      stacks = 1
    end

    local buffStack = string.gsub(spell, "[^%d]", "")
    buffStack = (buffStack and buffStack ~= "") and tonumber(buffStack) or 1

    duration = (stacks >= buffStack) and duration or nil
    expires = (stacks >= buffStack) and expires or nil
  end

  return index, (duration and 1 or 0), 1, (duration or 1), (expires or 1)
end

--- Provides buff stack information.
--- @param spell string The spell identifier.
--- @return string, number, number, number, number The current progression values.
--- @see FindAuraByName().
local function getStacks(spell)
  local buffName = string.gsub(spell, "%d", "")
  local index = strlower(string.gsub(spell, "[ :'%(%)%d]", ""))
  local stacks, _, duration = select(3, FindAuraByName(buffName, "player"))

  if stacks then
    if stacks == 0 then
      stacks = 1
    end

    local buffStack = string.gsub(spell, "[^%d]", "")
    buffStack = (buffStack and buffStack ~= "") and tonumber(buffStack) or 1

    duration = (stacks >= buffStack) and duration or nil
  end

  return index, (duration and 1 or 0), 1, 1, (GetTime() + 1)
end

--- Provides stagger information.
--- @return string, number, number The stagger values.
local function getStaggerMinMax()
  local min, max = UnitStagger("player"), UnitHealthMax("player")
  return "Stagger", min or 0, max
end

--- Provides shield block information.
--- @param buff string The buff identifier.
--- @return string, number, number, number, number The current progression values.
--- @see FindAuraByName().
local function getShieldBlockMinMax(buff)
  local duration, expires = select(5, FindAuraByName(buff, "player"))
  duration = duration or 1
  expires = expires or 1

  if duration == 0 and expires == 0 then
    -- Need to find the "real" data. With the T19 2 piece bonus, there are two "Shield Block" buffs.
    for i = 0, 20 do
      -- Possible memory leak?
      local buffDetails = { FindAuraByName(i, "player") }

      if buffDetails and buffDetails[1] == "Shield Block" and buffDetails[6] > 0 then
        -- Real Shield Block.
        return buff, (buffDetails[5] and 1 or 0), 1, (buffDetails[5] or 1), (buffDetails[6] or 1)
      end
    end
  end

  return buff, (duration ~= expires and 1 or 0), 1, duration, expires
end

--- Provides pet buff information.
--- @param buff string The buff identifier.
--- @return string, number, number, number, number The current progression values.
--- @see FindAuraByName().
local function getPetBuffMinMax(buff)
  local duration, expires = select(5, FindAuraByName(buff, "pet"))
  return buff, (duration and 1 or 0), 1, (duration or 1), (expires or 1)
end

--- Provides resource information for the Shadow Priest's Voidform buff.
--- While active, the buff gains increasing stacks over time, with a maximum amount of 100.
--- This function uses the current stack amount and provides a value representing progression within a portion of the limit.
--- This function returns information representing the incremental progress of the stacks using a fraction of the maximum value.
--- The following values may be used to retrieve the full progression:
---   Stacks 1 to 25:   "Voidform (1-25)"
---   Stacks 26 to 50:  "Voidform (26-50)"
---   Stacks 51 to 75:  "Voidform (51-75)"
---   Stacks 76 to 100: "Voidform (76-100)"
--- @param spell string The spell identifier.
--- @return string, number, number, number, number The current progression values.
--- @see FindAuraByName().
local function getVoidformMinMax(spell)
  local buffName = "Voidform"
  local stacks = select(3, FindAuraByName(buffName, "player"))

  if stacks then
    local buffStack = string.gsub(spell, "[^%d]", "")
    buffStack = (buffStack and buffStack ~= "") and tonumber(buffStack) or 0

    local stackAdjust = (buffStack == 2650) and 25 or ((buffStack == 5175) and 50 or ((buffStack == 76100) and 75 or 0))
    stacks = stacks - stackAdjust

    stacks = (stacks > 25) and 25 or stacks
    stacks = (stacks > 0) and stacks or nil
  end

  return buffName, (stacks and 1 or 0), 1, (stacks and 25 or 1), (stacks and (GetTime() + stacks + timeBuffer) or 1)
end

--- Provides resource information for the Shadow Priest's Lingering Insanity buff.
--- This buff is the same as the Shadow Priest's Voidform, but decreases in stacks over time.
--- This function follows the same approach used for Voidform.
--- The following values may be used to retrieve the full progression:
---   Stacks 1 to 25:   "Lingering Insanity (1-25)"
---   Stacks 26 to 50:  "Lingering Insanity (26-50)"
---   Stacks 51 to 75:  "Lingering Insanity (51-75)"
---   Stacks 76 to 100: "Lingering Insanity (76-100)"
--- @param spell string The spell identifier.
--- @return string, number, number, number, number The current progression values.
--- @see FindAuraByName().
local function getLingeringInsanityMinMax(spell)
  local buffName = "Lingering Insanity"
  local stacks = select(3, FindAuraByName(buffName, "player"))

  if stacks then
    local buffStack = string.gsub(spell, "[^%d]", "")
    buffStack = (buffStack and buffStack ~= "") and tonumber(buffStack) or 0

    local stackAdjust = (buffStack == 2650) and 25 or ((buffStack == 5175) and 50 or ((buffStack == 76100) and 75 or 0))
    stacks = stacks - stackAdjust

    stacks = (stacks > 25) and 25 or stacks
    stacks = (stacks > 0) and stacks or nil
  end

  return buffName, (stacks and 1 or 0), 1, (stacks and 25 or 1), (stacks and (GetTime() + stacks + timeBuffer) or 1)
end

BuffData.demonhunter = {
  ["metamorphosis"] = {
    "Metamorphosis",
    { r = 148, g = 105, b = 193, a = 1 },
    getMinMax = getMinMax,
    name = "Metamorphosis",
  },
  ["momentum"] = {
    "Momentum",
    { r = 0.824, g = 0.526, b = 0.157, a = 1 },
    getMinMax = getMinMax,
    name = "Momentum",
  },
  ["empowerwards"] = {
    "Empower Wards",
    { r = 0.471, g = 0.784, b = 0.294, a = 1 },
    getMinMax = getMinMax,
    name = "Empower Wards",
  },
  ["demonspikes"] = {
    "Demon Spikes",
    { r = 0.784, g = 0.725, b = 0.000, a = 1 },
    getMinMax = getMinMax,
    name = "Demon Spikes",
  },
  ["soulbarrier"] = {
    "Soul Barrier",
    { r = 0.392, g = 0.529, b = 0.137, a = 1 },
    getMinMax = getMinMax,
    name = "Soul Barrier",
  },
  ["immolationaura"] = {
    "Immolation Aura",
    { r = 0.863, g = 0.941, b = 0.275, a = 1 },
    getMinMax = getMinMax,
    name = "Immolation Aura",
  },
  ["painbringer"] = {
    "Painbringer",
    { r = 161, g = 64, b = 146, a = 1 },
    getMinMax = getMinMax,
    name = "Painbringer",
  },
  ["globalcooldown"] = {
    "Global Cooldown",
    { r = 0.482, g = 1.000, b = 0.827, a = 1 },
    getMinMax = getGCD,
    name = "Global Cooldown",
  },
}

BuffData.druid = {
  ["guardianofelune"] = {
    "Guardian of Elune",
    { r = 0.451, g = 0.039, b = 0.725, a = 1 },
    getMinMax = getMinMax,
    name = "Guardian of Elune",
  },
  ["ironfur"] = {
    "Ironfur",
    { r = 0.529, g = 0.686, b = 1.000, a = 1 },
    getMinMax = getMinMax,
    name = "Ironfur",
  },
  ["frenziedregeneration"] = {
    "Frenzied Regeneration",
    { r = 0.588, g = 0.353, b = 0.157, a = 1 },
    getMinMax = getMinMax,
    name = "Frenzied Regeneration",
  },
  ["souloftheforest"] = {
    "Soul of the Forest",
    { r = 0.196, g = 0.314, b = 0.431, a = 1 },
    getMinMax = getMinMax,
    name = "Soul of the Forest",
  },
  ["pulverize"] = {
    "Pulverize",
    { r = 0.196, g = 0.784, b = 0.784, a = 1 },
    getMinMax = getMinMax,
    name = "Pulverize",
  },
  ["tigersfury"] = {
    "Tiger's Fury",
    { r = 0.961, g = 0.804, b = 0.431, a = 1 },
    getMinMax = getMinMax,
    name = "Tiger's Fury",
  },
  ["savageroar"] = {
    "Savage Roar",
    { r = 1.000, g = 0.667, b = 0.039, a = 1 },
    getMinMax = getMinMax,
    name = "Savage Roar",
  },
  ["warriorofelune"] = {
    "Warrior of Elune",
    { r = 0.153, g = 0.216, b = 0.471, a = 1 },
    getMinMax = getStacks,
    name = "Warrior of Elune",
  },
  ["warriorofelune2"] = {
    "Warrior of Elune2",
    { r = 0.153, g = 0.216, b = 0.471, a = 1 },
    getMinMax = getStacks,
    name = "Warrior of Elune",
  },
  ["warriorofelune3"] = {
    "Warrior of Elune3",
    { r = 0.153, g = 0.216, b = 0.471, a = 1 },
    getMinMax = getStacks,
    name = "Warrior of Elune",
  },
  ["earthwarden"] = {
    "Earthwarden",
    { r = 0.471, g = 0.961, b = 0.725, a = 1 },
    getMinMax = getStacks,
    name = "Earthwarden",
  },
  ["earthwarden2"] = {
    "Earthwarden2",
    { r = 0.471, g = 0.961, b = 0.725, a = 1 },
    getMinMax = getStacks,
    name = "Earthwarden",
  },
  ["earthwarden3"] = {
    "Earthwarden3",
    { r = 0.471, g = 0.961, b = 0.725, a = 1 },
    getMinMax = getStacks,
    name = "Earthwarden",
  },
  ["clearcasting"] = {
    "Clearcasting",
    { r = 0.118, g = 0.608, b = 1.000, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Clearcasting",
  },
  ["clearcasting2"] = {
    "Clearcasting2",
    { r = 0.118, g = 0.608, b = 1.000, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Clearcasting",
  },
  ["clearcasting3"] = {
    "Clearcasting3",
    { r = 0.118, g = 0.608, b = 1.000, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Clearcasting",
  },
  ["bloodtalons"] = {
    "Bloodtalons",
    { r = 0.706, g = 0.098, b = 0.000, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Bloodtalons",
  },
  ["bloodtalons2"] = {
    "Bloodtalons2",
    { r = 0.706, g = 0.098, b = 0.000, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Bloodtalons",
  },
  ["globalcooldown"] = {
    "Global Cooldown",
    { r = 0.482, g = 1.000, b = 0.827, a = 1 },
    getMinMax = getGCD,
    name = "Global Cooldown",
  },
}

BuffData.mage = {
  ["norgannonsforesight"] = {
    "Norgannon's Foresight",
    { r = 0.000, g = 0.922, b = 1.000, a = 1 },
    getMinMax = getMinMax,
    name = "Norgannon's Foresight",
  },
  ["kaelthassultimateability"] = {
    "Kael'thas's Ultimate Ability",
    { r = 0.647, g = 0.294, b = 0.765, a = 1 },
    getMinMax = getMinMax,
    name = "Kael'thas's Ultimate Ability",
  },
  ["freneticspeed"] = {
    "Frenetic Speed",
    { r = 1.000, g = 0.784, b = 0.294, a = 1 },
    getMinMax = getMinMax,
    name = "Frenetic Speed",
  },
  ["chronoshift"] = {
    "Chrono Shift",
    { r = 0.882, g = 0.686, b = 0.451, a = 1 },
    getMinMax = getMinMax,
    name = "Chrono Shift",
  },
  ["incantersflow"] = {
    "Incanter's Flow",
    { r = 0.824, g = 0.157, b = 0.196, a = 1 },
    getMinMax = getIncantersFlowMinMax,
    name = "Incanter's Flow",
  },
  ["globalcooldown"] = {
    "Global Cooldown",
    { r = 0.482, g = 1.000, b = 0.827, a = 1 },
    getMinMax = getGCD,
    name = "Global Cooldown",
  },
}

BuffData.monk = {
  ["stagger"] = {
    "Stagger",
    ss.ClassResourceData.resourceColor("chi"),
    getMinMax = getStaggerMinMax,
    name = "Stagger",
  },
  ["hitcombo"] = {
    "Hit Combo",
    { r = 1.000, g = 0.686, b = 0.157, a = 1 },
    getMinMax = getMinMax,
    name = "Hit Combo",
  },
  ["thunderfocustea"] = {
    "Thunder Focus Tea",
    { r = 0.490, g = 0.667, b = 0.941, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Thunder Focus Tea",
  },
  ["thunderfocustea2"] = {
    "Thunder Focus Tea2",
    { r = 0.490, g = 0.667, b = 0.941, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Thunder Focus Tea",
  },
  ["lifecyclesvivify"] = {
    "Lifecycles (Vivify)",
    { r = 0.196, g = 0.824, b = 0.490, a = 1 },
    getMinMax = getMinMax,
    name = "Lifecycles (Vivify)",
  },
  ["lifecyclesenvelopingmist"] = {
    "Lifecycles (Enveloping Mist)",
    { r = 0.863, g = 0.824, b = 0.529, a = 1 },
    getMinMax = getMinMax,
    name = "Lifecycles (Enveloping Mist)",
  },
  ["ironskinbrew"] = {
    "Ironskin Brew",
    { r = 0.529, g = 1.000, b = 0.843, a = 1 },
    getMinMax = getMinMax,
    name = "Ironskin Brew",
  },
  ["elusivedance"] = {
    "Elusive Dance",
    { r = 0.392, g = 0.882, b = 0.510, a = 1 },
    getMinMax = getMinMax,
    name = "Elusive Dance",
  },
  ["globalcooldown"] = {
    "Global Cooldown",
    { r = 0.482, g = 1.000, b = 0.827, a = 1 },
    getMinMax = getGCD,
    name = "Global Cooldown",
  },
}

BuffData.shaman = {
  ["boulderfist"] = {
    "Boulderfist",
    { r = 0.588, g = 0.333, b = 0.118, a = 1 },
    getMinMax = getMinMax,
    name = "Boulderfist",
  },
  ["lightningcrash"] = {
    "Lightning Crash",
    { r = 82, g = 154, b = 206, a = 1 },
    getMinMax = getMinMax,
    name = "Lightning Crash",
  },
  ["crashlightning"] = {
    "Crash Lightning",
    { r = 0.098, g = 0.588, b = 1.000, a = 1 },
    getMinMax = getCrashLightningMinMax,
    name = "Crash Lightning",
  },
  ["chainlightning"] = {
    "Chain Lightning",
    { r = 0.098, g = 0.588, b = 1.000, a = 1 },
    getMinMax = getCrashLightningMinMax,
    name = "Chain Lightning",
  },
  ["doomwinds"] = {
    "Doom Winds",
    { r = 0.471, g = 0.882, b = 0.353, a = 1 },
    getMinMax = getMinMax,
    name = "Doom Winds",
  },
  ["windsong"] = {
    "Windsong",
    { r = 0.510, g = 0.784, b = 0.824, a = 1 },
    getMinMax = getMinMax,
    name = "Windsong",
  },
  ["landslide"] = {
    "Landslide",
    { r = 0.627, g = 0.176, b = 0.176, a = 1 },
    getMinMax = getMinMax,
    name = "Landslide",
  },
  ["elementalblastcriticalstrike"] = {
    "Elemental Blast: Critical Strike",
    { r = 0.804, g = 0.745, b = 1.000, a = 1 },
    getMinMax = getMinMax,
    name = "Elemental Blast: Critical Strike",
  },
  ["elementalblasthaste"] = {
    "Elemental Blast: Haste",
    { r = 0.647, g = 0.333, b = 0.686, a = 1 },
    getMinMax = getMinMax,
    name = "Elemental Blast: Haste",
  },
  ["elementalblastmastery"] = {
    "Elemental Blast: Mastery",
    { r = 1.000, g = 0.706, b = 0.000, a = 1 },
    getMinMax = getMinMax,
    name = "Elemental Blast: Mastery",
  },
  ["icefury"] = {
    "Icefury",
    { r = 0.682, g = 0.549, b = 1.000, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Icefury",
  },
  ["icefury2"] = {
    "Icefury2",
    { r = 0.682, g = 0.549, b = 1.000, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Icefury",
  },
  ["icefury3"] = {
    "Icefury3",
    { r = 0.682, g = 0.549, b = 1.000, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Icefury",
  },
  ["icefury4"] = {
    "Icefury4",
    { r = 0.682, g = 0.549, b = 1.000, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Icefury",
  },
  ["globalcooldown"] = {
    "Global Cooldown",
    { r = 0.482, g = 1.000, b = 0.827, a = 1 },
    getMinMax = getGCD,
    name = "Global Cooldown",
  },
}

if not ss.retailClient then
  local totems = {
    ["Fire Totem"] = 1,
    ["Earth Totem"] = 2,
    ["Water Totem"] = 3,
    ["Air Totem"] = 4,
  }

  local function getTotemMinMax(totem)
    local exists, name, startTime, duration = GetTotemInfo(totems[totem])
    local expires

    if exists then
      expires = startTime + duration
    end

    return name or "", (duration and 1 or 0), 1, (duration or 1), (expires or 1)
  end

  -- Add totems as individual buffs.
  BuffData.shaman.firetotem = {
    "Fire Totem",
    { r = 255, g = 90, b = 30, a = 1 },
    getMinMax = getTotemMinMax,
    name = "Fire Totem"
  }
  BuffData.shaman.earthtotem = {
    "Earth Totem",
    { r = 65, g = 195, b = 80, a = 1 },
    getMinMax = getTotemMinMax,
    name = "Earth Totem"
  }
  BuffData.shaman.watertotem = {
    "Water Totem",
    { r = 85, g = 240, b = 255, a = 1 },
    getMinMax = getTotemMinMax,
    name = "Water Totem"
  }
  BuffData.shaman.airtotem = {
    "Air Totem",
    { r = 130, g = 90, b = 255, a = 1 },
    getMinMax = getTotemMinMax,
    name = "Air Totem"
  }
end

BuffData.warrior = {
  ["enrage"] = {
    "Enrage",
    { r = 1.000, g = 0.215, b = 0.000, a = 1 },
    getMinMax = getMinMax,
    name = "Enrage",
  },
  ["enragedregeneration"] = {
    "Enraged Regeneration",
    { r = 0.804, g = 0.255, b = 0.137, a = 1 },
    getMinMax = getMinMax,
    name = "Enraged Regeneration",
  },
  ["meatcleaver"] = {
    "Meat Cleaver",
    { r = 0.314, g = 0.608, b = 0.765, a = 1 },
    getMinMax = getMinMax,
    name = "Meat Cleaver",
  },
  ["shieldblock"] = {
    "Shield Block",
    { r = 0.294, g = 0.549, b = 0.784, a = 1 },
    getMinMax = getShieldBlockMinMax,
    name = "Shield Block",
  },
  ["ignorepain"] = {
    "Ignore Pain",
    { r = 1.000, g = 0.647, b = 0.020, a = 1 },
    getMinMax = getMinMax,
    name = "Ignore Pain",
  },
  ["frothingberserker"] = {
    "Frothing Berserker",
    { r = 1.000, g = 0.549, b = 0.000, a = 1 },
    getMinMax = getMinMax,
    name = "Frothing Berserker",
  },
  ["dragonroar"] = {
    "Dragon Roar",
    { r = 0.392, g = 0.177, b = 0.177, a = 1 },
    getMinMax = getMinMax,
    name = "Dragon Roar",
  },
  ["bloodbath"] = {
    "Bloodbath",
    { r = 1.000, g = 0.275, b = 0.020, a = 1 },
    getMinMax = getMinMax,
    name = "Bloodbath",
  },
  ["berserkerrage"] = {
    "Berserker Rage",
    { r = 0.922, g = 0.490, b = 0.196, a = 1 },
    getMinMax = getMinMax,
    name = "Berserker Rage",
  },
  ["renewedfury"] = {
    "Renewed Fury",
    { r = 0.812, g = 0.294, b = 0.275, a = 1 },
    getMinMax = getMinMax,
    name = "Renewed Fury",
  },
  ["vengeancerevenge"] = {
    "Vengeance: Revenge",
    { r = 1.000, g = 0.706, b = 0.314, a = 1 },
    getMinMax = getMinMax,
    name = "Vengeance: Revenge",
  },
  ["vengeanceignorepain"] = {
    "Vengeance: Ignore Pain",
    { r = 1.000, g = 0.706, b = 0.314, a = 1 },
    getMinMax = getMinMax,
    name = "Vengeance: Ignore Pain",
  },
  ["juggernaut"] = {
    "Juggernaut",
    { r = 0.922, g = 0.824, b = 0.510, a = 1 },
    getMinMax = getMinMax,
    name = "Juggernaut",
  },
  ["warmachine"] = {
    "War Machine",
    { r = 1.000, g = 0.196, b = 0.216, a = 1 },
    getMinMax = getMinMax,
    name = "War Machine",
  },
  ["shattereddefenses"] = {
    "Shattered Defenses",
    { r = 170, g = 90, b = 30, a = 1 },
    getMinMax = getMinMax,
    name = "Shattered Defenses",
  },
  ["stoneheart"] = {
    "Stone Heart",
    { r = 180, g = 188, b = 200, a = 1 },
    getMinMax = getMinMax,
    name = "Stone Heart",
  },
  ["frenzy"] = {
    "Frenzy",
    { r = 0.549, g = 0.451, b = 0.294, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Frenzy",
  },
  ["frenzy2"] = {
    "Frenzy2",
    { r = 0.549, g = 0.451, b = 0.294, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Frenzy",
  },
  ["frenzy3"] = {
    "Frenzy3",
    { r = 0.549, g = 0.451, b = 0.294, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Frenzy",
  },
  ["focusedrage"] = {
    "Focused Rage",
    { r = 0.843, g = 0.196, b = 0.078, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Focused Rage",
  },
  ["focusedrage2"] = {
    "Focused Rage2",
    { r = 0.843, g = 0.196, b = 0.078, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Focused Rage",
  },
  ["focusedrage3"] = {
    "Focused Rage3",
    { r = 0.843, g = 0.196, b = 0.078, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Focused Rage",
  },
  ["globalcooldown"] = {
    "Global Cooldown",
    { r = 0.482, g = 1.000, b = 0.827, a = 1 },
    getMinMax = getGCD,
    name = "Global Cooldown",
  },
}

BuffData.deathknight = {
  ["soulgorge"] = {
    "Soulgorge",
    { r = 0.490, g = 0.039, b = 0.353, a = 1 },
    getMinMax = getMinMax,
    name = "Soulgorge",
  },
  ["gravewarden"] = {
    "Gravewarden",
    { r = 148, g = 127, b = 33, a = 1 },
    getMinMax = getMinMax,
    name = "Gravewarden",
  },
  ["unholyfrenzy"] = {
    "Unholy Frenzy",
    { r = 0.902, g = 0.784, b = 0.922, a = 1 },
    getMinMax = getMinMax,
    name = "Unholy Frenzy",
  },
  ["icytalons"] = {
    "Icy Talons",
    { r = 0.373, g = 0.412, b = 0.667, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Icy Talons",
  },
  ["icytalons2"] = {
    "Icy Talons2",
    { r = 0.373, g = 0.412, b = 0.667, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Icy Talons",
  },
  ["icytalons3"] = {
    "Icy Talons3",
    { r = 0.373, g = 0.412, b = 0.667, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Icy Talons",
  },
  ["boneshield"] = {
    "Bone Shield",
    { r = 0.498, g = 0.686, b = 0.118, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Bone Shield",
  },
  ["boneshield2"] = {
    "Bone Shield2",
    { r = 0.498, g = 0.686, b = 0.118, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Bone Shield",
  },
  ["boneshield3"] = {
    "Bone Shield3",
    { r = 0.498, g = 0.686, b = 0.118, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Bone Shield",
  },
  ["boneshield4"] = {
    "Bone Shield4",
    { r = 0.498, g = 0.686, b = 0.118, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Bone Shield",
  },
  ["boneshield5"] = {
    "Bone Shield5",
    { r = 0.498, g = 0.686, b = 0.118, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Bone Shield",
  },
  ["boneshield6"] = {
    "Bone Shield6",
    { r = 0.498, g = 0.686, b = 0.118, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Bone Shield",
  },
  ["boneshield7"] = {
    "Bone Shield7",
    { r = 0.498, g = 0.686, b = 0.118, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Bone Shield",
  },
  ["boneshield8"] = {
    "Bone Shield8",
    { r = 0.498, g = 0.686, b = 0.118, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Bone Shield",
  },
  ["boneshield9"] = {
    "Bone Shield9",
    { r = 0.498, g = 0.686, b = 0.118, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Bone Shield",
  },
  ["boneshield10"] = {
    "Bone Shield10",
    { r = 0.498, g = 0.686, b = 0.118, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Bone Shield",
  },
  ["globalcooldown"] = {
    "Global Cooldown",
    { r = 0.482, g = 1.000, b = 0.827, a = 1 },
    getMinMax = getGCD,
    name = "Global Cooldown",
  },
}

BuffData.hunter = {
  ["mongoosefury"] = {
    "Mongoose Fury",
    { r = 0.804, g = 0.157, b = 0.098, a = 1 },
    getMinMax = getMinMax,
    name = "Mongoose Fury",
  },
  ["steadyfocus"] = {
    "Steady Focus",
    { r = 1.000, g = 0.922, b = 0.000, a = 1 },
    getMinMax = getMinMax,
    name = "Steady Focus",
  },
  ["bombardment"] = {
    "Bombardment",
    { r = 0.961, g = 0.804, b = 0.000, a = 1 },
    getMinMax = getMinMax,
    name = "Bombardment",
  },
  ["direbeast"] = {
    "Dire Beast",
    { r = 252, g = 184, b = 64, a = 1 },
    getMinMax = getMinMax,
    name = "Dire Beast",
  },
  ["trickshots"] = {
    "Trick Shots",
    { r = 255, g = 235, b = 10, a = 1 },
    getMinMax = getMinMax,
    name = "Trick Shots",
  },
  ["preciseshots"] = {
    "Precise Shots",
    { r = 245, g = 230, b = 195, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Precise Shots",
  },
  ["preciseshots2"] = {
    "Precise Shots2",
    { r = 245, g = 230, b = 195, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Precise Shots",
  },
  ["lockandload"] = {
    "Lock and Load",
    { r = 0.961, g = 0.000, b = 0.000, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Lock and Load",
  },
  ["lockandload2"] = {
    "Lock and Load2",
    { r = 0.961, g = 0.000, b = 0.000, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Lock and Load",
  },
  ["moknathaltactics"] = {
    "Mok'Nathal Tactics",
    { r = 0.765, g = 0.529, b = 0.349, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Mok'Nathal Tactics",
  },
  ["moknathaltactics2"] = {
    "Mok'Nathal Tactics2",
    { r = 0.765, g = 0.529, b = 0.349, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Mok'Nathal Tactics",
  },
  ["moknathaltactics3"] = {
    "Mok'Nathal Tactics3",
    { r = 0.765, g = 0.529, b = 0.349, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Mok'Nathal Tactics",
  },
  ["moknathaltactics4"] = {
    "Mok'Nathal Tactics4",
    { r = 0.765, g = 0.529, b = 0.349, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Mok'Nathal Tactics",
  },
  ["frenzy"] = {
    "Frenzy",
    { r = 0.830, g = 0.350, b = 0.230, a = 1 },
    getMinMax = getPetBuffMinMax,
    name = "Frenzy",
  },
  ["mendpet"] = {
    "Mend Pet",
    { r = 230, g = 125, b = 10, a = 1 },
    getMinMax = getPetBuffMinMax,
    name = "Mend Pet",
  },
  ["beastcleave"] = {
    "Beast Cleave",
    { r = 65, g = 110, b = 120, a = 1 },
    getMinMax = getPetBuffMinMax,
    name = "Beast Cleave",
  },
  ["bestialtenacity"] = {
    "Bestial Tenacity",
    { r = 165, g = 55, b = 40, a = 1 },
    getMinMax = getPetBuffMinMax,
    name = "Bestial Tenacity",
  },
  ["globalcooldown"] = {
    "Global Cooldown",
    { r = 0.482, g = 1.000, b = 0.827, a = 1 },
    getMinMax = getGCD,
    name = "Global Cooldown",
  },
}

BuffData.paladin = {
  ["zeal"] = {
    "Zeal",
    { r = 0.941, g = 0.902, b = 0.588, a = 1 },
    getMinMax = getMinMax,
    name = "Zeal",
  },
  ["blessedstalwart"] = {
    "Blessed Stalwart",
    { r = 247, g = 183, b = 247, a = 1 },
    getMinMax = getMinMax,
    name = "Blessed Stalwart",
  },
  ["shieldoftherighteous"] = {
    "Shield of the Righteous",
    { r = 229, g = 224, b = 73, a = 1 },
    getMinMax = getMinMax,
    name = "Shield of the Righteous",
  },
  ["seraphim"] = {
    "Seraphim",
    { r = 0.569, g = 0.451, b = 0.137, a = 1 },
    getMinMax = getMinMax,
    name = "Seraphim",
  },
  ["globalcooldown"] = {
    "Global Cooldown",
    { r = 0.482, g = 1.000, b = 0.827, a = 1 },
    getMinMax = getGCD,
    name = "Global Cooldown",
  },
}

BuffData.priest = {
  ["norgannonsforesight"] = {
    "Norgannon's Foresight",
    { r = 0.000, g = 0.922, b = 1.000, a = 1 },
    getMinMax = getMinMax,
    name = "Norgannon's Foresight",
  },
  ["twistoffate"] = {
    "Twist of Fate",
    { r = 0.451, g = 0.333, b = 0.843, a = 1 },
    getMinMax = getMinMax,
    name = "Twist of Fate",
  },
  ["voidform125"] = {
    "Voidform (1-25)",
    { r = 248, g = 48, b = 55, a = 1 },
    getMinMax = getVoidformMinMax,
    name = "Voidform",
  },
  ["voidform2650"] = {
    "Voidform (26-50)",
    { r = 248, g = 48, b = 55, a = 1 },
    getMinMax = getVoidformMinMax,
    name = "Voidform",
  },
  ["voidform5175"] = {
    "Voidform (51-75)",
    { r = 248, g = 48, b = 55, a = 1 },
    getMinMax = getVoidformMinMax,
    name = "Voidform",
  },
  ["voidform76100"] = {
    "Voidform (76-100)",
    { r = 248, g = 48, b = 55, a = 1 },
    getMinMax = getVoidformMinMax,
    name = "Voidform",
  },
  ["lingeringinsanity125"] = {
    "Lingering Insanity (1-25)",
    { r = 96, g = 113, b = 233, a = 1 },
    getMinMax = getLingeringInsanityMinMax,
    name = "Lingering Insanity",
  },
  ["lingeringinsanity2650"] = {
    "Lingering Insanity (26-50)",
    { r = 96, g = 113, b = 233, a = 1 },
    getMinMax = getLingeringInsanityMinMax,
    name = "Lingering Insanity",
  },
  ["lingeringinsanity5175"] = {
    "Lingering Insanity (51-75)",
    { r = 96, g = 113, b = 233, a = 1 },
    getMinMax = getLingeringInsanityMinMax,
    name = "Lingering Insanity",
  },
  ["lingeringinsanity76100"] = {
    "Lingering Insanity (76-100)",
    { r = 96, g = 113, b = 233, a = 1 },
    getMinMax = getLingeringInsanityMinMax,
    name = "Lingering Insanity",
  },
  ["voidray"] = {
    "Void Ray",
    { r = 0.804, g = 0.275, b = 0.804, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Void Ray",
  },
  ["voidray2"] = {
    "Void Ray2",
    { r = 0.804, g = 0.275, b = 0.804, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Void Ray",
  },
  ["voidray3"] = {
    "Void Ray3",
    { r = 0.804, g = 0.275, b = 0.804, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Void Ray",
  },
  ["voidray4"] = {
    "Void Ray4",
    { r = 0.804, g = 0.275, b = 0.804, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Void Ray",
  },
  ["surgeoflight"] = {
    "Surge of Light",
    { r = 0.902, g = 0.569, b = 0.294, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Surge of Light",
  },
  ["surgeoflight2"] = {
    "Surge of Light2",
    { r = 0.902, g = 0.569, b = 0.294, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Surge of Light",
  },
  ["globalcooldown"] = {
    "Global Cooldown",
    { r = 0.482, g = 1.000, b = 0.827, a = 1 },
    getMinMax = getGCD,
    name = "Global Cooldown",
  },
}

BuffData.rogue = {
  ["truebearing"] = {
    "True Bearing",
    { r = 1.000, g = 0.824, b = 0.373, a = 1 },
    getMinMax = getMinMax,
    name = "True Bearing",
  },
  ["ruthlessprecision"] = {
    "Ruthless Precision",
    { r = 0.333, g = 0.550, b = 0.725, a = 1 },
    getMinMax = getMinMax,
    name = "Ruthless Precision",
  },
  ["broadside"] = {
    "Broadside",
    { r = 1.000, g = 0.627, b = 0.059, a = 1 },
    getMinMax = getMinMax,
    name = "Broadside",
  },
  ["grandmelee"] = {
    "Grand Melee",
    { r = 0.725, g = 0.333, b = 0.176, a = 1 },
    getMinMax = getMinMax,
    name = "Grand Melee",
  },
  ["skullandcrossbones"] = {
    "Skull and Crossbones",
    { r = 0.725, g = 0.686, b = 0.686, a = 1 },
    getMinMax = getMinMax,
    name = "Skull and Crossbones",
  },
  ["buriedtreasure"] = {
    "Buried Treasure",
    { r = 1.000, g = 0.843, b = 0.000, a = 1 },
    getMinMax = getMinMax,
    name = "Buried Treasure",
  },
  ["bladeflurry"] = {
    "Blade Flurry",
    { r = 1.000, g = 0.765, b = 0.588, a = 1 },
    getMinMax = getMinMax,
    name = "Blade Flurry",
  },
  ["envenom"] = {
    "Envenom",
    { r = 0.431, g = 1.000, b = 0.255, a = 1 },
    getMinMax = getMinMax,
    name = "Envenom",
  },
  ["shivarransymmetry"] = {
    "Shivarran Symmetry",
    { r = 1.000, g = 0.765, b = 0.588, a = 1 },
    getMinMax = getMinMax,
    name = "Shivarran Symmetry",
  },
  ["alacrity"] = {
    "Alacrity",
    { r = 1.000, g = 1.000, b = 0.392, a = 1 },
    getMinMax = getMinMax,
    name = "Alacrity",
  },
  ["sliceanddice"] = {
    "Slice and Dice",
    { r = 1.000, g = 0.294, b = 0.000, a = 1 },
    getMinMax = getMinMax,
    name = "Slice and Dice",
  },
  ["elaborateplanning"] = {
    "Elaborate Planning",
    { r = 0.941, g = 0.784, b = 0.078, a = 1 },
    getMinMax = getMinMax,
    name = "Elaborate Planning",
  },
  ["subterfuge"] = {
    "Subterfuge",
    { r = 0.882, g = 0.843, b = 1.000, a = 1 },
    getMinMax = getMinMax,
    name = "Subterfuge",
  },
  ["symbolsofdeath"] = {
    "Symbols of Death",
    { r = 1.000, g = 0.216, b = 0.980, a = 1 },
    getMinMax = getMinMax,
    name = "Symbols of Death",
  },
  ["envelopingshadows"] = {
    "Enveloping Shadows",
    { r = 0.882, g = 0.451, b = 0.882, a = 1 },
    getMinMax = getMinMax,
    name = "Enveloping Shadows",
  },
  ["globalcooldown"] = {
    "Global Cooldown",
    { r = 0.482, g = 1.000, b = 0.827, a = 1 },
    getMinMax = getGCD,
    name = "Global Cooldown",
  },
}

BuffData.warlock = {
  ["empoweredlifetap"] = {
    "Empowered Life Tap",
    { r = 0.686, g = 0.217, b = 1.000, a = 1 },
    getMinMax = getMinMax,
    name = "Empowered Life Tap",
  },
  ["deadwindharvester"] = {
    "Deadwind Harvester",
    { r = 16, g = 154, b = 140, a = 1 },
    getMinMax = getMinMax,
    name = "Deadwind Harvester",
  },
  ["backdraft"] = {
    "Backdraft",
    { r = 1.000, g = 0.726, b = 0.235, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Backdraft",
  },
  ["backdraft2"] = {
    "Backdraft2",
    { r = 1.000, g = 0.726, b = 0.235, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Backdraft",
  },
  ["backdraft3"] = {
    "Backdraft3",
    { r = 1.000, g = 0.726, b = 0.235, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Backdraft",
  },
  ["backdraft4"] = {
    "Backdraft4",
    { r = 1.000, g = 0.726, b = 0.235, a = 1 },
    getMinMax = getStackingMinMax,
    name = "Backdraft",
  },
  ["demonicempowerment"] = {
    "Demonic Empowerment",
    { r = 105, g = 235, b = 15, a = 1 },
    getMinMax = getPetBuffMinMax,
    name = "Demonic Empowerment",
  },
  ["globalcooldown"] = {
    "Global Cooldown",
    { r = 0.482, g = 1.000, b = 0.827, a = 1 },
    getMinMax = getGCD,
    name = "Global Cooldown",
  },
}

--- Provides resource details.
--- @param class string The class identifier.
--- @param buff string The buff identifier.
--- @return table The resource data.
function BuffData.get(class, buff)
  local res = BuffData[strlower(class)][strlower(buff)]
  res.ssClass = "BuffData"
  return res
end

--- Returns a reference to the target function.
--- @param name string The name of the function.
--- @return function The function reference.
function BuffData.getFunction(name)
  if name == "getStackingMinMax" then
    return getStackingMinMax
  else
    return getMinMax
  end
end

--- Initializes a stored resource with actual values.
--- @param resource string The resource to initialize.
function BuffData.init(resource)
  local index = strlower(string.gsub(resource[1], "[ :'%-%(%)]", ""))

  if BuffData[ss.curClass][index] then
    -- update identifier, color and getMinMax functions.
    resource[1] = BuffData[ss.curClass][index][1]
    resource[2] = BuffData[ss.curClass][index][2]
    resource.getMinMax = BuffData[ss.curClass][index].getMinMax
  else
    resource.getMinMax = function()
      return "", 0, 1, 1, 1
    end
  end
end

ss.Utils.addModule("BuffData", BuffData)
