------------------------------
-- Resource monitor builder --
------------------------------
local AddOnName, nsVars = ...
local ss = nsVars.SS

local setmetatable = setmetatable
local UnitGetTotalAbsorbs = UnitGetTotalAbsorbs
local pairs, strlower = pairs, strlower
local STAGGER_RED_TRANSITION, STAGGER_YELLOW_TRANSITION = STAGGER_RED_TRANSITION, STAGGER_YELLOW_TRANSITION
local GetRuneCooldown = GetRuneCooldown
local GetTime, math, UnitInVehicle = GetTime, math, UnitInVehicle
local UnitHealth, UnitHealthMax, UnitPower, UnitPowerMax = UnitHealth, UnitHealthMax, UnitPower, UnitPowerMax

local updateResourceGroupStagger = function(display, displayUnit)
  local spell, curStagger, maxHealth = displayUnit.getMinMax()
  display.tooltipLabel = "Stagger"

  if (curStagger / maxHealth) > STAGGER_RED_TRANSITION then
    display:GetParent():SetAlpha(1)
    display.filling:SetVertexColor(ss:GetRGBAValues(ss.ClassResourceData.resourceColor("stagger heavy")))
  elseif (curStagger / maxHealth) > STAGGER_YELLOW_TRANSITION then
    display:GetParent():SetAlpha(1)
    display.filling:SetVertexColor(ss:GetRGBAValues(ss.ClassResourceData.resourceColor("stagger medium")))
  elseif curStagger > 0 then
    display:GetParent():SetAlpha(1)
    display.filling:SetVertexColor(ss:GetRGBAValues(ss.ClassResourceData.resourceColor("stagger light")))
  else
    display:GetParent():SetAlpha(0)
  end
end

local updateResourceGroupBuff = function(display, group)
  group = group or "resource"

  for k,v in pairs(display[group.."Group"]) do
    local index = (group == "resource") and k or (11 - k)

    if display[group.."GroupUnit"].buffGroup[index] then
      if display[group.."GroupUnit"].buffGroup[index].name and display[group.."GroupUnit"].buffGroup[index].name == "Stagger" then
        updateResourceGroupStagger(v, display[group.."GroupUnit"].buffGroup[index])
        return
      end

      if display[group.."GroupUnit"].buffGroup[index] then
        v.tooltipLabel = display[group.."GroupUnit"].buffGroup[index].name or nil
        local spell, cur, max, duration, expires = display[group.."GroupUnit"].buffGroup[index].getMinMax(display[group.."GroupUnit"].buffGroup[index][1])

        local rem = expires - GetTime()
        rem = rem / duration

        if rem > 1 then
          rem = 1
        end

        if cur == 1 and (rem >= 0) and (rem <= 1) then
          v.filling:SetHeight((30 * 1.086) * rem)
          v.filling:SetTexCoord(0,1,math.abs(rem - 1),1)
          v.filling:SetAlpha(rem)
          v:GetParent():SetAlpha(1)
        else
          v:GetParent():SetAlpha(0)
        end
      end
    end
  end
end

local updateResourceGroupDefault = function(display, group)
  group = group or "resource"

  local spell, curRes, maxRes, start, duration, r = display[group.."GroupUnit"].getMinMax(display[group.."GroupUnitId"])
  local displayMax = (group == "resource") and maxRes or (11 - maxRes)

  if not display[group.."Group"][displayMax] then
    ss.Layout:AddMicroSpheres()
    ss.Layout:AddMicroSpheres("mainResource")
  end

  for k,v in pairs(display[group.."Group"]) do
    v.tooltipLabel = display[group.."GroupUnit"].name or nil
    local curAlpha = v.filling:GetAlpha()
    local validation1, validation2, validation3, validation4

    if group == "secondaryResource" then
      validation1 = k >= (11 - curRes)
      validation2 = k >= (10 - curRes)
      validation3 = k >= (11 - curRes)
      validation4 = k < (11 - curRes)
    else
      validation1 = (k <= curRes)
      validation2 = (k <= curRes + 1)
      validation3 = (k <= curRes)
      validation4 = (k > curRes)
    end

    if validation1 and strlower(ss.curClass) == "deathknight" then
      -- Handle death knight runes
      local runeCDProg, runeCDTot = GetRuneCooldown(k)
      if runeCDProg == 0 then
        runeCDProg = 1
      else
        v.filling.start = runeCDProg
        v.filling.duration = runeCDTot
        runeCDProg = GetTime() - runeCDProg
        runeCDProg = runeCDProg / runeCDTot
      end

      v.filling:SetHeight((30 * 1.086) * runeCDProg)
      v.filling:SetTexCoord(0,1,math.abs(runeCDProg - 1),1)
      v.filling:SetAlpha(runeCDProg)
    elseif validation2 and start and duration then
      local rem = (GetTime() - start) / duration

      if group == "secondaryResource" then
        validation1 = (k == (10 - curRes))
      else
        validation1 = (k == curRes + 1)
      end

      if validation1 and  (rem >= 0) and (rem <= 1) then
        v.filling:SetHeight((30 * 1.086) * rem)
        v.filling:SetTexCoord(0,1,math.abs(rem - 1),1)
        v.filling:SetAlpha(rem/2)
      else
        v.filling:SetHeight(30 * 1.086)
        v.filling:SetTexCoord(0,1,math.abs(0),1)
        v.filling:SetAlpha(curAlpha + 0.08)
      end
    elseif validation3 and (curAlpha < 1) then
      v.filling:SetHeight(30 * 1.086)
      v.filling:SetTexCoord(0,1,math.abs(0),1)
      v.filling:SetAlpha(curAlpha + 0.08)

      if v.resourceAnimation and v.resourceAnimation:IsVisible() then
        v.resourceAnimation:SetAlpha(curAlpha + 0.08)
      end
    elseif validation4 and (curAlpha > 0) then
      v.filling:SetAlpha(curAlpha - 0.08)

      if v.resourceAnimation and v.resourceAnimation:IsVisible() then
        v.resourceAnimation:SetAlpha(curAlpha - 0.08)
      end
    end
  end
