local SeamoreSpheresContainerLayers = {}
local AddOnName, nsVars = ...
local ss = nsVars.SS

local min, max, tonumber, math = min, max, tonumber, math

--- Updates a fill texture and it's gradient data.
-- @param self A reference to the fill texture.
-- @param targetHeight The target height
-- @param color The target color.
-- @param maxAlpha The alpha limit.
--
local function updateGradientFilling(self, targetHeight, color, maxAlpha)
  if self and ss.global.show.gradient then
    if color and maxAlpha and self.gradient then
      local r,g,b = ss.Utils:GetRGBAValues(color)
      self.gradient:SetGradientAlpha("VERTICAL",r,g,b,maxAlpha,r,g,b,0)
    end
    
    self._gradT = targetHeight / self._maxH
    self._gradT = self._gradT > 1 and 1 or self._gradT -- ensure value isnt greater than 1 when resource changes
    
    local textCoordTop = (self._gradT - ss.gradientScale)
    local newHeight    = targetHeight - (self._maxH * ss.gradientScale)
    textCoordTop = textCoordTop < 0 and 0 or textCoordTop
    
    if self._gradT < 0 then
      self._gradT = 0
    end
    
    newHeight = newHeight < 1 and 1 or newHeight
    
    self:SetHeight  (newHeight)
    self:SetTexCoord(0,1,math.abs(textCoordTop - 1),1)
    
    self._gradB = 1 - (self._gradT - ss.gradientScale)
    self._gradH      = self._maxH * ss.gradientScale
    
    self._gradB = self._gradB < 0 and 0 or self._gradB
    self._gradB = self._gradB > 1 and 1 or self._gradB
    
    self._newY = (self._maxH * (1 - self._gradB)) - self._gradH
    
    if self._updateSplitValues then
      self:_updateSplitValues(textCoordTop, newHeight)
    end
  end
end

--- Updates split fill textures and their gradient data.
-- @param self A reference to the texture.
-- @param textCoordTop The target top.
-- @param newHeight The target height.
--
local function handleSplitValues(self, textCoordTop, newHeight)
  if self then
    self._gradB = self._gradB - ss.gradientScale
    self._gradT    = self._gradT + ss.gradientScale
    
    newHeight    = newHeight + (ss.gradientScale * self._maxH)
    self._newY         = self._newY + (ss.gradientScale * self._maxH)
    textCoordTop = textCoordTop + ss.gradientScale
    
    if self._gradT > 1 then
      local diff = self._gradT - 1
      self._gradH       = self._gradH - (diff * self._maxH)
      self._gradT = 1
    elseif (self._gradT - ss.gradientScale) < ss.gradientScale then
      local diff = ss.gradientScale - (self._gradT - ss.gradientScale)
      
      newHeight    = newHeight - (diff * self._maxH)
      self._newY         = self._newY - (diff * self._maxH)
      textCoordTop = textCoordTop - diff
      self._gradB = 1
    end
    
    self:SetHeight  (newHeight)
    self:SetTexCoord(0,1,math.abs(textCoordTop - 1),1)
    
    self._newY = self._newY + (self._maxH * 0.075)
  end
end

--- Updates a texture gradient.
-- @param self The texture reference.
--
local function updateGradient(self)
  if self and ss.global.show.gradient then
    self.gradient:SetHeight  (self._gradH)
    self.gradient:SetTexCoord(0, 1, math.abs(self._gradT - 1), math.abs(self._gradB))
    self.gradient:SetPoint   ("BOTTOM", self._newX, self._newY)
    
    -- Clear temp values.
    self._maxH = nil
    self._newY = nil
    self._newX = nil
    self._gradH = nil
    self._gradT = nil
    self._gradB = nil
  end
end

local function initSplitFillGradientTextures(container, size)
  -- Initialize the left gradient.
  container.fillingLeftGradient:ClearAllPoints()
  container.fillingLeftGradient:SetPoint  ("BOTTOM", (-size/4 + 0.70), (size * 0.025))
  container.fillingLeftGradient:SetWidth  ((size * 0.95) / 2)
  container.fillingLeftGradient:SetHeight (size * 0.95)
  
  -- Initialize the right gradient.
  container.fillingRightGradient:ClearAllPoints()
  container.fillingRightGradient:SetPoint  ("BOTTOM", (size/4 - 0.70), (size * 0.025))
  container.fillingRightGradient:SetWidth  ((size * 0.95) / 2)
  container.fillingRightGradient:SetHeight (size * 0.95)
