-- ------------------------------------------------------------------------------ --
--                                TradeSkillMaster                                --
--                http://www.curse.com/addons/wow/tradeskill-master               --
--                                                                                --
--             A TradeSkillMaster Addon (http://tradeskillmaster.com)             --
--    All Rights Reserved* - Detailed license information included with addon.    --
-- ------------------------------------------------------------------------------ --

TSMAPI_FOUR.Delay = {}
local _, TSM = ...
local Delay = TSM:NewPackage("Delay")
local private = { delays = {}, frameNumber = 0, frame = nil }
local CALLBACK_TIME_WARNING_THRESHOLD_MS = 20



-- ============================================================================
-- Module Functions
-- ============================================================================

function Delay.OnInitialize()
	private.frame = CreateFrame("Frame")
	private.frame:Show()
	private.frame:SetScript("OnUpdate", private.ProcessDelays)
end



-- ============================================================================
-- TSMAPI Functions
-- ============================================================================

function TSMAPI_FOUR.Delay.AfterTime(...)
	local label, duration, callback, repeatDelay = nil, nil, nil, nil
	if type(select(1, ...)) == "number" then
		duration, callback, repeatDelay = ...
	else
		label, duration, callback, repeatDelay = ...
	end
	assert(type(duration) == "number" and type(callback) == "function" and (not repeatDelay or type(repeatDelay) == "number"), format("invalid args '%s', '%s', '%s', '%s'", tostring(label), tostring(duration), tostring(callback), tostring(repeatDelay)))

	if label then
		for _, delay in ipairs(private.delays) do
			if delay.label == label then
				-- delay is already running, so just return
				return
			end
		end
	else
		label = TSMAPI_FOUR.Util.GetDebugStackInfo(2)
	end

	local delayTbl = TSMAPI_FOUR.Util.AcquireTempTable()
	delayTbl.endTime = GetTime() + duration
	delayTbl.callback = callback
	delayTbl.label = label
	delayTbl.repeatDelay = repeatDelay
	tinsert(private.delays, delayTbl)
end

function TSMAPI_FOUR.Delay.AfterFrame(...)
	local label, duration, callback, repeatDelay = nil, nil, nil, nil
	if type(select(1, ...)) == "number" then
		duration, callback, repeatDelay = ...
	else
		label, duration, callback, repeatDelay = ...
	end
	assert(type(duration) == "number" and type(callback) == "function" and (not repeatDelay or type(repeatDelay) == "number"), format("invalid args '%s', '%s', '%s', '%s'", tostring(label), tostring(duration), tostring(callback), tostring(repeatDelay)))

	if label then
		for _, delay in ipairs(private.delays) do
			if delay.label == label then
				-- delay is already running, so just return
				return
			end
		end
	else
		label = TSMAPI_FOUR.Util.GetDebugStackInfo(2)
	end

	local delayTbl = TSMAPI_FOUR.Util.AcquireTempTable()
	delayTbl.endFrame = private.frameNumber + duration
	delayTbl.callback = callback
	delayTbl.label = label
	delayTbl.repeatDelay = repeatDelay
	tinsert(private.delays, delayTbl)
end

function TSMAPI_FOUR.Delay.Cancel(label)
	for i, delay in ipairs(private.delays) do
		if delay.label == label then
			TSMAPI_FOUR.Util.ReleaseTempTable(tremove(private.delays, i))
			return
		end
	end
end



-- ============================================================================
-- Main Delay Callback
-- ============================================================================

function private.ProcessDelays()
	private.frameNumber = private.frameNumber + 1
	for i = #private.delays, 1, -1 do
		local delay = private.delays[i]
		local label = delay.label
		if delay.endFrame and delay.endFrame <= private.frameNumber then
			-- the end time has passed
			local callback = delay.callback
			if delay.repeatDelay then
				delay.endFrame = private.frameNumber + delay.repeatDelay
			else
				TSMAPI_FOUR.Util.ReleaseTempTable(tremove(private.delays, i))
			end
			local startTime = debugprofilestop()
			callback()
			local timeTaken = debugprofilestop() - startTime
			if timeTaken > CALLBACK_TIME_WARNING_THRESHOLD_MS then
				TSM:LOG_WARN("Time delay callback (%s) took %0.2fms", label, timeTaken)
			end
		elseif delay.endTime and delay.endTime <= GetTime() then
			-- the end time has passed
			local callback = delay.callback
			if delay.repeatDelay then
				delay.endTime = GetTime() + delay.repeatDelay
			else
				TSMAPI_FOUR.Util.ReleaseTempTable(tremove(private.delays, i))
			end
			local startTime = debugprofilestop()
			callback()
			local timeTaken = debugprofilestop() - startTime
			if timeTaken > CALLBACK_TIME_WARNING_THRESHOLD_MS then
				TSM:LOG_WARN("Time delay callback (%s) took %0.2fms", label, timeTaken)
			end
		end
	end
end
