-------------------------------------------------
-- Unit frame builder for an individual sphere --
-------------------------------------------------
local addon, ns = ...
local ss = ns.SS

local setmetatable = setmetatable
local math, CreateFrame, ToggleDropDownMenu, tonumber = math, CreateFrame, ToggleDropDownMenu, tonumber
local PetFrameDropDown, PlayerFrameDropDown, TargetFrameDropDown = PetFrameDropDown, PlayerFrameDropDown, TargetFrameDropDown
local UIParent, GameTooltip, GameTooltip_SetDefaultAnchor = UIParent, GameTooltip, GameTooltip_SetDefaultAnchor
local GetSpecialization, GetSpecializationInfo = GetSpecialization, GetSpecializationInfo
local tostring, pairs, select, strlower, table = tostring, pairs, select, strlower, table
local UnitPowerMax, InCombatLockdown = UnitPowerMax, InCombatLockdown

local mtSphere = {
  displayMode = "active",
  dbConfig = {},
  sphere = {},
  configName = "",
  name = "",
  modeId = "",
  mode = {},
  prevMode = {},
  monitorConfig = {},
  textFormat = {},
  resourceMonitor = {},
  animPosX = 0,
  animPosY = 0,
  animId = 0,
  animGroup = 0,
  animCamDist = 1,
  animAlpha = 1,
  speed = 5,
  fillTexture = {},
  template = {},
  anchor = "",
  offsetX = 0,
  offsetY = 0,
};

function mtSphere:createAssets(parent)
  -- base
  local base = ss.BaseCreator:New(parent, self.dbConfig.general.configName, self.displayMode)
  base:SetScale((self.displayMode == "active") and (ss.global.general.scale / 100) or 1)

  --container
  local sphere = ss.ContainerCreator:New(base, self.dbConfig.general.configName, self.displayMode)
  -- Add extra properties
  sphere.textFormat = self.dbConfig.textFormat
  --Add background
  sphere.bg = ss.BackgroundCreator:New(base)

  -- Get the current mode
  sphere.configName = self.dbConfig.general.configName
  base.configName = sphere.configName
  sphere.mode = self.dbConfig.modes[ss.profile.modeInfo[base.configName].modeSpec][ss.profile.modeInfo[base.configName].mode]

  local unitId, secondaryUnitId = ss.ClassResources.modeResourceUnitId(sphere.configName)

  if sphere.mode.fillColor[2] and secondaryUnitId then
    sphere.fillingLeft, sphere.fillingRight = ss.FillingCreator:New(sphere,self.dbConfig.general.size,sphere.mode.fillColor[1][2],sphere.mode.fillTexture[1],sphere.mode.fillTexture[2],sphere.mode.fillColor[2][2])

    if self.displayMode == "active" then
      -- Add power tracking
      sphere.config = {
        ["name"] = self.dbConfig.general.configName,
        ["unit"] = self.dbConfig.general.unit,
        ["unitId"] = unitId,
        ["realHeight"] = self.dbConfig.general.size * 0.95,
        ["textFormat"] = self.dbConfig.textFormat,
        ["previousValue"] = 0,
        ["secondaryUnitId"] = secondaryUnitId,
        ["previousSecondaryValue"] = 0,
        ["onMonitorUpdate"] = self.resourceUpdate,
        ["onMonitorUpdateSecondary"] = self.resourceUpdate,
      };

      self.monitorConfig = sphere.config
    end

    -- Add divider
    sphere.divider = ss.DividerCreator:New(sphere)
  else
    sphere.filling = ss.FillingCreator:New(sphere,self.dbConfig.general.size,sphere.mode.fillColor[1][2],sphere.mode.fillTexture[1],sphere.mode.fillTexture[2])

    if self.displayMode == "active" then
      -- Add power tracking
      sphere.config = {
        ["name"] = self.dbConfig.general.configName,
        ["unit"] = self.dbConfig.general.unit,
        ["unitId"] = unitId,
        ["realHeight"] = self.dbConfig.general.size * 1.086,
        ["textFormat"] = self.dbConfig.textFormat,
        ["previousValue"] = 0,
        ["onMonitorUpdate"] = self.resourceUpdate,
      };

     -- Add absorbs texture for health spheres
      if sphere.configName == "health" then
        local absorbColor = {r=1,g=1,b=1,a=0.5 }
        if (sphere.mode.fillColor[1][2].r > 0.7) and (sphere.mode.fillColor[1][2].g > 0.7) and (sphere.mode.fillColor[1][2].b > 0.7) then
          -- Ensure the absorbs are displayed on white/light grey values
          absorbColor = ss.private.general.resources.Absorb
        end

        sphere.fillingAbsorb = ss.FillingCreator:New(sphere,self.dbConfig.general.size,absorbColor,sphere.mode.fillTexture[1],sphere.mode.fillTexture[2])
        sphere.fillingAbsorb.name = "absorb"
        sphere.config["absorbUnitId"] = "absorb"
        sphere.config["previousAbsorbValue"] = 0
      end

      self.monitorConfig = sphere.config
    end
  end

  -- Add swirl
  sphere.swirl1, sphere.swirl2, sphere.swirl3 = ss.Swirl:NewGroup(sphere, sphere.configName, sphere.mode.swirlColor)
  -- Add animation
  sphere.animation = ss.AnimationCreator:New(sphere, self.dbConfig.animation)
  -- Add gloss
  sphere.glossHolder = ss.Gloss:New(sphere)
  -- Add text
  sphere.fontHolder, sphere.font1, sphere.font2 = ss.FontCreator:New(sphere, self.displayMode, sphere.mode.fontColor[1], sphere.mode.fontColor[2], (sphere.mode.fillColor[2] and secondaryUnitId))

  sphere:Show()

  return sphere
