--[[----------------------------------------------------------------------------

WFDelayQueue.lua

--------------------------------------------------------------------------------

ADOBE CONFIDENTIAL
------------------
Copyright 2012 Adobe Systems Incorporated
All Rights Reserved.

NOTICE: All information contained herein is, and remain the property of
Adobe Systems Incorporated and its suppliers, if any. The intellectual and
technical concepts contained herein are proprietary to Adobe Systems
Incorporated and its suppliers and may be covered by U.S. and Foreign
Patents, patents in process, and are protected by trade secret or copyright
law.  Dissemination of this information or reproduction of this material is
strictly forbidden unless prior written permission is obtained from
Adobe Systems Incorporated.

------------------------------------------------------------------------------]]


--============================================================================--

--[[

WFDelayQueue provides a standard mechanism for queueing objects to be processed
via a function at a later time. It provides the class WFDelayQueue which has
the following interface

	WFDelayQueue( when, fn )
		when indicates when the delay should flush. Currently the only supported
		value is 'idle'. fn is the function to run for each item in the queue.
		The queue is weak and will also not process items that are closed or
		about to be closed.
		
	delayQueue:enqueue( x )
		Enqueues a value for later processing.
		
	delayQueue:runNow()
		Causes the queue to run early.

]]

--============================================================================--

local setmetatable = setmetatable
local assert = assert
local pairs = pairs
local pcall = pcall

	-- RxLua
local Rx = require 'Rx'

local WFArguments = require 'WFArguments'
local WFArguments_check = assert( WFArguments.check )

local WFIdle = require 'WFIdle'
local WFIdle_onIdleDo = assert( WFIdle.onIdleDo )

local WFCloseSupport = require 'WFCloseSupport'
local WFCloseSupport_hasBeenOrWillBeClosed = assert( WFCloseSupport.hasBeenOrWillBeClosed )

--------------------------------------------------------------------------------

local fullyWeakMT = { __mode = 'kv' }

--------------------------------------------------------------------------------

local function getEnqueueForSelector( selector )

	if selector == 'idle' then
		return WFIdle_onIdleDo
	end
	
	assert( false, "Only idle delays are supported at this point." )

end

--------------------------------------------------------------------------------

local WFDelayQueue = Rx.Class 'WFDelayQueue' {

	_queued = false, -- The queue
	_runUpdate = false, -- The function to run when updating
	_askForTime = false, -- The function to call to ask for time
	
	__init = function( self, when, fn )
	
		WFArguments_check( '_', 'string', 'callable' )
		
		self._askForTime = getEnqueueForSelector( when )
		
		-- Note that _runUpdate is a function rather than a method.
		-- It has self and fn bound directly into it.

		self._runUpdate = function()
			local queued = self._queued
			if not queued then
				return
			end
			self._queued = false
			for x in pairs( queued ) do
				if not WFCloseSupport_hasBeenOrWillBeClosed( x ) then
					pcall( fn, x ) -- pcall loops?
				end
			end
		end
		
	end,
	
	enqueue = function( self, x )
		if x == nil then
			return
		end
		if not self._queued then
			self._queued = setmetatable( { }, fullyWeakMT )
			self._askForTime( self._runUpdate )
		end
		self._queued[ x ] = true
	end,
	
	runNow = function( self )
		self._runUpdate()
	end

}

--------------------------------------------------------------------------------

return WFDelayQueue