end

local updateResourceGroup = function(display, group)
  group = group or "resource"

  if display[group.."GroupUnitId"] then
    if display[group.."GroupUnitId"] == "buff" then
      updateResourceGroupBuff(display, group)
    else
      updateResourceGroupDefault(display, group)
    end
  end
end

local mtResourceMonitor = {
};

function mtResourceMonitor.monitorResource(display)
  local unit = display.config.unit
  if display.config.unit == "vehicle" and not UnitInVehicle("player")then
    ss.Layout:OnPlayerExitedVehicle()
  end

  local maxResourceVal = 0
  local currentVal = 0
  local newPercent = nil

  updateResourceGroup(display, "resource")
  updateResourceGroup(display, "secondaryResource")

  if display.configName == "health" or display.configName == "pet" or display.configName == "vehicle" then
    maxResourceVal = UnitHealthMax(unit)
    currentVal = UnitHealth(unit)
  else
    maxResourceVal = UnitPowerMax(display.config.unit, display.config.unitId)
    currentVal = UnitPower(display.config.unit, display.config.unitId)
  end

  if display.config.secondaryUnitId then
    display.config.previousValue, newPercent = display.config.onMonitorUpdate(currentVal, maxResourceVal,  display.config.realHeight, display.font1, display.fillingLeft, display.config.previousValue, nil, nil, display.config.passed)

    if display.configName == "pet" or display.configName == "vehicle" then
      maxResourceVal = UnitPowerMax(display.config.unit)
      currentVal = UnitPower(display.config.unit)

      display.config.previousSecondaryValue = display.config.onMonitorUpdateSecondary(currentVal, maxResourceVal,  display.config.realHeight, display.font2, display.fillingRight, display.config.previousSecondaryValue, nil, nil, display.config.passed)
    else
      maxResourceVal = UnitPowerMax(display.config.unit, display.config.secondaryUnitId)
      currentVal = UnitPower(display.config.unit, display.config.secondaryUnitId)

      display.config.previousSecondaryValue = display.config.onMonitorUpdateSecondary(currentVal, maxResourceVal,  display.config.realHeight, display.font2, display.fillingRight, display.config.previousSecondaryValue, nil, nil, display.config.passed)
    end
  else
    display.config.previousValue, newPercent = display.config.onMonitorUpdate(currentVal, maxResourceVal,  display.config.realHeight, display.font1, display.filling, display.config.previousValue, display.font2, display.config.textFormat, display.config.passed)

    if display.config.absorbUnitId then
      currentVal = UnitGetTotalAbsorbs(unit)

      display.config.previousAbsorbValue = display.config.onMonitorUpdate(currentVal, maxResourceVal,  display.config.realHeight, nil, display.fillingAbsorb, display.config.previousAbsorbValue, nil, display.config.textFormat, display.config.passed)
    end
  end

  if newPercent then
    display.swirl1:SetAlpha(newPercent)
    display.swirl2:SetAlpha(newPercent)
    display.swirl3:SetAlpha(newPercent)

      -- TODO: Prevent animation from restarting when updating duration
--    display.swirl1:SetDurationPercent(newPercent)
--    display.swirl2:SetDurationPercent(newPercent)
--    display.swirl3:SetDurationPercent(newPercent)
  end

  -- Set the flag to start skipping redundant processing.
  display.config.passed = true
end

function mtResourceMonitor:Watch()
  return self.monitorResource
end

ss["ResourceMonitor"] = {}

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