end

function mtSphere:createGroupItemAssets(index, resource, region)
  -- base
  local base = ss.BaseCreator:NewGroupItem(self.sphere, self.dbConfig.general.configName, index, region)
  --container
  local sphere = ss.ContainerCreator:NewGroupItem(base, self.dbConfig.general.configName, index)
  --Add background
  sphere.bg = ss.BackgroundCreator:New(base)
  --Add filling
  local fillIndex = region and 2 or 1
  sphere.filling = ss.FillingCreator:New(sphere,30,resource[2], ss.FillTextures.get("primary", ss.profile.microResourceFillTextures[fillIndex]))
  -- Add animation
  sphere.animation = ss.AnimationCreator:NewGroupItem(sphere)
  -- Add gloss
  sphere.glossHolder = ss.Gloss:New(sphere)
  -- Add tooltip
  base.tooltip = ss.GeneralTooltip:New(index, region)
  base:SetScript("OnLeave", function(self) base.tooltip:Hide(); end)
  base:SetScript("OnEnter", function(self)
    self.tooltip:ClearLines()

    if sphere.tooltipLabel and self:GetAlpha() > 0 then
      self.tooltip:SetOwner(self,"CENTER")
      self.tooltip:AddLine(sphere.tooltipLabel)
      self.tooltip:Show()
    end
  end)

  sphere:Show()

  return sphere
end

function mtSphere:AddResourceGroup(resource, region)
  self.sphere.resourceGroup = self.sphere.resourceGroup or {}

  if resource.buffGroup then
    self:AddMicroGroup(resource, "buff", 1, table.getn(resource.buffGroup), 1, nil, region)
  elseif table.getn(resource) > 0 then
    if not resource.getMinMax then
      self:AddResourceGroup(resource[1], region)
      self:AddSecondaryResourceGroup(resource[2], region)
    else
      local max = tonumber(resource[1]) and select(3, resource.getMinMax(resource[1])) or 1
      self:AddMicroGroup(resource, resource[1], 1, max, 1, nil, region)
    end
  end
end

function mtSphere:AddMicroGroup(resource, resourceId, start, limit, step, group, region)
  group = group or "resource"

  for i = start, limit, step do
    self.sphere[group.."Group"][i] = self.sphere[group.."Group"][i] or self:createGroupItemAssets(i, (resourceId == "buff") and resource.buffGroup[i] or resource, region)

    if resourceId ~= "buff" and ss.ClassResourceData.resourceAnimation(resource.name) then
      -- Add or update resource animation.
      if self.sphere[group.."Group"][i].resourceAnimation then
        self.sphere[group.."Group"][i].resourceAnimation:UpdateAnimation(ss.ClassResourceData.resourceAnimation(resource.name))
      else
        self.sphere[group.."Group"][i].resourceAnimation = ss.AnimationCreator:New(self.sphere[group.."Group"][i], ss.ClassResourceData.resourceAnimation(resource.name))
      end

      self.sphere[group.."Group"][i].resourceAnimation:SetAlpha(0)
    elseif self.sphere[group.."Group"][i].resourceAnimation then
      self.sphere[group.."Group"][i].resourceAnimation:Hide()
    end
  end

  if self.displayMode == "active" then
    -- Add power tracking
    self.sphere[group.."GroupUnit"] = resource
    self.sphere[group.."GroupUnitId"] = resourceId
  end
