--- Sphere Micro Sphere Data Framework
--
local MicroSphereData = {}
local AddOnName, nsVars = ...
local ss = nsVars.SS

local strlower, pairs, type = strlower, pairs, type

--- Default micro sphere values, this includes only class resources such as combo points.
MicroSphereData.data = {
  ["deathknight"] = {
    ["blood"] = { ss.ClassResourceData.resourceInfo("runes"), {} },
    ["frost"] = { ss.ClassResourceData.resourceInfo("runes"), {} },
    ["unholy"] = { ss.ClassResourceData.resourceInfo("runes"), {} },
  },
  ["paladin"] = {
    ["protection"] = { ss.ClassResourceData.resourceInfo("holy power"), {} },
    ["holy"] = { ss.ClassResourceData.resourceInfo("holy power"), {} },
    ["retribution"] = { ss.ClassResourceData.resourceInfo("holy power"), {} },
  },
  ["mage"] = {
    ["arcane"] = { ss.ClassResourceData.resourceInfo("arcane charges"), {} },
  },
  ["druid"] = {
    ["classic"] = { cat = { ss.ClassResourceData.resourceInfo("combo points"), {} }, },
    ["balance"] = { cat = { ss.ClassResourceData.resourceInfo("combo points"), {} }, },
    ["feral"] = { cat = { ss.ClassResourceData.resourceInfo("combo points"), {} }, },
    ["guardian"] = { cat = { ss.ClassResourceData.resourceInfo("combo points"), {} }, },
    ["restoration"] = { cat = { ss.ClassResourceData.resourceInfo("combo points"), {} }, },
  },
  ["monk"] = {
    ["windwalker"] = { ss.ClassResourceData.resourceInfo("chi"), {} },
  },
  ["rogue"] = {
    ["classic"] = { ss.ClassResourceData.resourceInfo("combo points"), {} },
    ["outlaw"] = { ss.ClassResourceData.resourceInfo("combo points"), {} },
    ["assassination"] = { ss.ClassResourceData.resourceInfo("combo points"), {} },
    ["subtlety"] = { ss.ClassResourceData.resourceInfo("combo points"), {} },
  },
  ["warlock"] = {
    ["affliction"] = { ss.ClassResourceData.resourceInfo("soul shards"), {} },
    ["demonology"] = { ss.ClassResourceData.resourceInfo("soul shards"), {} },
    ["destruction"] = { ss.ClassResourceData.resourceInfo("soul shards"), {} },
  },
}

--- Verifies that the micro sphere table is properly stuctured.
--- @param res table The current resource table.
--- @return table the structured table.
local function checkStructure(res)
  if type(res) == "table" and res.ssClass then
    res = { res, {} }
  end

  return res
end

--- Initializes the micro sphere resources.
--- @param resource table The micro spheres to initialize.
local function init(resource)
  if type(resource) == "table" then
    for _, v in pairs(resource) do
      if v.name then
        ss[v.ssClass].init(v)
      else
        init(v)
      end
    end
  end
end

--- Verifies that the micro resources are properly structured and default values are up to date.
local function checkMicroResourceTable()
  if ss.curClass == "druid" then
    for spec, forms in pairs(ss.profile.microResources) do
      for form, res in pairs(forms) do
        if res then
          res = checkStructure(res)

          -- If using default class resource, check if it has changed or been removed.
          if res[1].ssClass == "ClassResourceData" and not MicroSphereData.hasClassResource(res[1].name, spec) then
            if MicroSphereData.data[ss.curClass][spec] and MicroSphereData.data[ss.curClass][spec][form] then
              res[1] = MicroSphereData.data[ss.curClass][spec][form][1]
            else
              res[1] = {}
            end
          end

          ss.profile.microResources[spec][form] = res
        end
      end
    end
  else
    for spec, res in pairs(ss.profile.microResources) do
      if res then
        res = checkStructure(res)

        -- If using default class resource, check if it has changed or been removed.
        if res[1].ssClass == "ClassResourceData" and not MicroSphereData.hasClassResource(res[1].name, spec) then
          if MicroSphereData.data[ss.curClass][spec] then
            res[1] = MicroSphereData.data[ss.curClass][spec][1]
          else
            res[1] = {}
          end
        end

        ss.profile.microResources[spec] = res
      end
    end
  end
end

--- Loads micro resource settings from storage.
function MicroSphereData.load()
  -- Load custom resources.
  MicroSphereData.loadCustomResources("BuffData")
  MicroSphereData.loadCustomResources("DebuffData")
  MicroSphereData.loadCustomResources("SpellChargeData")

  ss.profile.microResources = ss.profile.microResources or (MicroSphereData.data[ss.curClass] or {})

  -- Check table structures.
  checkMicroResourceTable()

  -- Assign functions.
  init(ss.profile.microResources)
  ss.profile.microResourcesSecondary = ss.profile.microResourcesSecondary or {}
  init(ss.profile.microResourcesSecondary)
