--- Pending Task
--
local PendingTask = {}
local AddOnName, nsVars = ...
local ss = nsVars.SS

local InCombatLockdown, GetTime = InCombatLockdown, GetTime
local table, pairs, type, next, unpack = table, pairs, type, next, unpack
local ErrorInvalidArgs = ss.Utils.Errors.ErrorInvalidArgs
local throw = ss.Utils.throw

local pendingTasks = {}
local delayedTasks = {}
local i

local eventFrame = CreateFrame("Frame")

eventFrame:SetScript("OnUpdate", function()
  -- Attempt to complete delayed tasks.
  for k, v in pairs(delayedTasks) do
    -- Use the function call as part of the validation.
    if type(v) ~= "function" or v() == true then
      -- if the result of v() is true, the task was completed.
      table.remove(delayedTasks, k)
    end
  end
end)

--- Completes all pending tasks.
-- @param notify Flag to display a message to the use. (Optional)
--
function PendingTask.completeAll(notify)
  -- Check for pending tasks
  if next(pendingTasks) == nil then
    return
  end
  
  local completed = {};
  
  for k,v in pairs(pendingTasks) do
    if InCombatLockdown() then
      break
    end
    
    if type(v._f) == "function" then
      v._f()
      if notify and v._t then
        ss.Utils:printf(ss.T["msg_task_completed"], AddOnName, v._t)
      end
    elseif notify and v._t then
      ss.Utils:printf(ss.T["msg_task_failed"], AddOnName, v._t)
    end
    
    table.insert(completed, k)
  end
  
  for k,v in pairs(completed) do
    pendingTasks[v] = nil
  end
end

--- Adds a task to execute at a later time.
-- @param name The name of the task.
-- @param task The function to call.
-- @param text The text to display to the user. (Optional)
--
function PendingTask.add(name, task, text)
  -- TODO: If running, run dont add.
  pendingTasks[name] = {
    _f = task,
    _t = text,
  }
  
  if not InCombatLockdown() and ss.playerLoggedIn then
    ss.PendingTask.completeAll()
  elseif ss.playerLoggedIn and text then
    ss.Utils:printf(ss.T["msg_will_be_called"], AddOnName, text)
  end
end

--- Removes a pending task.
-- @param name The name of the task.
--
function PendingTask.remove(name)
  pendingTasks[name] = nil
end

--- Calls a function after a given duration.
--
-- Usage:
--
-- ss.PendingTask.delay(5, function(var)
--   print(var)
-- end, restdouble2)
--
-- @param delay The execution delay.
-- @param task The function to call.
-- @param ... The function parameters. (Optional)
--
function PendingTask.delay(delay, task, ...)
  if type(delay) == "number" and type(task) == "function" then
    local time = GetTime()
    local params = {...}
  
    -- Wrap the task in a function tracking elapsed time.
    local function delayedTask()
      if GetTime() >= time + delay then
        task(unpack(params))
      
        -- Return true to notify completion.
        return true
      end
    end
  
    table.insert(delayedTasks, delayedTask)
  else
    throw(ErrorInvalidArgs, "PendingTask", "delay",
      {"delay", "number", delay},
      {"task", "function", task}
    )
  end
  
end

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