end

function mtSphere:AddSecondaryResourceGroup(resource, region)
  self.sphere.secondaryResourceGroup = self.sphere.secondaryResourceGroup or {}

  if resource and resource.buffGroup then
    self:AddMicroGroup(resource, "buff", 10, 11 - table.getn(resource.buffGroup), -1, "secondaryResource", region)
  elseif resource and table.getn(resource) > 0 then
    local max = tonumber(resource[1]) and select(3, resource.getMinMax(resource[1])) or 1
    self:AddMicroGroup(resource, resource[1], 10, 11 - max, -1, "secondaryResource", region)
  end
end

function mtSphere:toggleResourceGroup(visible)
  if visible and self.sphere.resourceGroup then
    for k,v in pairs(self.sphere.resourceGroup) do
      if self.sphere.resourceGroupUnitId == "buff" then
        if k <= table.getn(self.sphere.resourceGroupUnit.buffGroup) then
          v.filling:SetAlpha(1)
          v.filling:SetVertexColor(ss:GetRGBAValues(self.sphere.resourceGroupUnit.buffGroup[k][2]))
          v:GetParent():Show()
        else
          v:GetParent():Hide()
        end
      elseif k <= select(3, self.sphere.resourceGroupUnit.getMinMax(self.sphere.resourceGroupUnitId)) then
        v.filling:SetVertexColor(ss:GetRGBAValues(self.sphere.resourceGroupUnit[2]))
        v.filling:SetAlpha(0)
        v:GetParent():SetAlpha(1)
        v:GetParent():Show()
      else
        v:GetParent():Hide()
      end
    end
  elseif self.sphere.resourceGroup then
    for k,v in pairs(self.sphere.resourceGroup) do
      v:GetParent():Hide()
    end
  end
end

function mtSphere:toggleSecondaryResourceGroup(visible)
  if visible and self.sphere.secondaryResourceGroup then
    for k,v in pairs(self.sphere.secondaryResourceGroup) do
      if self.sphere.secondaryResourceGroupUnitId == "buff" then
        local index = 11 - k
        if index <= table.getn(self.sphere.secondaryResourceGroupUnit.buffGroup) then
          v.filling:SetAlpha(1)
          v.filling:SetVertexColor(ss:GetRGBAValues(self.sphere.secondaryResourceGroupUnit.buffGroup[index][2]))
          v:GetParent():Show()
        else
          v:GetParent():Hide()
        end
      elseif k >= 11 - select(3, self.sphere.secondaryResourceGroupUnit.getMinMax(self.sphere.secondaryResourceGroupUnitId)) then
        v.filling:SetVertexColor(ss:GetRGBAValues(self.sphere.secondaryResourceGroupUnit[2]))
        v.filling:SetAlpha(0)
        v:GetParent():SetAlpha(1)
        v:GetParent():Show()
      else
        v:GetParent():Hide()
      end
    end
  elseif self.sphere.secondaryResourceGroup then
    for k,v in pairs(self.sphere.secondaryResourceGroup) do
      v:GetParent():Hide()
    end
  end
end

function mtSphere:updateResourceGroupTextures(index, texture)
  local group = (index and index == 1) and "resourceGroup" or "secondaryResourceGroup"
  if self.sphere[group] then
    for k,v in pairs(self.sphere[group]) do
      v.filling:SetTexture(ss.FillTextures.get("primary", texture))
    end
  end
end

function mtSphere:initialize(sphereConfig,parent,displayMode)
  self.displayMode = displayMode or "active"
  self.dbConfig = sphereConfig
  self.configName = sphereConfig.general.configName
  self.name = sphereConfig.general.name
  self.mode = sphereConfig.modes[ss.profile.modeInfo[self.configName].modeSpec][ss.profile.modeInfo[self.configName].mode]
  self.textFormat = sphereConfig.textFormat
  self.animPosX = sphereConfig.animation.posX
  self.animPosY = sphereConfig.animation.posY
  self.animGroup = sphereConfig.animation.groupId
  self.animId = sphereConfig.animation.id
  self.animCamDist = sphereConfig.animation.camDistance
  self.animAlpha = sphereConfig.animation.alpha
  self.fillTexture = self.mode.fillTexture
  self.modeId = ss.profile.modeInfo[self.configName].mode
  self.sphere = self:createAssets(parent)
  self:setSwirlSpeed(sphereConfig.swirl.speed)
  self:showFont(1, sphereConfig.font[1].show)
  self:showFont(2, sphereConfig.font[2].show)

  if displayMode == "active" then
    ss:MakeFrameMovable(self.sphere:GetParent(), nil, self.onDrag)

    self.resourceMonitor = ss.ResourceMonitor:New()
    self.sphere:SetScript("OnUpdate",self.resourceMonitor:Watch())
  end
