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

WFFunctionUtilsTest.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.

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

local WFFunctionUtils = require 'WFFunctionUtils'

local WFRxTestCase = require 'WFRxTestCase'

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

local function noOp()
end

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

local function rxTestBogusArgs( rxTest )

	local bogusArgs = { {}, -- nothing
						{ {}, noOp }, -- wrong order
						{ noOp, "not a table" } }

	local funcs = { WFFunctionUtils.map, WFFunctionUtils.filter }

	for _, f in ipairs( funcs ) do

		for _, args in ipairs( bogusArgs ) do

			rxTest:expectError( f, unpack( args, 1, 2 ) )

		end

	end

	rxTest:expectError( WFFunctionUtils.reduce )

	rxTest:expectError( WFFunctionUtils.reduce, nil )

	rxTest:expectError( WFFunctionUtils.reduce, nil, "not func" )

	rxTest:expectError( WFFunctionUtils.reduce, nil, noOp, "not table" )

end

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

local function returnSame( ... ) return ... end

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

local function add1( v ) return v + 1 end

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

local function returnSameIfEven( v ) 

	if v % 2 == 0 then return v end 

end

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

local function rxTestMap( rxTest )

	rxTest:expectEqual( WFFunctionUtils.map( returnSame, {} ), {} )

	rxTest:expectEqual( WFFunctionUtils.map( returnSame, { 1, 2, 3 } ), 
						{ 1, 2, 3 } )

	rxTest:expectEqual( WFFunctionUtils.map( add1, { 1, 2, 3 } ), 
						{ 2, 3, 4 } )

	rxTest:expectEqual( WFFunctionUtils.map( returnSameIfEven, { 1, 2, 3 } ), 
						{ nil, 2, nil } )
end

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

local function returnSameIfBigger( n ) 

	return function( v )

		if v > n then return v end 

	end

end

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

local function returnSameIfBiggerFalseIfNot( n ) 

	return function( v )

		if v > n then return v end 

		return false

	end

end

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

local function rxTestFilter( rxTest )

	rxTest:expectEqual( WFFunctionUtils.filter( returnSame, {} ), 
						{} )

	rxTest:expectEqual( WFFunctionUtils.filter( returnSame, { 1, 2, 3 } ), 
						{ 1, 2, 3 } )

	rxTest:expectEqual( WFFunctionUtils.filter( add1, { 1, 2, 3 } ), 
						{ 2, 3, 4 } )

	rxTest:expectEqual( WFFunctionUtils.filter( returnSameIfBigger( 5 ), 
													{ 1, 2, 3 } ), 
						{} )

	rxTest:expectEqual( WFFunctionUtils.filter( returnSameIfBiggerFalseIfNot( 2 ), 
													{ 1, 2, 3 } ), 
						{ false, false, 3 } )
end

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

local function concat( a, b )

	return a .. b 

end

local function longestStr( s1, s2 )

	return #s1 < #s2 and s2 or s1

end

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

local function rxTestReduce( rxTest )

	rxTest:expectEqual( WFFunctionUtils.reduce( "xyz", concat, {} ),
							"xyz" )

	rxTest:expectEqual( WFFunctionUtils.reduce( "xyz", concat, { 'a', 'b', 'c' } ),
							"xyzabc" )

	rxTest:expectEqual( WFFunctionUtils.reduce( "", longestStr, { 'Two', 'Deuce', 'twice' } ),
							"Deuce" )

end

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

local function multiReduceExampleAccumulator( 
				num, sum, product, odds, evens, scaled, specials, scale, isSpecialFunc )

	sum = sum + num

	product = product * num

	if num % 2 == 1 then

		odds[ #odds + 1 ] = num

	end

	if num % 2 == 0 then

		evens[ #evens + 1 ] = num

	end

	scaled[ #scaled + 1 ] = scale * num
	
	if isSpecialFunc( num ) then

		specials[ #specials + 1 ] = num

	end

	return sum, product, odds, evens, scaled, specials, scale, isSpecialFunc

end

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

local function rxTestMultiReturnReduce( rxTest )

	rxTest:expectError( 
		function() 
			
			WFFunctionUtils.multiReturnReduce( {} )

		end )

	rxTest:expectError( 
		function() 
			
			WFFunctionUtils.multiReturnReduce( function() end  )

		end )

	local arr = { 1, 2, 3, 4, 5, 6  }

	local expectedSum = 21

	local expectedProduct = 720

	local expectedOdds = { 1, 3, 5 }

	local expectedEvens = { 2, 4, 6 }

	local scale = 3

	local expectedScaled = { scale * 1, scale * 2, scale * 3, scale * 4, scale * 5, scale * 6  }

	local function isSpecial( n )

		return n == 2 or n == 5

	end

	local expectedSpecials = { 2, 5, }

	-- note that two parameters, scale and isSpecial, are "read only" and
	-- used to populate other return values.
	local sum, product, odds, evens, scaled, specials =
		WFFunctionUtils.multiReturnReduce(
			multiReduceExampleAccumulator,
				arr, 0, 1, {}, {}, {}, {}, scale, isSpecial )

	rxTest:expectEqual( sum, expectedSum )

	rxTest:expectEqual( product, expectedProduct )

	rxTest:expectEqual( odds, expectedOdds )

	rxTest:expectEqual( evens, expectedEvens )

	rxTest:expectEqual( scaled, expectedScaled )

	rxTest:expectEqual( specials, expectedSpecials )

end

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

local function rxTestWeakBind1st( rxTest )

	--- errors out when nil is bound
	
	rxTest:expectError( WFFunctionUtils.weakBind1st, noOp, nil )

	-- check to make sure it calls if arg is not collected and it returns
	-- what we expect
	local arg1 = {}

	local didCall = "no"

	local logFuncCall = WFFunctionUtils.weakBind1st( 
							function () didCall = "yes" end, 
							arg1 )

	logFuncCall()

	rxTest:expectEqual( didCall, "yes" )

	local prependArg1 = WFFunctionUtils.weakBind1st( returnSame, arg1 )

	rxTest:expectEqual( { prependArg1( 2, 3, 4 ) }, { arg1, 2, 3, 4  } )

	-- force the param to be collected and make sure the function is skipped
	arg1 = nil

	collectgarbage()

	collectgarbage()

	didCall = "no"

	logFuncCall()

	rxTest:expectEqual( didCall, "no" )

	rxTest:expectEqual( select( "#", prependArg1( 2, 3, 4 ) ), 0 )

end

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


return WFRxTestCase.wrapSuite( 'WFFunctionUtilsTest', {

	rxTestBogusArgs = rxTestBogusArgs,

	rxTestMap = rxTestMap,

	rxTestFilter = rxTestFilter,

	rxTestReduce = rxTestReduce, 

	rxTestMultiReturnReduce = rxTestMultiReturnReduce,
	
	rxTestWeakBind1st = rxTestWeakBind1st } )
