----------------------------
-- Sphere Filling Creator --
----------------------------
local addon, ns = ...
local ss = ns.SS

local math = 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 updateGradientFilling = function(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 handleSplitValues = function(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 updateGradient = function(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 mtFilling = {
  createGradient = function(gradient)
    local grad = gradient.parent:CreateTexture(nil, "BACKGROUND")
    grad:SetTexture    (gradient.texture)
    grad:SetPoint      (gradient.anch,gradient.x,gradient.y)
    grad:SetWidth      (gradient.width)
    grad:SetHeight     (gradient.height)
    grad:SetAlpha      (1)
    grad:SetVertexColor(ss.Utils:GetRGBAValues(gradient.color))

    local r,g,b = ss.Utils:GetRGBAValues(gradient.color)
    grad:SetGradientAlpha("VERTICAL",r,g,b,0,r,g,b,0)

    if not InCombatLockdown() then
      grad:Show()
    end

    return grad
  end,
  createFillGradient = function(parent, filling, size)
    -- Add a gradient to the texture.
    local gradient =  ss.FillingCreator.createGradient{
      parent  = parent,
      texture = filling:GetTexture(),
      anch    = "BOTTOM",
      x       = 0,
      y       = size * -0.05,
      width   = size * 1.086,
      height  = size * 1.086,
      color   = {filling:GetVertexColor()},
    }
    
    filling._updateValues   = updateGradientFilling
    filling._updateGradient = updateGradient
  
    function filling:UpdateGradient(targetHeight, maxHeight, color, maxAlpha)
      self._maxH   = maxHeight
      self._newX = 0
      self:_updateValues(targetHeight, color, maxAlpha)
      self:_updateGradient()
    end

    return gradient
  end,
  createSplitGradients = function(parent, fillingLeft, fillingRight, size)
    -- Add left side gradient.
    local leftGradient = ss.FillingCreator.createGradient{
      parent  = parent,
      texture = fillingLeft:GetTexture(),
      anch    = "BOTTOM",
      x       = -size/4 + 0.70,
      y       = size * 0.025,
      width   = (size * 0.95) / 2,
      height  = size * 0.95,
      color   = {fillingLeft:GetVertexColor()},
    }
  
    fillingLeft._updateValues       = updateGradientFilling
    fillingLeft._updateSplitValues  = handleSplitValues
    fillingLeft._updateGradient     = updateGradient
  
    function fillingLeft:UpdateGradient(targetHeight, maxHeight)
      self._maxH   = maxHeight
      self._newX = (-self._maxH/4-1)
      self:_updateValues(targetHeight)
      self:_updateGradient()
    end
  
    -- Add right side gradient.
    local rightGradient = ss.FillingCreator.createGradient{
      parent  = parent,
      texture = fillingRight:GetTexture(),
      anch    = "BOTTOM",
      x       = size/4 - 0.70,
      y       = size * 0.025,
      width   = (size * 0.95) / 2,
      height  = size * 0.95,
      color   = {fillingRight:GetVertexColor()},
    }
  
    fillingRight._updateValues      = updateGradientFilling
    fillingRight._updateSplitValues = handleSplitValues
    fillingRight._updateGradient    = updateGradient

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

    return leftGradient, rightGradient
  end,
};

--- Creates a new fill texture or split texture.
-- @param parent The parent container.
-- @param size The size.
-- @param displayMode The sphere display mode.
-- @param c The texture color.
-- @param t The texture.
-- @param t2 The second texture for split textures.
-- @param c2 The second color for split textures.
-- @return The texture(s).
--
function mtFilling:New(parent, size, displayMode, c, t, t2, c2)
  size = (displayMode and displayMode == "preview") and 150 or size

  if c2 then
    local split = ss.FillTextures.get("split", 1)
    t, t2 = split[1], split[2]

    local fillingLeft = parent:CreateTexture(nil, "BACKGROUND")
    fillingLeft:SetTexture    (t)
    fillingLeft:SetPoint      ("BOTTOM", -size/4 + 0.70, size * 0.025)
    fillingLeft:SetWidth      ((size * 0.95) / 2)
    fillingLeft:SetHeight     (size * 0.95)
    fillingLeft:SetAlpha      (1)
    fillingLeft:SetVertexColor(ss.Utils:GetRGBAValues(c))

    local fillingRight = parent:CreateTexture(nil, "BACKGROUND")
    fillingRight:SetTexture    (t2)
    fillingRight:SetPoint      ("BOTTOM", size/4 - 0.70, size * 0.025)
    fillingRight:SetWidth      ((size * 0.95) / 2)
    fillingRight:SetHeight     (size * 0.95)
    fillingRight:SetAlpha      (1)
    fillingRight:SetVertexColor(ss.Utils:GetRGBAValues(c2))

    if ss.global.show.gradient then
      fillingLeft.gradient, fillingRight.gradient = self.createSplitGradients(parent, fillingLeft, fillingRight, size)
    end

    return fillingLeft, fillingRight
  else
    if t2 then
      t = ss.FillTextures.defaultTexture
    end

    local filling = parent:CreateTexture(nil, "BACKGROUND")
    filling:SetTexture    (t)
    filling:SetPoint      ("BOTTOM",0,size * -0.05)
    filling:SetWidth      (size * 1.086)
    filling:SetHeight     (size * 1.086)
    filling:SetAlpha      (1)
    filling:SetVertexColor(ss.Utils:GetRGBAValues(c))

    if ss.global.show.gradient then
      filling.gradient = self.createFillGradient(parent, filling, size)
    end

    return filling
  end
end

ss.Utils.addModule("FillingCreator", mtFilling)