end

local function setSplitFillTextures(container, unitId, secondaryUnitId)
  if container.mode.fillColor[2] and secondaryUnitId then
    local texture = container.mode.fillTexture or ss.FillTextures.get("split", 1)
  
    container.fillingLeft:SetTexture(texture[1])
    container.fillingLeft:SetAlpha(1)
    container.fillingLeft:SetVertColor(container.mode.fillColor[1][2])
  
    container.fillingRight:SetTexture(texture[2])
    container.fillingRight:SetAlpha(1)
    container.fillingRight:SetVertColor(container.mode.fillColor[2][2])
    
    if ss.global.show.gradient then
      -- Set gradient color.
      local r,g,b,a = container.fillingLeft:GetVertexColor()
      container.fillingLeftGradient:SetTexture(texture[1])
      container.fillingLeftGradient:SetAlpha(1)
      container.fillingLeftGradient:SetGradientAlpha("VERTICAL",r,g,b,a,r,g,b,0)
  
      r,g,b,a = container.fillingRight:GetVertexColor()
      container.fillingRightGradient:SetTexture(texture[2])
      container.fillingRightGradient:SetAlpha(1)
      container.fillingRightGradient:SetGradientAlpha("VERTICAL",r,g,b,a,r,g,b,0)
    else
      container.fillingRightGradient:SetAlpha(0)
      container.fillingLeftGradient:SetAlpha(0)
      container.fillingLeftGradient:SetGradientAlpha("VERTICAL",0,0,0,0,0,0,0,0)
      container.fillingRightGradient:SetGradientAlpha("VERTICAL",0,0,0,0,0,0,0,0)
    end
  
    if container.displayMode == "active" then
      -- keep previous val if set to avoid "restarting" the sphere visually.
      local prevVal  = container.config.previousValue          or 0
      local prevVal2 = container.config.previousSecondaryValue or 0

      -- Add power tracking
      container.config = {
        ["name"] = ss.db[container.configName].general.configName,
        ["unit"] = ss.db[container.configName].general.unit,
        ["unitId"] = unitId,
        ["realHeight"] = ss.db[container.configName].general.size * 0.95,
        ["textFormat"] = ss.db[container.configName].textFormat,
        ["previousValue"] = prevVal,
        ["secondaryUnitId"] = secondaryUnitId,
        ["previousSecondaryValue"] = prevVal2,
      }
    end
  
    container.divider:SetAlpha(1)
  else
    container.divider:SetAlpha(0)
    container.fillingLeft:SetAlpha(0)
    container.fillingLeftGradient:SetAlpha(0)
    container.fillingLeftGradient:SetGradientAlpha("VERTICAL",0,0,0,0,0,0,0,0)
    container.fillingRight:SetAlpha(0)
    container.fillingRightGradient:SetAlpha(0)
    container.fillingRightGradient:SetGradientAlpha("VERTICAL",0,0,0,0,0,0,0,0)
  end
end

local function initSplitFillTextures(container)
  local size = (container.displayMode and container.displayMode == "preview") and 150 or ss.db[container.configName].general.size
  
  -- Initialize the left texture.
  container.fillingLeft:ClearAllPoints()
  container.fillingLeft:SetPoint("BOTTOM", -size/4 + 0.70, size * 0.025)
  container.fillingLeft:SetWidth((size * 0.95) / 2)
  container.fillingLeft:SetHeight(size * 0.95)

  container.fillingLeft._updateValues      = updateGradientFilling
  container.fillingLeft._updateSplitValues = handleSplitValues
  container.fillingLeft._updateGradient    = updateGradient

  function container.fillingLeft:UpdateGradient(targetHeight, maxHeight)
    self._maxH   = maxHeight
    self._newX = (-self._maxH/4-1)
    self:_updateValues(targetHeight)
    self:_updateGradient()
  end
  
  -- Initialize the right texture.
  container.fillingRight:ClearAllPoints()
  container.fillingRight:SetPoint("BOTTOM", size/4 - 0.70, size * 0.025)
  container.fillingRight:SetWidth((size * 0.95) / 2)
  container.fillingRight:SetHeight(size * 0.95)

  container.fillingRight._updateValues      = updateGradientFilling
  container.fillingRight._updateSplitValues = handleSplitValues
  container.fillingRight._updateGradient    = updateGradient

  function container.fillingRight:UpdateGradient(targetHeight, maxHeight)
    self._maxH   = maxHeight
    self._newX = (self._maxH/4 + 1)
    self:_updateValues(targetHeight)
    self:_updateGradient()
  end

  initSplitFillGradientTextures(container, size)
  
  -- Initialize the divider.
  container.divider:SetPoint("CENTER", 100, 100)
