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

local UnitBuff, UnitStagger, UnitHealthMax = ss.APIUtils.UnitBuff, UnitStagger, UnitHealthMax
local string, GetTime, tonumber, strlower, select = string, GetTime, tonumber, strlower, select

local timeBuffer = 0.05

--- Provides global cooldown information.
-- @param spell The spell name so that it can be returned if necessary.
-- @return The spell information.
--
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 The buff name so that it can be returned if necessary.
-- @return The buff information.
-- @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 The buff identifier.
-- @return The buff information.
-- @see UnitBuff().
--
local function getMinMax(buff)
  local duration, expires = select(5, UnitBuff("player", buff))
  return buff, (duration and 1 or 0), 1, (duration or 1), (expires or 1)
end

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

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

  if stacks then
    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 The buff identifier.
-- @return The buff information.
-- @see UnitBuff().
--
local function getStacks(spell)
  local buffName = string.gsub(spell, "%d", "")
  local index = strlower(string.gsub(spell, "[ :'%(%)%d]", ""))
  local stacks, _, duration = select(3, UnitBuff("player", buffName))

  if stacks then
    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 The stagger values.
-- @see UnitStagger().
-- @see UnitHealthMax().
--
local function getStaggerMinMax()
  local min, max = UnitStagger("player"), UnitHealthMax("player")
  return "Stagger", min, max
end

--- Provides shield block information.
-- @param buff The buff identifier.
-- @return The buff information.
-- @see UnitBuff().
--
local function getShieldBlockMinMax(buff)
  local duration, expires = select(5, UnitBuff("player", buff))
  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 = { UnitBuff("player", i) }
      
      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 The pet buff identifier.
-- @return The pet buff information.
-- @see UnitBuff().
--
local function getPetBuffMinMax(buff)
  local duration, expires = select(5, UnitBuff("pet", buff))
  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 The buff identifier.
-- @return The buff information.
-- @see UnitBuff().
--
local function getVoidformMinMax(spell)
  local buffName = "Voidform"
  local stacks = select(3, UnitBuff("player", buffName))

  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 The buff identifier.
-- @return The buff information.
-- @see getVoidformMinMax().
-- @see UnitBuff().
--
local function getLingeringInsanityMinMax(spell)
  local buffName = "Lingering Insanity"
  local stacks = select(3, UnitBuff("player", buffName))
  
  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 = {
    ["dirgeofangerboda"]     = { "Dirge of Angerboda",    { r=150, g=215, b=55, a=1 },        getMinMax = getMinMax,         name = "Dirge of Angerboda",},
    ["wailofsvala"]          = { "Wail of Svala",         { r=135, g=140, b=225, a=1 },       getMinMax = getMinMax,         name = "Wail of Svala",},
    ["howlofingvar"]         = { "Howl of Ingvar",        { r=205, g=135, b=110, a=1 },       getMinMax = getMinMax,         name = "Howl of Ingvar",},
    ["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",},
    ["flametongue"]                  = { "Flametongue",                      { r=1.000, g=0.725, b=0.451, a=1 }, getMinMax = getMinMax,         name = "Flametongue",},
    ["crashlightning"]               = { "Crash Lightning",                  { r=0.098, g=0.588, b=1.000, a=1 }, getMinMax = getMinMax,         name = "Crash 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",},
    ["shockofthetwistingnether"]     = { "Shock of the Twisting Nether",     { r=0.608, g=1.000, b=0.529, a=1 }, getMinMax = getMinMax,         name = "Shock of the Twisting Nether",},
    ["chillofthetwistingnether"]     = { "Chill of the Twisting Nether",     { r=0.608, g=0.882, b=1.000, a=1 }, getMinMax = getMinMax,         name = "Chill of the Twisting Nether",},
    ["fireofthetwistingnether"]      = { "Fire of the Twisting Nether",      { r=0.843, g=0.176, b=0.000, a=1 }, getMinMax = getMinMax,         name = "Fire of the Twisting Nether",},
    ["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 The class identifier.
-- @param buff The buff identifier.
-- @return 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 The name of the function.
-- @return 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 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,0,0,0 end
  end
end

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