---@class ThreadLib
local ThreadLib = QuestieLoader:CreateModule("ThreadLib")

--- COMPATIBILITY ---
local C_Timer = QuestieCompat.C_Timer

--Coroutine functions
local coStatus, coResume, coCreate = coroutine.status, coroutine.resume, coroutine.create
local lType = type
-- local cTimer = C_Timer
local newTicker = C_Timer.NewTicker


---Thread a function, callback function is called when the thread is done.
---@param threadFunction function @The function to thread
---@param delay integer @Anything below 0.05 is each frame
---@param errorMessage string? @What is the "Prepend" of the error message
---@param callbackFunction function? @Function to call when the thread is done
---@return Ticker Timer @The WoW timer, run Timer:Cancel() and let the handle of the thread become orphaned to cancel
---@return thread Thread @The coroutine thread
function ThreadLib.Thread(threadFunction, delay, errorMessage, callbackFunction)
  if lType(threadFunction) ~= "function" then
    error("ThreadLib:Thread: threadFunction is not a function")
  end
  if lType(delay) ~= "number" then
    error("ThreadLib:Thread: delay is not a number")
  end
  if errorMessage and lType(errorMessage) ~= "string" then
    error("ThreadLib:Thread: errorMessage is not a string")
  end
  if callbackFunction and lType(callbackFunction) ~= "function" then
    error("ThreadLib:Thread: callbackFunction is not a function")
  end

  local thread = coCreate(threadFunction)

  local timer
  timer = newTicker(delay or 0, function()
      if(coStatus(thread) == "suspended") then --It's faster not to lookup the value but instead have it here
        local success, ret = coResume(thread)
        -- Something in the coroutine went wrong, print the error and stop the timer
        if not success then
            Questie:Error(errorMessage or "Error in thread", ret)
            timer:Cancel();
        end
      elseif (coStatus(thread) == "dead") then --It's faster not to lookup the value but instead have it here
        timer:Cancel();
        if(callbackFunction) then
          callbackFunction()
        end

        --? Is this needed?
        timer = nil
        ---@diagnostic disable-next-line: cast-local-type
        thread = nil
      end
  end)
  return timer, thread
end

---Thread a function, callback function is called when the thread is done.
---@param threadFunction function @The function to thread
---@param delay integer @Anything below 0.05 is each frame
---@param callbackFunction function @Function to call when the thread is done
---@return Ticker Timer @The WoW timer, run Timer:Cancel() and let the handle of the thread become orphaned to cancel
---@return thread Thread @The coroutine thread
function ThreadLib.ThreadCallback(threadFunction, delay, callbackFunction)
  return ThreadLib.Thread(threadFunction, delay, nil, callbackFunction)
end

---Thread a function, using a specific error message.
---@param threadFunction function @The function to thread
---@param delay integer @Anything below 0.05 is each frame
---@param errorMessage string @What is the "Prepend" of the error message
---@return Ticker Timer @The WoW timer, run Timer:Cancel() and let the handle of the thread become orphaned to cancel
---@return thread Thread @The coroutine thread
function ThreadLib.ThreadError(threadFunction, delay, errorMessage)
  return ThreadLib.Thread(threadFunction, delay, errorMessage)
end

---Thread a function
---@param threadFunction function @The function to thread
---@param delay integer @Anything below 0.05 is each frame
---@return Ticker Timer @The WoW timer, run Timer:Cancel() and let the handle of the thread become orphaned to cancel
---@return thread Thread @The coroutine thread
function ThreadLib.ThreadSimple(threadFunction, delay)
  return ThreadLib.Thread(threadFunction, delay)
end


--? This was kind of a halv baked idea, that i questioned was even good, but i don't really want to delete it yet.
--[[

  ---@class Thread
  ---@field private _thread thread
  ---@field private _timer Ticker
  ---@field private _callback function?
  ---@field Kill fun()
  local newThread = {
    _thread = coCreate(threadFunction),
    _callback = callbackFunction,

    Continue = ThreadContinue,

    ---@param self Thread
    Kill = function(self)
      print(Questie.DEBUG_CRITICAL, "[ThreadLib] Thread cancelled")
      self._timer:Cancel()
      self._thread = nil
      self._timer = nil
      self.Kill = nil
      self.Continue = nil
    end
  }

  newThread._timer = newTicker(delay or 0, function()
      if(coStatus(newThread._thread) == "suspended") then --It's faster not to lookup the value but instead have it here
        local success, ret = coResume(newThread._thread)
        -- Something in the coroutine went wrong, print the error and stop the timer
        if not success then
            Questie:Error(errorMessage or "Error in thread", ret)
            newThread._timer:Cancel();
        end
      elseif (coStatus(newThread._thread) == "dead") then --It's faster not to lookup the value but instead have it here
        newThread._timer:Cancel();
        if(newThread._callback) then
          callbackFunction()
        end
        newThread._thread = nil
        newThread._timer = nil
        wipe(newThread)
      end
  end)

  return newThread

]]--
