-------------------------------------
-- Sphere Resource Monitor Creator --
-------------------------------------
local AddOnName, nsVars = ...
local ss = nsVars.SS

local setmetatable = setmetatable
local GetTime, random = GetTime, random
local UnitGetTotalAbsorbs, UnitInVehicle = UnitGetTotalAbsorbs, UnitInVehicle
local UnitHealth, UnitHealthMax, UnitPower, UnitPowerMax = UnitHealth, UnitHealthMax, UnitPower, UnitPowerMax

--- Resource Emulation
--
-- Controls the target value for the emulated resource.
-- Once a value is reached, it is updated to cause a fluctuation between (5-45%) and (55-95%)
-- Ensures a short duration has elapsed before changing a target.
-----------------------------------------------------------------------------------------------
local waitDuration = 10
local waitDurationSecondary = 5

local _mtEmulator = {
  maxVal = 100000,
  prevCurTargetVal = 0.1,
  prevTargetVal    = 0,
  prevPVal         = 100000,
  prevCurTargetValSecond = 0.1,
  prevTargetValSecond    = 0,
  prevPValSecond         = 100000,
  mockResourceValue = function(self)
    if (self.prevPVal >= (self.maxVal * self.prevCurTargetVal)) and (self.prevCurTargetVal > 0.5) then
      if not self.waitDuration then
        self.waitDuration = GetTime() + (random(waitDuration) / 10)
      elseif self.waitDuration and self.waitDuration < GetTime() then
        -- Set a new target value below 45%.
        self.prevCurTargetVal = random(5, 45) / 100
        self.prevTargetVal = (self.maxVal * self.prevCurTargetVal) - 0.01
        self.waitDuration = nil
      end
    elseif (self.prevPVal <= (self.maxVal * self.prevCurTargetVal)) and (self.prevCurTargetVal < 0.5) then
      if not self.waitDuration then
        self.waitDuration = GetTime() + (random(waitDuration) / 10)
      elseif self.waitDuration and self.waitDuration < GetTime() then
        -- Set a new target value above 55%.
        self.prevCurTargetVal = random(55, 95) / 100
        self.prevTargetVal = (self.maxVal * self.prevCurTargetVal) + 0.01
        self.waitDuration = nil
      end
    end
    
    return self.prevTargetVal
  end,
  mockResourceValueSecond = function(self)
    if (self.prevPValSecond >= (self.maxVal * self.prevCurTargetValSecond)) and (self.prevCurTargetValSecond > 0.5) then
      if not self.waitDurationSecond then
        self.waitDurationSecond = GetTime() + (random(waitDurationSecondary) / 10)
      elseif self.waitDurationSecond and self.waitDurationSecond < GetTime() then
        -- Set a new target value below 45%.
        self.prevCurTargetValSecond = random(5, 45) / 100
        self.prevTargetValSecond = (self.maxVal * self.prevCurTargetValSecond) - 0.01
        self.waitDurationSecond = nil
      end
    elseif (self.prevPValSecond <= (self.maxVal * self.prevCurTargetValSecond)) and (self.prevCurTargetValSecond < 0.5) then
      if not self.waitDurationSecond then
        self.waitDurationSecond = GetTime() + (random(waitDurationSecondary) / 10)
      elseif self.waitDurationSecond and self.waitDurationSecond < GetTime() then
        -- Set a new target value above 55%.
        self.prevCurTargetValSecond = random(55, 95) / 100
        self.prevTargetValSecond = (self.maxVal * self.prevCurTargetValSecond) + 0.01
        self.waitDurationSecond = nil
      end
    end
    
    return self.prevTargetValSecond
  end,
}