end

local function initFillGradientTexture(container, size)
  -- Initialize the gradient.
  container.fillingGradient:ClearAllPoints()
  container.fillingGradient:SetPoint  ("BOTTOM", 0, (size * -0.05))
  container.fillingGradient:SetWidth  (size * 1.086)
  container.fillingGradient:SetHeight (size * 1.086)
  
  if container.displayMode == "active" and container.configName == "health" then
    -- Initialize the absorb gradient.
    container.fillingAbsorbGradient:ClearAllPoints()
    container.fillingAbsorbGradient:SetPoint  ("BOTTOM", 0, (size * -0.05))
    container.fillingAbsorbGradient:SetWidth  (size * 1.086)
    container.fillingAbsorbGradient:SetHeight (size * 1.086)
  end
end

local function setFillTextures(container, unitId, secondaryUnitId)
  if container.mode.fillColor[2] and secondaryUnitId then
    container.filling:SetAlpha(0)
    container.fillingGradient:SetAlpha(0)
    container.fillingGradient:SetGradientAlpha("VERTICAL",0,0,0,0,0,0,0,0)
    container.fillingAbsorb:SetAlpha(0)
    container.fillingAbsorbGradient:SetAlpha(0)
    container.fillingAbsorbGradient:SetGradientAlpha("VERTICAL",0,0,0,0,0,0,0,0)
  else
    local texture = container.mode.fillTexture[2] and ss.FillTextures.defaultTexture or container.mode.fillTexture[1]
    local color = container.mode.fillColor[1][2]
  
    container.filling:SetTexture(texture)
    container.filling:SetAlpha(1)
    container.filling:SetVertColor(color)
  
    if container.configName ~= "health" or container.displayMode ~= "active" then
      -- Hide the absorb textures for all but active health.
      container.fillingAbsorb:SetAlpha(0)
      container.fillingAbsorbGradient:SetAlpha(0)
      container.fillingAbsorbGradient:SetGradientAlpha("VERTICAL",0,0,0,0,0,0,0,0)
    end
  
    -- Update gradient textures.
    if ss.global.show.gradient then
      local r,g,b,a = container.filling:GetVertexColor()
      container.fillingGradient:SetTexture(texture)
      container.fillingGradient:SetAlpha(1)
      container.fillingGradient:SetGradientAlpha("VERTICAL",r,g,b,a,r,g,b,0)
    else
      container.fillingGradient:SetAlpha(0)
      container.fillingAbsorbGradient:SetAlpha(0)
      container.fillingGradient:SetGradientAlpha("VERTICAL",0,0,0,0,0,0,0,0)
      container.fillingAbsorbGradient:SetGradientAlpha("VERTICAL",0,0,0,0,0,0,0,0)
    end
  
    if container.displayMode == "active" then
      -- keep previous val if set to avoid "restarting" the sphere visually.
      local prevAbsorbVal = container.config.previousAbsorbValue
      local prevVal = container.config.previousValue or 0
  
      -- Update the power tracking config.
      container.config = {
        ["name"] = ss.db[container.configName].general.configName,
        ["unit"] = ss.db[container.configName].general.unit,
        ["unitId"] = unitId,
        ["realHeight"] = ss.db[container.configName].general.size * 1.086,
        ["textFormat"] = ss.db[container.configName].textFormat,
        ["previousValue"] = prevVal,
      }
  
      if container.configName == "health" then
        local absorbColor
        
        if (color.r > 0.7) and (color.g > 0.7) and (color.b > 0.7) then
          -- Ensure the absorbs are displayed on white/light grey values.
          absorbColor = ss.private.general.resources.Absorb
        end
  
        -- Update absorb texture.
        container.fillingAbsorb:SetTexture(texture)
        container.fillingAbsorb:SetAlpha(1)
        container.fillingAbsorb:SetVertColor(absorbColor)
        
        -- Add absorb values to the config.
        container.fillingAbsorb.name = "absorb"
        container.config.absorbUnitId = "absorb"
        container.config.previousAbsorbValue = prevAbsorbVal or 0
  
        if ss.global.show.gradient then
          -- Update absorb gradient texture.
          local r,g,b,a = container.fillingAbsorb:GetVertexColor()
          container.fillingAbsorbGradient:SetTexture(texture)
          container.fillingAbsorbGradient:SetAlpha(1)
          container.fillingAbsorbGradient:SetGradientAlpha("VERTICAL",r,g,b,a,r,g,b,0)
        end
      end
    end
  end