end

function mtSphere.resourceUpdate(currVal, maxVal, height, text, filling, prevVal, valText, textFormat, passed)
  if not tonumber(maxVal) or maxVal == 0 then maxVal = 1 end
  if not tonumber(currVal) then currVal = 1 end

  if passed and (currVal == prevVal) then
    -- dont process the resource if it isn't changing
    return currVal
  end

  -- If the previous value is higher than the maximum reset to max
  prevVal = (prevVal <= maxVal) and prevVal or maxVal

  if ss.global.general.smoothing then
    local step = 0
    local onePercentValue = maxVal / 100

    if maxVal < 100 then
      onePercentValue = onePercentValue * ((((100/maxVal) - 1) / 10) + 1)
    end

    local newValue = prevVal
    if prevVal < currVal then
      step = 1
      if prevVal + (onePercentValue * step) > currVal then
        newValue = currVal
      else
        newValue = prevVal + (onePercentValue * step)
      end
    elseif prevVal > currVal then
      step = -1
      if prevVal + (onePercentValue * step) < currVal then
        newValue = currVal
      else
        newValue = prevVal + (onePercentValue * step)
      end
    end
    prevVal = newValue

  else
    prevVal = currVal
  end

  local targetTexHeight = (prevVal/maxVal * height)
  if targetTexHeight < 1 then targetTexHeight = 1 end
  local newDisplayPercentage = prevVal/maxVal

  if newDisplayPercentage > 1 then
    newDisplayPercentage = 1
    targetTexHeight = height
  end

  filling:SetHeight(targetTexHeight)
  filling:SetTexCoord(0,1,math.abs(newDisplayPercentage - 1),1)

  if text or valText then
    local string = math.floor(newDisplayPercentage*100)
    if tonumber(string) == nil then string = 0 end
    if text then
      text:SetText(string)
    end

    if valText then
      string = ss:valueFormat(currVal, textFormat, false)
      valText:SetText(string)
    end
  end

  return prevVal, newDisplayPercentage
end

function mtSphere:setResource(resource)
  if not resource and not resource.modes[ss.profile.modeInfo[resource.general.configName].modeSpec] then
    return;
  end

  if self.displayMode == "active" and not InCombatLockdown() and resource.general.configName ~= "vehicle" and resource.general.configName ~= "vehicleResource" then
    self:show(true);
    self.sphere:GetParent():SetHeight(ss.global.general["SphereSize"] / resource.general.scale)
    self.sphere:GetParent():SetWidth(ss.global.general["SphereSize"] / resource.general.scale)
    self.sphere:GetParent():SetPoint(resource.general.anchor, resource.general.posX, resource.general.posY)
  end

  self.dbConfig = resource
  self.sphere.textFormat = resource.textFormat
  self.textFormat = resource.textFormat
  self.modeId = ss.profile.modeInfo[resource.general.configName].mode
  local mode = resource.modes[ss.profile.modeInfo[resource.general.configName].modeSpec][ss.profile.modeInfo[resource.general.configName].mode]
  self.sphere.configName = resource.general.configName
  self.sphere:GetParent().configName = resource.general.configName
  self.configName = resource.general.configName
  self:setSwirlSpeed(resource.swirl.speed)
  self:setSwirlTexture(resource.swirl.id)
  self:setAnimation(resource.animation)

  if self.displayMode == "preview" then
    self:setAnimationStrata(resource.animation.frameLevel + ss.private.layout.config.frameLevelAdjust)
  end

  self:showFont(1, resource.font[1].show)
  self:showFont(2, resource.font[2].show)

  self:setMode(mode, ss.profile.modeInfo[resource.general.configName].mode)
end

