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

WFFunctionUtils.lua

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

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.

------------------------------------------------------------------------------]]
--[[


	Various functions to help with functional programming style

	These three are useful for transforming arrays

	function map( fn, array, ... )
		-- returns a new array by applying function fn to each element in
		-- array. The fn is invoked as fn( array[ i ], ... ) for each element
		-- array[ i ] of array. Each return value of fn( array[ i ], ... ) is
		-- inserted as a scalar. Therefore if fn( array[ i ], .... ) returns
		-- nill the new array will be sparse.

	function filter( fn, array, ... )
		-- returns a new array by applying function fn to each element in
		-- array. The fn is invoked as fn( array[ i ], ... ) for each element
		-- array[ i ] of array. If the fn( array[ i ], ... ) returns nil or nothing it
		-- is not added to the returned array. Therefore the new array may not
		-- have as many elements as the original but is guaranteed not to be
		-- sparse.

	function reduce( acc, fn, array, ... )
		-- returns a new value by applying function fn to each element in
		-- array. The fn is invoked as acc = fn( acc, array[ i ], ... ) for
		-- each element array[ i ] of array. 
		-- 
	
	function multiReturnReduce( fn, array, ... )
		-- this is like reduce except:
		-- 
		-- 	1) Function signature is different: 
		-- 			f( array[ i ], ... )
		-- 			
		--  2) multiple values are returned. If select( '#', ... ) == n then 
		--     n values will be returned at the end.
		--     
		--  Note: the ... are very flexible, some may used as accumulators, 
		--  others may be used as parameters to f.
		--  
		--  Example:
		-- 	-- notice that the function signature has all the values it needs, some
		-- 	-- are accumulators, others are used to filter.
		-- 	local func partitionByType( albumIdOrAssetId, assetIds, albumIds, albumIdMap )
		-- 
		-- 		local list = albumIdMap[ albumIdOrAssetId ] and albumIds or assetIds
		-- 
		-- 		list[ #list + 1 ] = albumIdOrAssetId
		-- 
		-- 		-- we just keep returning ... for the recursion, but the final result
		-- 		-- doesn't need to capture all of these ret vals
		-- 		return assetIds, albumIds, albumIdMap
		-- 
		-- 	end
		-- 
		-- 	local albumIdMap -- computed for us elsewhere
		-- 
		-- 	-- We build out two separate arrays while iterating a list once
		-- 	local assetIds, albumIds =
		-- 		multiReturnReduce(
		-- 			partitionByType,
		-- 				docIds,
		-- 				{}, {}, albumIdMap )
		--  
		--     
		-- Use this when you want to build up multiple return values while iterating
		-- an array once

]]

local KSTableUtils = require 'KSTableUtils'
local WFArguments = require 'WFArguments'
local WFArguments_check = assert( WFArguments.check, 
										"missing WFArguments.check" )

local WFFunctionUtils = {}

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

WFFunctionUtils.map = function( fn, array, ... )

	WFArguments_check( 'function', 'table' )

	local ret = {}

	for i, v in ipairs( array ) do

		ret[ i ] = fn( v, ... )

	end

	return ret

end

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

WFFunctionUtils.filter = function( fn, array, ... )

	WFArguments_check( 'function', 'table' )

	local ret = {}
	local j = 1

	for _, v in ipairs( array ) do

		local newV = fn( v, ... )

		if newV ~= nil then

			ret[ j ] = newV

			j = j  + 1

		end

	end

	return ret

end

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

WFFunctionUtils.reduce = function( acc, fn, array, ... )

	WFArguments_check( '_', 'function', 'table' )

	local ret = acc

	for _, v in ipairs( array ) do

		ret = fn( ret, v, ... )

	end

	return ret

end

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

WFFunctionUtils.weakBind1st = function( fn, arg1 )

	WFArguments_check( 'function', '!nil' )

	local weakArg1Container = KSTableUtils.makeWithMode( "kv" )

	weakArg1Container[ arg1 ] = arg1

	return function( ... )

		local revivedArg1 = next( weakArg1Container )

		if revivedArg1 == nil then

			return

		end

		return fn( revivedArg1, ... )

	end


end

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

WFFunctionUtils.multiReturnReduce = function( f, arr, ... )

	WFArguments_check( 'function', 'table' )

	local n = select( "#", ... )

	local rets = { ... }

	for i = 1, #arr do

		rets = { f( arr[ i ], unpack( rets, 1, n ) ) }

	end

	return unpack( rets, 1, n )

end

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

return WFFunctionUtils