local mtResourceMonitor = {
  monitorResource = function(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

    -- Update the micro spheres.
    display:UpdateMicroSpheres()

    -- Get first value.
    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
      -- Update the first value.
      display.config.previousValue, newPercent = display.config.onMonitorUpdate(
        currentVal,
        maxResourceVal,
        display.config.realHeight,
        display.font1,
        display.fillingLeft,
        display.config.previousValue,
        nil,
        nil,
        display.config.passed)

      -- Get second value.
      if (display.configName == "pet") or (display.configName == "vehicle") then
        maxResourceVal = UnitPowerMax(display.config.unit)
        currentVal     = UnitPower   (display.config.unit)
      else
        maxResourceVal = UnitPowerMax(display.config.unit, display.config.secondaryUnitId)
        currentVal     = UnitPower   (display.config.unit, display.config.secondaryUnitId)
      end

      -- Update the second value.
      display.config.previousSecondaryValue = display.config.onMonitorUpdateSecondary(
        currentVal,
        maxResourceVal,
        display.config.realHeight,
        display.font2,
        display.fillingRight,
        display.config.previousSecondaryValue,
        nil,
        nil,
        display.config.passed)
    else
      -- Update the first value.
      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
        -- Update the absorb value.
        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,
  emulateResource = function(display)
    if display._emu.emulate == true then
      -- DEBUG: manually manage height value with the swirl color (alpha) slider
--      local _, _, _, debugHeight = ss.ConfigPreview.widgetFrame.xmlSphereDisplayFrame2.sphere:getSwirlColor()
    
      -- Begin emulating the resource.
      local newPercent = nil
    
      -- Mock resource changes.
      display._emu.prevPVal, newPercent = display._emu.resourceUpdate(
        display._emu:mockResourceValue(),
--        display._emu.maxVal * debugHeight, -- DEBUG: manually manage height value, above line MUST be commented.
        display._emu.maxVal,
        display._emu.isDual and (150 * 0.95) or (150 * 1.086),
        display.font1,
        display._emu.isDual and display.fillingLeft or display.filling,
        display._emu.prevPVal,
        display._emu.isDual and nil or display.font2,
        display._emu.isDual and nil or display.textFormat,
        false)
      
      if display._emu.isDual then
        display._emu.prevPValSecond = display._emu.resourceUpdate(
          display._emu:mockResourceValueSecond(),
          --          display._emu.maxVal * debugHeight, -- DEBUG: manually manage height value, above line MUST be commented.
          display._emu.maxVal,
          150 * 0.95,
          display.font2,
          display.fillingRight,
          display._emu.prevPValSecond,
          nil,
          nil,
          false)
      end
      
      if newPercent then
        display.swirl1:SetAlpha(newPercent)
        display.swirl2:SetAlpha(newPercent)
        display.swirl3:SetAlpha(newPercent)
      end
    else
      -- Begin terminating the emulation.
      local newPercent = nil
      
      -- Reset the filling height to 100%.
      display._emu.prevPVal, newPercent = display._emu.resourceUpdate(
        display._emu.maxVal,
        display._emu.maxVal,
        display._emu.isDual and (150 * 0.95) or (150 * 1.086),
        display.font1,
        display._emu.isDual and display.fillingLeft or display.filling,
        display._emu.prevPVal,
        display._emu.isDual and nil or display.font2,
        display._emu.isDual and nil or display.textFormat,
        false)
      
      if display._emu.isDual then
        display._emu.prevPValSecond = display._emu.resourceUpdate(
          display._emu.maxVal,
          display._emu.maxVal,
          150 * 0.95,
          display.font2,
          display.fillingRight,
          display._emu.prevPValSecond,
          nil,
          nil,
          false)
      end
      
      if newPercent then
        display.swirl1:SetAlpha(newPercent)
        display.swirl2:SetAlpha(newPercent)
        display.swirl3:SetAlpha(newPercent)
      end
      
      if display._emu.prevPVal >= (display._emu.maxVal * 0.99999) and (display._emu.isDual and (display._emu.prevPValSecond >= (display._emu.maxVal * 0.99999)) or true) then
        -- End the emulation and remove the emulator.
        display:SetScript("OnUpdate", nil)
        display._emu = nil
      end
    end
  end,
};

--- Provides the resource monitor event handler.
-- @return the update handler.
--
function mtResourceMonitor:Watch()
  return self.monitorResource
end

--- Emulates resource changes on a display.
-- @param display The display to use.
-- @param resourceUpdate The update handler.
-- @param isDual Flag for dual resources.
--
function mtResourceMonitor:Emulate(display, resourceUpdate, isDual)
  if display._emu then
    -- Restart the existing component.
    display._emu.resourceUpdate = resourceUpdate
    display._emu.isDual  = isDual
    display._emu.emulate = true
  else
    -- Create and attach a new emulator to the display.
    local _emu = {
      resourceUpdate = resourceUpdate,
      isDual  = isDual,
      emulate = true,
    }
  
    setmetatable(_emu, _mtEmulator)
    _mtEmulator.__index = _mtEmulator
    display._emu = _emu
  
    display:SetScript("OnUpdate", self.emulateResource)
  end
end

--- Completes an ongoing emulation.
-- @param display The target display.
--
function mtResourceMonitor:EmulateFinish(display)
  -- Clear the emulate property so the monitor can begin completion.
  display._emu.emulate = nil
end

ss["ResourceMonitor"] = {}

--- Creates a new resource monitor.
-- @return The resource monitor.
--
function ss.ResourceMonitor:New()
  local instance = {}
  setmetatable(instance, mtResourceMonitor)
  mtResourceMonitor.__index = mtResourceMonitor

  return instance
end