function mtSphere:setMode(mode, modeName)
  if not mode then return; end

  ss.profile.modeInfo[self.configName].mode = modeName or ss.profile.modeInfo[self.configName].mode
  self.prevMode = self.mode
  self.mode = mode
  self.sphere.mode = mode

  -- adjust sphere colors
  local unitId, secondaryUnitId = ss.ClassResources.modeResourceUnitId(self.configName)

  if self.mode.fillColor and self.mode.fillColor[2] and secondaryUnitId then
    if self.sphere.filling and not InCombatLockdown()then
      self.sphere.filling:Hide()
    end

    -- display the proper fill texture frames are displayed
    if not self.sphere.fillingLeft then
      self.sphere.fillingLeft, self.sphere.fillingRight = ss.FillingCreator:New(self.sphere,self.dbConfig.general.size,self.sphere.mode.fillColor[1][2],self.sphere.mode.fillTexture[1],self.sphere.mode.fillTexture[2],self.sphere.mode.fillColor[2][2], self.displayMode)

      self.sphere.divider = ss.DividerCreator:New(self.sphere)
    else
      self.sphere.fillingLeft:SetVertexColor(ss:GetRGBAValues(self.mode.fillColor[1][2]))
      self.sphere.fillingRight:SetVertexColor(ss:GetRGBAValues(self.mode.fillColor[2][2] or self.mode.fillColor[1][2]))

      if not self.mode.fillTexture[2] then
        local t = ss.FillTextures.get("split", 1)
        self.sphere.fillingLeft:SetTexture(t[1])
        self.sphere.fillingRight:SetTexture(t[2])
      else
        self.sphere.fillingLeft:SetTexture(self.mode.fillTexture[1])
        self.sphere.fillingRight:SetTexture(self.mode.fillTexture[2])
      end
    end

    ss.FontCreator:SetFonts(self.sphere.font1, self.sphere.font2, true)

    if self.displayMode == "active" then
      -- keep previous val if set to avoid "restarting" the sphere visually.
      local prevVal = self.monitorConfig.previousValue or 0
      local prevVal2 = self.monitorConfig.previousSecondaryValue or 0

      self.monitorConfig = {
        ["name"] = self.configName,
        ["unit"] = self.dbConfig.general.unit,
        ["unitId"] = unitId,
        ["realHeight"] = self.dbConfig.general.size * 0.95,
        ["textFormat"] = self.dbConfig.textFormat,
        ["previousValue"] = prevVal,
        ["secondaryUnitId"] = secondaryUnitId,
        ["previousSecondaryValue"] = prevVal2,
        ["onMonitorUpdate"] = self.resourceUpdate,
        ["onMonitorUpdateSecondary"] = self.resourceUpdate,
      };

      self.sphere.config = self.monitorConfig;
    elseif not self.mode.fillTexture[2] then
      self.sphere.mode.fillTexture = ss.FillTextures.get("split", 1)
      self.sphere.mode.fillColor[2] = self.sphere.mode.fillColor[1]
    end

    -- Update sphere colors
    if not InCombatLockdown() then
      self.sphere.divider:Show()
      self.sphere.fillingLeft:Show()
      self.sphere.fillingRight:Show()
    end
  else
    if self.sphere.fillingLeft then
      -- clear power tracking and hide previous textures
      if not InCombatLockdown() then
        self.sphere.fillingLeft:Hide()
        self.sphere.fillingRight:Hide()
        self.sphere.divider:Hide()
      end
    end

    -- display the proper fill texture frames are displayed
    if not self.sphere.filling then
      self.sphere.filling = ss.FillingCreator:New(self.sphere,self.dbConfig.general.size,self.sphere.mode.fillColor[1][2],self.sphere.mode.fillTexture[1],self.sphere.mode.fillTexture[2])
    else
      self.sphere.filling:SetTexture(self.mode.fillTexture[2] and ss.FillTextures.get("primary","moon") or self.mode.fillTexture[1])
      self.sphere.filling:SetVertexColor(ss:GetRGBAValues(self.mode.fillColor[1][2]))
    end

    ss.FontCreator:SetFonts(self.sphere.font1, self.sphere.font2, false)

    if self.displayMode == "active" then
      -- keep previous val if set to avoid "restarting" the sphere visually.
      local prevVal = self.monitorConfig.previousValue or 0
      local prevAbsorbVal = self.monitorConfig.previousAbsorbValue or 0

      -- replace power tracking
      self.monitorConfig = {
        ["name"] = self.configName,
        ["unit"] = self.dbConfig.general.unit,
        ["unitId"] = unitId,
        ["realHeight"] = self.dbConfig.general.size * 1.086,
        ["textFormat"] = self.dbConfig.textFormat,
        ["previousValue"] = prevVal,
        ["onMonitorUpdate"] = self.resourceUpdate,
      };

      -- Update Absorb texture if required
      if self.sphere.configName == "health" or self.sphere.configName == "vehicle" then
        local absorbColor = {r=1,g=1,b=1,a=0.5}
        if (self.mode.fillColor[1][2].r > 0.7) and (self.mode.fillColor[1][2].g > 0.7) and (self.mode.fillColor[1][2].b > 0.7) then
          -- Ensure the absorbs are displayed on white/light grey values
          absorbColor = ss.private.general.resources.Absorb
        end
        self.sphere.fillingAbsorb:SetVertexColor(ss:GetRGBAValues(absorbColor))
        self.monitorConfig.absorbUnitId = "absorb"
        self.monitorConfig.previousAbsorbValue = prevAbsorbVal
      end

      self.sphere.config = self.monitorConfig;
    elseif self.sphere.mode.fillTexture[2] then
      self.sphere.mode.fillTexture = {
        [1] = ss.FillTextures.get("primary","moon"),
      };
    end

    -- Update sphere
    if not InCombatLockdown() then
      self.sphere.filling:Show()
    end
  end

  self.sphere.font1:SetTextColor(ss:GetRGBAValues(self.mode.fontColor[1]))
  self.sphere.font2:SetTextColor(ss:GetRGBAValues(self.mode.fontColor[2]))
  self.sphere.swirl1.texture:SetVertexColor(ss:GetRGBAValues(self.sphere.mode.swirlColor[1]))
  self.sphere.swirl2.texture:SetVertexColor(ss:GetRGBAValues(self.sphere.mode.swirlColor[2] or self.sphere.mode.swirlColor[1]))
  self.sphere.swirl3.texture:SetVertexColor(ss:GetRGBAValues(self.sphere.mode.swirlColor[3] or self.sphere.mode.swirlColor[1]))
  self.fillTexture = self.mode.fillTexture
