--- Extends Objects With Additional Functionality
--
local Extentions = {}
local AddOnName, nsVars = ...
local ss = nsVars.SS

local type, pairs = type, pairs
local IsShiftKeyDown = IsShiftKeyDown

--- Calls SetVertexColor using table values.
-- @param obj The Frame object.
-- @param color The color table.
--
local function SetVertColor(obj, color)
  obj:SetVertexColor(ss.Utils:GetRGBAValues(color))
end

--- Animates an alpha change.
-- @param display The Frame.
-- @param target The target alpha.
-- @param onComplete Completion handler. (Optional)
-- @param onUpdate OnUpdate handler. (Optional)
--
local function Fade(display, target, onComplete, onUpdate)
  local fadeDuration  = 0.1
  target = target or 0
  
  if target == display.fadeTarget then
    return
  end
  
  ss.Extentions:Hook(display.fadeAnimGroup, "OnFinished", nil)
  ss.Extentions:Hook(display.fadeAnimGroup, "OnUpdate", nil)
  
  display.fadeTarget = target
  display.fadeOnComplete = onComplete
  display.fadeOnUpdate = onUpdate
  
  -- Retrieve current values to handle ongoing animations.
  local dur = (display.fadeAnimGroup.fade:GetProgress() * fadeDuration) or fadeDuration
  
  if not display.fadeAnimGroup:IsDone() then
    display.fadeAnimGroup:Stop()
  else
    dur = fadeDuration
  end
  
  -- Set animation values.
  display.fadeAnimGroup.fade:SetFromAlpha(display.fadeCurAlpha or display:GetAlpha())
  display.fadeAnimGroup.fade:SetToAlpha  (target)
  display.fadeAnimGroup.fade:SetDuration (dur)
  display.fadeAnimGroup:Play()
  
  ss.Extentions:Hook(display.fadeAnimGroup, "OnFinished", function()
    -- Apply the alpha change.
    display:SetAlpha(display.fadeTarget)
    display.fadeCurAlpha = display.fadeTarget
  
    if type(display.fadeOnComplete) == "function" then
      display.fadeOnComplete(display)
    end
    display.fadeOnComplete = nil
    display.fadeOnUpdate = nil
  end)
  
  ss.Extentions:Hook(display.fadeAnimGroup, "OnUpdate", function()
    -- Ensure the unit frames is responsive imediately.
    display.fadeCurAlpha = display:GetAlpha()
  
    if type(display.fadeOnUpdate) == "function" then
      display.fadeOnUpdate(display)
    end
  end)
end

--- Adds the SetVertColor functionality.
-- @param ... One or more Frames.
--
function Extentions:DelegateVertexColor(...)
  local obj = {...}

  for _, v in pairs(obj) do
    if not v.SetVertexColor then
      ss.Utils.throw(ss.Utils:stringf("Object (%s) does not implement SetVertexColor", type(v)), 2)
    else
      v.SetVertColor = SetVertColor
    end
  end
end

function Extentions:Hook(frame, event, hookFunc, tag)
  frame["_hook"..event] = frame["_hook"..event] or {}
  frame["_hook"..event][tag or "_"] = hookFunc
  
  if not frame["_hooked"..event] then
    frame["_hooked"..event] = true
    
    frame:HookScript(event, function(self, ...)
      for _, hook in pairs(self["_hook"..event]) do
        hook(self, ...)
      end
    end)
  end
end

--- Makes a frame movable whike holding the selected key.
-- @param frame The target frame.
-- @param button The button to hold, defaults to "LeftButton".
-- @param listener optional callback for "OnDragStop".
--
function Extentions:MakeMovable(frame, button, listener)
  if type(frame) == "table" then
    frame:SetClampedToScreen(true)
    frame:SetMovable(true)
    frame:EnableMouse(true)
    frame:RegisterForDrag(button or "LeftButton")
    
    self:Hook(frame, "OnDragStart", function(self)
      if IsShiftKeyDown() then
        self:StartMoving()
      end
    end)
    
    self:Hook(frame, "OnDragStop", function(self)
      self:StopMovingOrSizing()
      
      if listener then
        listener(self)
      end
    end)
  else
    ss.Utils.throw(ss.Utils.Errors.ErrorInvalidArgs, "Extentions", "MakeMovable",
      {"frame", "Frame", frame}
    )
  end
end

--- Makes a frame movable whike holding the selected key.
-- @param frame The target frame.
-- @param button The button to hold, defaults to "LeftButton".
-- @param listener optional callback for "OnDragStop".
--
function Extentions:MakeFadable(...)
  local frames = {...}
  
  for _, frame in pairs(frames) do
    if type(frame) == "table" then
      frame.fadeCurAlpha = frame:GetAlpha()
      frame.fadeTarget = frame.fadeCurAlpha
      frame.fadeAnimGroup = frame.fadeAnimGroup or frame:CreateAnimationGroup()
      frame.fadeAnimGroup.fade = frame.fadeAnimGroup.fade or frame.fadeAnimGroup:CreateAnimation("Alpha")
      frame.Fade = Fade
    else
      ss.Utils.throw(ss.Utils.Errors.ErrorInvalidArgs, "Extentions", "MakeFadable",
        {"frame", "Frame", frame}
      )
    end
  end
end

ss.Utils.addModule("Extentions", Extentions)