end

local function initFillTexture(container)
  local size = (container.displayMode and container.displayMode == "preview") and 150 or ss.db[container.configName].general.size
  
  -- Initialize the texture.
  container.filling:ClearAllPoints()
  container.filling:SetPoint("BOTTOM", 0, size * -0.05)
  container.filling:SetWidth(size * 1.086)
  container.filling:SetHeight(size * 1.086)
  
  container.filling._updateValues   = updateGradientFilling
  container.filling._updateGradient = updateGradient

  function container.filling:UpdateGradient(targetHeight, maxHeight, color, maxAlpha)
    self._maxH = maxHeight
    self._newX = 0
    self:_updateValues(targetHeight, color, maxAlpha)
    self:_updateGradient()
  end
  
  if container.displayMode == "active" and container.configName == "health" then
    -- Initialize the absorb texture.
    container.fillingAbsorb:ClearAllPoints()
    container.fillingAbsorb:SetPoint("BOTTOM", 0, size * -0.05)
    container.fillingAbsorb:SetWidth(size * 1.086)
    container.fillingAbsorb:SetHeight(size * 1.086)

    container.fillingAbsorb._updateValues   = updateGradientFilling
    container.fillingAbsorb._updateGradient = updateGradient

    function container.fillingAbsorb:UpdateGradient(targetHeight, maxHeight, color, maxAlpha)
      self._maxH = maxHeight
      self._newX = 0
      self:_updateValues(targetHeight, color, maxAlpha)
      self:_updateGradient()
    end
  end

  initFillGradientTexture(container, size)
end

function SeamoreSpheresContainerLayers:Init()
  local unitId, secondaryUnitId = ss.ClassResources.modeResourceUnitId(self.configName)
  
  -- Initialize Fill Textures.
  initSplitFillTextures(self, unitId, secondaryUnitId)
  initFillTexture(self, unitId, secondaryUnitId)
  self.config = self.config or {}
end

function SeamoreSpheresContainerLayers:Refresh()
  local unitId, secondaryUnitId = ss.ClassResources.modeResourceUnitId(self.configName,
    (self.displayMode == "preview") and self.modeName)
  
  -- Initialize fill textures.
  setSplitFillTextures(self, unitId, secondaryUnitId)
  setFillTextures(self, unitId, secondaryUnitId)
end

rawset(_G, "SeamoreSpheresContainerLayers_OnLoad", function(container)
  container.layers = {}
  for k,v in pairs(SeamoreSpheresContainerLayers) do
    container.layers[k] = function()
      v(container)
    end
  end

  -- Add texture envents.
  ss.Extentions:DelegateVertexColor(
    container.fillingLeft,
    container.fillingLeftGradient,
    container.fillingRight,
    container.fillingRightGradient,
    container.filling,
    container.fillingGradient,
    container.fillingAbsorb,
    container.fillingAbsorbGradient)
  
  -- TODO: Remove this. references for old code to work
  container.fillingLeft.gradient = container.fillingLeftGradient
  container.fillingRight.gradient = container.fillingRightGradient
  container.filling.gradient = container.fillingGradient
  container.fillingAbsorb.gradient = container.fillingAbsorbGradient
  container.font1 = container.fontHolder.font1
  container.font2 = container.fontHolder.font2
end)