end

function mtSphere:setFillTexture(group, id)
  local texture = ss.FillTextures.get(group, id)

  if texture[2] then
    self.sphere.fillingLeft:SetTexture(texture[1])
    self.sphere.fillingRight:SetTexture(texture[2])
    self.fillTexture = texture
  else
    self.sphere.filling:SetTexture(texture)
    self.fillTexture = {[1] = texture}
  end
end

function mtSphere:setFillColor(color)
  if color[2] and color[1]then
    self.sphere.fillingLeft:SetVertexColor(ss:GetRGBAValues(color[1]))
    self.sphere.fillingRight:SetVertexColor(ss:GetRGBAValues(color[2]))
  elseif color[2] then
    self.sphere.fillingRight:SetVertexColor(ss:GetRGBAValues(color[2]))
  else
    self.sphere.filling:SetVertexColor(ss:GetRGBAValues(color[1]))
  end
end

function mtSphere:setSwirlTexture(id)
  self.sphere.swirlId = id
  local textures = ss.FillTextures.get("swirl", id)
  self.sphere.swirl1.texture:SetTexture(textures[1])
  self.sphere.swirl2.texture:SetTexture(textures[2])
  self.sphere.swirl3.texture:SetTexture(textures[3])

  local size = (self.displayMode == "active") and ss.db[self.configName].general.size or (ss.db[self.configName].general.size * ss.db[self.configName].general.scale)
  size = ss.FillTextures.getSwirlSize(id, size)
  self.sphere.swirl1:SetWidth(size * 0.9)
  self.sphere.swirl1:SetHeight(size * 0.9)
  self.sphere.swirl2:SetWidth(size * 0.9)
  self.sphere.swirl2:SetHeight(size * 0.9)
  self.sphere.swirl3:SetWidth(size * 0.9)
  self.sphere.swirl3:SetHeight(size * 0.9)
end

function mtSphere:setSwirlColor(color, index)
  if not index then
    self.sphere.swirl1.texture:SetVertexColor(ss:GetRGBAValues(color))
    self.sphere.swirl2.texture:SetVertexColor(ss:GetRGBAValues(color))
    self.sphere.swirl3.texture:SetVertexColor(ss:GetRGBAValues(color))
  else
    self.sphere["swirl"..index].texture:SetVertexColor(ss:GetRGBAValues(color))
  end
end

function mtSphere:setAnimation(anim)
  if not self.sphere.animation then
    self.sphere.animation = ss.AnimationCreator:New(self, anim)
  end

  self:setAnimationAlpha(anim.alpha)
  self:setAnimationOffset(anim.posX, anim.posY)
  self:setAnimationScale(anim.camDistance)
  self:setAnimationDisplayInfo(anim.groupId, anim.id)
  self:setAnimationStrata(anim.frameLevel or 3)
end