end

--- Loads custom micro resources from storage.
--- @param class string The class of resource to load.
function MicroSphereData.loadCustomResources(class)
  for k, v in pairs(ss.global.customResources[class][ss.curClass]) do
    if not ss[v.ssClass][ss.curClass][k] then
      ss[v.ssClass][ss.curClass][k] = ss.Utils.tcopy(v, true)
      ss[v.ssClass][ss.curClass][k].getMinMax = ss[v.ssClass].getFunction(v.getMinMax)
    end
  end
end

--- Updates stored micro sphere settings.
--- @param data table The micro sphere data.
--- @param index number The storage index.
--- @param index2 string The storage index for group 2 values.
function MicroSphereData.set(data, index, index2)
  ss.profile.microResources = ss.profile.microResources or {}
  ss.profile.microResources[strlower(ss.curSpecName)] = ss.profile.microResources[strlower(ss.curSpecName)] or { {}, {} }

  if index2 then
    ss.profile.microResources[strlower(ss.curSpecName)][index2] = ss.profile.microResources[strlower(ss.curSpecName)][index2] or { {}, {} }
    ss.profile.microResources[strlower(ss.curSpecName)][index2] = checkStructure(ss.profile.microResources[strlower(ss.curSpecName)][index2])
    ss.profile.microResources[strlower(ss.curSpecName)][index2][index] = ss.Utils.tcopy(data or {}, true)
  else
    ss.profile.microResources[strlower(ss.curSpecName)] = checkStructure(ss.profile.microResources[strlower(ss.curSpecName)])
    ss.profile.microResources[strlower(ss.curSpecName)][index] = ss.Utils.tcopy(data or {}, true)
  end

  ss.Layout:AddMicroSpheres()
end

--- Updates stored micro sphere settings (secondary).
--- @param data table The micro sphere data.
--- @param index number The storage index.
--- @param index2 string The storage index for group 2 values.
function MicroSphereData.setSecondary(data, index, index2)
  ss.profile.microResourcesSecondary = ss.profile.microResourcesSecondary or {}
  ss.profile.microResourcesSecondary[strlower(ss.curSpecName)] = ss.profile.microResourcesSecondary[strlower(ss.curSpecName)] or { {}, {} }

  if index2 then
    ss.profile.microResourcesSecondary[strlower(ss.curSpecName)][index2] = ss.profile.microResourcesSecondary[strlower(ss.curSpecName)][index2] or { {}, {} }
    ss.profile.microResourcesSecondary[strlower(ss.curSpecName)][index2] = checkStructure(ss.profile.microResourcesSecondary[strlower(ss.curSpecName)][index2])
    ss.profile.microResourcesSecondary[strlower(ss.curSpecName)][index2][index] = ss.Utils.tcopy(data or {}, true)
  else
    ss.profile.microResourcesSecondary[strlower(ss.curSpecName)] = checkStructure(ss.profile.microResourcesSecondary[strlower(ss.curSpecName)])
    ss.profile.microResourcesSecondary[strlower(ss.curSpecName)][index] = ss.Utils.tcopy(data or {}, true)
  end

  ss.Layout:AddMicroSpheres("mainResource")
end

--- Checks if a class resource is available.
--- @param classResource string Optional string to specify the resource.
--- @param specName string Optional string to specify the spec.
--- @return boolean True if a resource is available.
function MicroSphereData.hasClassResource(classResource, specName)
  if classResource then
    if ss.curClass == "druid" then
      if MicroSphereData.data[ss.curClass] and MicroSphereData.data[ss.curClass][specName or strlower(ss.curSpecName)] then
        for _, v in pairs(MicroSphereData.data[ss.curClass][specName or strlower(ss.curSpecName)]) do
          if v[1].name == classResource then
            return true
          end
        end
      end

      return false
    end

    return MicroSphereData.data[ss.curClass]
        and MicroSphereData.data[ss.curClass][specName or strlower(ss.curSpecName)]
        and MicroSphereData.data[ss.curClass][specName or strlower(ss.curSpecName)][1].name == classResource
  end

  return MicroSphereData.data[ss.curClass] and MicroSphereData.data[ss.curClass][strlower(ss.curSpecName)]
end

--- Checks for a class resource the requires a micro sphere region.
--- @return boolean Flag for class resource usage.
function MicroSphereData.usingClassResource()
  local resource = ss.profile.microResources

  if resource and resource[strlower(ss.curSpecName)] then
    resource = resource[strlower(ss.curSpecName)]
    if resource[1] and type(resource[1]) == "table" then
      return (resource[1].ssClass and resource[1].ssClass == "ClassResourceData"),
      (resource[2] and resource[2].ssClass and resource[2].ssClass == "ClassResourceData")
    elseif resource.ssClass and resource.ssClass == "ClassResourceData" then
      return true
    end
  end

  return false
end

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