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

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

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

local KSTableUtils_derefOpt = require 'KSTableUtils'.derefOpt

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

local WFModelsUtils = {}

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

function WFModelsUtils.aggregateModels( result, ... )
	
	for _, models in ipairs { ... } do
		for key, model in pairs( models ) do
			assert( not result[ key ], "Duplicate model definition for : " .. key )
			result[ key ] = model
		end
	end
	
	return result
	
end

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

local function getSubmodel( parentModel, submodelName )

	return parentModel.models[ submodelName ]

end

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

function WFModelsUtils.makeSubmodel( parentModel, submodelName, ... )
	
	local submodelFn = getSubmodel( parentModel, submodelName )

	return submodelFn and submodelFn( parentModel, ... )

end

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

function WFModelsUtils.callFunctionOrSubmodel( rxTest, optSubmodelsByImplementation, wfCatalog, fnNamespace, fnName, ... )

	if optSubmodelsByImplementation then
	
		-- Check to see if there exists a pass-through submodel that calls the specified
		-- function in the specified namespace.
	
		local submodelInfo = KSTableUtils_derefOpt( optSubmodelsByImplementation, fnNamespace, fnName )

		local submodelName = submodelInfo and submodelInfo.submodelName 
				or fnName
		
		local parentModelConstructorFn = submodelInfo and submodelInfo.parentModelConstructorFn or 
				optSubmodelsByImplementation.defaultParentModelConstructorFn

		local parentModel = parentModelConstructorFn( rxTest, wfCatalog )
		
		-- For submodels, the first parameter (wfCatalog) is provided by the model itself,
		-- so we strip it out.
		
		local getArgsForSubmodel = function( _, ... )
			
			return ...
		
		end

		local submodel = WFModelsUtils.makeSubmodel( parentModel, submodelName, getArgsForSubmodel( ... ) )
		
		-- only return submodel results if submodel exists
		
		if submodel then
		
			rxTest:finally( function()
			
				-- When the test ends, make sure the models are closed and gc'ed.
				
				parentModel.close()
				parentModel = nil
				submodel = nil
				
				collectgarbage()
				collectgarbage()
				collectgarbage()
				
			end )
		
			local submodelResultStream = submodel.value or submodel
			
			return submodelResultStream or submodel, parentModel
			
		end
		
	end 

	-- If no submodel exists, call the specified function in the specified
	-- namespace directly.
	
	return require( fnNamespace )[ fnName ]( ... )
	
end

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

local function pack( ... )

	return { ... }, select( '#', ... )

end

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

local function getNonNilWFCatalogStream( wfCatalogStream )
	return wfCatalogStream
		:filter(
			function( wfCatalog )

				return wfCatalog ~= nil

			end )
		:take( 1 )
end

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

function WFModelsUtils.createModelForFunction( wfCatalogStream, fn, ... )

	local ret, nRet = pack( ... )
	
	local result = getNonNilWFCatalogStream( wfCatalogStream )
	
		:flatMap( function( wfCatalog )
		
			return fn( wfCatalog, unpack( ret, 1, nRet ) )
			
		end )
		
	return {
		value = result
	}

end

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

return WFModelsUtils