function mtSphere:setAnimationOffset(x, y)
  x, y = x or select(2, self.sphere.animation:GetPosition()), y or select(3, self.sphere.animation:GetPosition())
  self.sphere.animation:SetPosition(0,x,y)
  self.animPosX = x
  self.animPosY = y
end

function mtSphere:setAnimationAlpha(alpha)
  self.sphere.animation:SetAlpha(alpha)
  self.animAlpha = alpha
end

function mtSphere:setAnimationScale(val)
  self.sphere.animation:SetCamDistanceScale(val)
  self.animCamDist = val
end

function mtSphere:setAnimationDisplayInfo(groupId, id)
  self.sphere.animation:SetDisplayInfo(ss.Animations:Get(groupId, id))
  self.animGroup = groupId
  self.animId = id
end

function mtSphere:setAnimationStrata(strata)
  self.sphere.animation:SetFrameLevel(strata or 3)
end

function mtSphere:setTextColor(color)
  self.sphere.font1:SetTextColor(ss:GetRGBAValues(color))
end

function mtSphere:setTextColor2(color)
  self.sphere.font2:SetTextColor(ss:GetRGBAValues(color))
end

function mtSphere:setText(text)
  self.sphere.font1:SetText(text)
end

function mtSphere:setText2(text)
  self.sphere.font2:SetText(text)
end

function mtSphere:setTextFormat(textFormat)
  self.textFormat = textFormat
  self.sphere.textFormat = textFormat
end

function mtSphere:UpdateScale()
  self.sphere:GetParent():SetScale(ss.global.general.scale / 100)
  self:setAnimation(self:getAnimation())


  if self.sphere.resourceGroup then
    for k,v in pairs(self.sphere.resourceGroup) do
      v.animation:UpdateAnimation()
      if v.resourceAnimation then
        v.resourceAnimation:UpdateAnimation()
      end
    end
    for k,v in pairs(self.sphere.secondaryResourceGroup) do
      v.animation:UpdateAnimation()
      if v.resourceAnimation then
        v.resourceAnimation:UpdateAnimation()
      end
    end
  end
end

function mtSphere:setSwirlSpeed(speed)
  self.sphere.swirl1:SetSpeed(speed,2,4)
  self.sphere.swirl2:SetSpeed(speed,3,5)
  self.sphere.swirl3:SetSpeed(speed,2,3)

  self.speed = speed or 5
end

function mtSphere:getSwirlSpeed()
  return self.sphere.swirl1:GetSpeed()
end

function mtSphere:getMode()
  return self.mode or self.sphere.mode
end

function mtSphere:getModes(specName)
  return ss.db[self.configName].modes[specName] or ss.db[self.configName].modes[ss.profile.modeInfo[self.configName].modeSpec]
end

function mtSphere:getAnimationStrata()
  return self.sphere.animation:GetFrameLevel()
end

function mtSphere:getFillTexture()
  return self.fillTexture
end

function mtSphere:getFillColor()
  if self.mode.fillColor[2] and self.sphere.fillingLeft and self.sphere.fillingRight then
    local r,g,b,a = self.sphere.fillingLeft:GetVertexColor()
    local r2,g2,b2,a2 = self.sphere.fillingRight:GetVertexColor()

    return {
      [1] = {r=r,g=g,b=b,a=a},
      [2] = {r=r2,g=g2,b=b2,a=a2},
    };
  elseif self.sphere.filling then
    local r,g,b,a = self.sphere.filling:GetVertexColor()

    return {
      [1] = {r=r,g=g,b=b,a=a},
    };
  else
    return nil
  end
end

function mtSphere:getSwirlTextures()
  return {
    self.sphere.swirl1.texture:GetTexture(),
    self.sphere.swirl2.texture:GetTexture(),
    self.sphere.swirl3.texture:GetTexture(),
  }
end

function mtSphere:getSwirlId()
  return self.sphere.swirlId
end

function mtSphere:getSwirlColor(index)
  index = index or 1
  return self.sphere["swirl"..index].texture:GetVertexColor()
end

function mtSphere:getAnimation()
  return self.dbConfig.animation
end

function mtSphere:getAnimationDisplayInfo()
  return self.animGroup, self.animId
end

function mtSphere:getAnimationOffset()
  return self.animPosX, self.animPosY
end

function mtSphere:getAnimationAlpha()
  return self.animAlpha
end

function mtSphere:getAnimationScale()
  return self.animCamDist
end

function mtSphere:getTextColor()
  return self.sphere.font1:GetTextColor()
end

