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

KSReactivePromiseBridge.lua

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

ADOBE CONFIDENTIAL
------------------
Copyright 2011 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.

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

	-- WF.core.reactive
local KSPromises = require 'KSPromises'
local KSReactive = require 'KSReactive'


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

local rawset = rawset


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

-- @mark KSReactivePromiseBridge namespace

--[[!
	\class KSReactivePromiseBridge

	\brief This namespace allows you to convert behaviors or events to promises,
		and promises to behaviors or events.

]]--

local KSReactivePromiseBridge = {}

--------------------------------------------------------------------------------
--[[!
	\brief Converts a promise into a behavior. (how?)

	\param promiseP The promise object.
	\param ... The default value prior to the promise resolving.

	\return The behavior object.

]]--

function KSReactivePromiseBridge.promiseToBehavior( --[[Promise]] promiseP, ... )

	return promiseP:untilResolvedB( 'data', ... )

end

--------------------------------------------------------------------------------
--[[!
	\brief Converts a promise into an event. (how?)

	\param promiseP The promise object.
 
	\return The event object.

]]--

function KSReactivePromiseBridge.promiseToEvent( --[[Promise]] promiseP )

	-- GC Notes (ownership)
	-- resultE -> varE
	-- resultE -> action -> promiseP
	
	-- GC Notes (shutdown)
	-- action is close target of resultE

	local sourceE = KSReactive.sourceE()
	
	local resultE = sourceE:relayE() -- the result
	
	promiseP:whenResolvedDo {

		data = function( status, value, ... )
			sourceE:post( value, ... )
			sourceE:close()
		end,

		error = function( status, ... )
			sourceE:postError( ... )
			sourceE:close()
		end

	}
	:suppressCloseLogging()
	:attachTo( resultE )
	
	return resultE

end

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

local PROMISES_RETAIN_EVENTS = true
	-- Set this to true to have promises generated by eventToPromise (and
	-- behaviorToPromise) retain the event stream. Note that doing so will
	-- cause testEventToPromiseLost to fail. The idea behind that test was
	-- to detect cases where the event stream would never execute again because
	-- nothing would post to it. We need to solve that in other ways.

--------------------------------------------------------------------------------
--[[!
	\brief Converts an event into a promise. (how?)

	\param eventE The event object.
 
	\return The promise object.

]]--

--[[

	The graph:
	
		resolver owns the bridges (onData, etc) from the event
		the bridges point to the resolver
		the promise points to the bridges
		
	If the promise gets collected, then the resolver will close
	which will close the bridges.
	
	(Hmm. We could almost do this with simple GC since there
	are no forward links and then we wouldn't need the objects
	that watch for collection of the promise.)

]]

local function eventToPromise( --[[Event]] eventE )

	local promiseP, resolver = KSPromises.promiseP( true )
	
	local firstE = eventE:takeE( 1 )
	
	local dataE = firstE:ownedOnDataE( resolver, function( ... )

		if resolver then
			resolver( 'data', ... )
			resolver = nil
		end

	end )
	
	local errorE = firstE:ownedOnErrorE( resolver, function( ... )

		if resolver then
			resolver( 'error', ( ... ) or "< Unknown Error >" )
			resolver = nil
		end

	end )
	
	local function resolveDone()
		if resolver then
			resolver()
			resolver = nil
		end
	end
	
	local doneE
	
	if firstE:isDone() then
		resolveDone()
	else
		doneE = firstE:ownedOnDoneE( resolver, resolveDone )
	end
	
	-- Attach the event hooks to the promise so that if the promise stays
	-- alive, we keep the event alive.
	
	local resultP
	
	if PROMISES_RETAIN_EVENTS then
	
		rawset( promiseP, 'private:eventLink', { dataE, errorE, doneE } )
		
		resultP = promiseP:observeP( function()
			rawset( promiseP, 'private:eventLink', nil )
		end )
		
	else
	
		rawset( resolver, 'private:eventLink', { dataE, errorE, doneE } )
		
		resultP = promiseP
	
	end
	
	return resultP, resolver

end

--------------------------------------------------------------------------------
--[[!
	Converts a behavior into a promise that reports the first
	behavior value that passes a filter function.

	\param behaviorB The behavior object.
	\param filter The filter function on the behavior value.
 
	\return The promise object and the resolver.
]]--

local function behaviorToPromise_imp( --[[Behavior]] behaviorB, --[[function]] filter )

	-- Return a promise that reports the first value from behaviorB that
	-- passes filter.
	
	local isError, message = behaviorB:isErrorNow()
	
	if isError then
		return KSPromises.resolvedPromiseP( 'error', message or "< Unknown Error >" )
	end
	
	if filter( behaviorB:valueNow() ) then
		return KSPromises.resolvedPromiseP( 'data', behaviorB:valueNow() )
	end
	
	return eventToPromise( behaviorB:filterE( filter ) )
	
end

--------------------------------------------------------------------------------
--[[!
	\brief Converts a behavior into a promise that reports the first
	behavior value that passes a filter function.

	\param behaviorB The behavior object.
	\param filter The filter function on the behavior value.
 
	\return The promise object.
]]--

function KSReactivePromiseBridge.behaviorToPromise( --[[Behavior]] behaviorB, --[[function]] filter )

	return ( behaviorToPromise_imp( behaviorB, filter ) )
	
end

--------------------------------------------------------------------------------
--[[!
	\brief Converts a behavior into a promise that reports the first
	behavior value that passes a filter function.

	\param behaviorB The behavior object.
	\param filter The filter function on the behavior value.
 
	\return The promise object.
]]--

function KSReactivePromiseBridge.closingBehaviorToPromise(
				--[[Behavior]] behaviorB, --[[function]] filter )

	local p, r = behaviorToPromise_imp( behaviorB, filter )
	
	if r then
		r:addCloseTarget( behaviorB )
	else
		behaviorB:close()
	end
	
	return p
	
end

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

return KSReactivePromiseBridge