function mtSphere:getTextColor2()
  return self.sphere.font2:GetTextColor()
end

function mtSphere:getText()
  return self.sphere.font1:GetText()
end

function mtSphere:getText2()
  return self.sphere.font2:GetText()
end

function mtSphere:getTextFormat()
  return self.textFormat
end

function mtSphere.onDrag(self)
  ss.db[self.configName].general.anchor, ss.db[self.configName].general.posX, ss.db[self.configName].general.posY = select(3, self:GetPoint())
end

function mtSphere:showFont(font, show)
  if font == 1 and show == false then
    self.sphere.font1:Hide()
  elseif font == 2 and show == false then
    self.sphere.font2:Hide()
  elseif not font and show == false then
    self.sphere.font1:Hide()
    self.sphere.font2:Hide()
  elseif font == 1 then
    self.sphere.font1:Show()
  elseif font == 2 then
    self.sphere.font2:Show()
  else
    self.sphere.font1:Show()
    self.sphere.font2:Show()
  end
end

function mtSphere:show(show)
  if not show then
    self.sphere:GetParent():SetAlpha(0)
    self.sphere:SetAlpha(0)
    self.sphere:SetScript("OnUpdate",nil)
  else
    self.sphere:GetParent():SetAlpha(1)
    self.sphere:SetAlpha(1)
    self.sphere:SetScript("OnUpdate",(self.displayMode == "active") and self.resourceMonitor:Watch() or nil)
  end
end

function mtSphere:dbUpdate()
  if self.displayMode == "preview" then
    local configName = self.configName
    ss.db[configName].animation.posX, ss.db[configName].animation.posY = self:getAnimationOffset()
    ss.db[configName].animation.groupId, ss.db[configName].animation.id = self:getAnimationDisplayInfo()
    ss.db[configName].animation.camDistance = self:getAnimationScale()
    ss.db[configName].animation.alpha = self:getAnimationAlpha()
    ss.db[configName].animation.frameLevel = self:getAnimationStrata() - ss.private.layout.config.frameLevelAdjust
    ss.db[configName].textFormat = self:getTextFormat()
    ss.db[configName].swirl.speed = self:getSwirlSpeed()
    ss.db[configName].swirl.id = self:getSwirlId()
    ss.db[configName].modes[ss.profile.modeInfo[configName].modeSpec][self.modeId].fillTexture = self:getFillTexture() -- breaks if init not set

    local color = self:getFillColor()
    local r,g,b,a = ss:GetRGBAValues(color[1])
    ss.db[configName].modes[ss.profile.modeInfo[configName].modeSpec][self.modeId].fillColor[1][2] = {r=r,g=g,b=b,a=a}

    if color[2] then
      r,g,b,a = ss:GetRGBAValues(color[2])
      ss.db[configName].modes[ss.profile.modeInfo[configName].modeSpec][self.modeId].fillColor[2][2] = {r=r,g=g,b=b,a=a}
    end

    r,g,b,a = self:getSwirlColor(1)
    ss.db[configName].modes[ss.profile.modeInfo[configName].modeSpec][self.modeId].swirlColor[1] = {r=r,g=g,b=b,a=a}
    r,g,b,a = self:getSwirlColor(2)
    ss.db[configName].modes[ss.profile.modeInfo[configName].modeSpec][self.modeId].swirlColor[2] = {r=r,g=g,b=b,a=a}
    r,g,b,a = self:getSwirlColor(3)
    ss.db[configName].modes[ss.profile.modeInfo[configName].modeSpec][self.modeId].swirlColor[3] = {r=r,g=g,b=b,a=a}

    r,g,b,a = self:getTextColor()
    ss.db[configName].modes[ss.profile.modeInfo[configName].modeSpec][self.modeId].fontColor[1] = {r=r,g=g,b=b,a=a}

    r,g,b,a = self:getTextColor2()
    ss.db[configName].modes[ss.profile.modeInfo[configName].modeSpec][self.modeId].fontColor[2] = {r=r,g=g,b=b,a=a}

    ss.db[configName].font[1].show = self.sphere.font1:IsVisible()
    ss.db[configName].font[2].show = self.sphere.font2:IsVisible()
  end
end

function mtSphere:Get(p)
  return self[p]
end

function mtSphere:Set(p, v)
  if self[p] then
    self[p] = v
  end
end

ss["Sphere"] = {};

function ss.Sphere:New()
  local instance = {}
  setmetatable(instance, mtSphere)
  mtSphere.__index = mtSphere
  return instance
end
