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

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

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

	-- If ACR is not supported, disable test before we attempt to
	-- require WFAcr. (That will fail.)
if not require 'WFPlatformInfo'.hasACR then return {} end

	-- wf.core
local KSIdGenerator = require 'KSIdGenerator'
local KSTableUtils = require 'KSTableUtils'
local WFCatMockCatalog = require 'WFCatMockCatalog'
local WFRxTestCase = require 'WFRxTestCase'

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

local PixelPathProviderScheduler = require "PixelPathProviderScheduler"
local PixelPathProviderRequestTypes = require "PixelPathProviderRequestTypes"
local PixelPathProviderStateMachine = require "PixelPathProviderStateMachine"

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

local PixelPathProviderRequestTypeNames = PixelPathProviderRequestTypes.names

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

local function makeSchedulerForTest( rxTest )

	local catalog = WFCatMockCatalog.createFakeWFCatalog( rxTest )

	local scheduler = PixelPathProviderScheduler( catalog )

	rxTest:finally( function() scheduler:close() end )

	return scheduler, catalog

end

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

local function localAssetId( i )

	return i

end

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

local function pixStreamId( i )

	return string.format( "pixStreamId%d", i )

end

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

local function extractLocalAssetIdFromRequest( request )

	return request[ 2 ]

end

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

local function addRecordToDocStore( rxTest, catalog, i )

	rxTest:expectSingleValue(

		catalog:docStore():bulkSetDocValue {
			{
				_id = KSIdGenerator.uuidInLowercaseHex(),
				_localId = localAssetId( i ),
				subtype = "image",
				_localOnly = {
					files = {
						original = {
							master = {
								relativePath = string.format( 'local path %s', i ),
								volumeGuid = KSIdGenerator.uuidInLowercaseHex()
							}
						}
					}
				},
				develop = {
					croppedHeight = 100 * i,
					croppedWidth = 100 * i,
					xmpCameraRaw = '<xmp />',
					userOrientation = i % 4,
				},
				rating = i % 5,
			},
		},

		':ignore:' )

end

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

local function docValueHasId( wantedLocalAssetId, docValue )

	if docValue._localId == wantedLocalAssetId then
		
		return wantedLocalAssetId, wantedLocalAssetId

	end

	return wantedLocalAssetId

end

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

local function updateDocstoreRecord( rxTest, catalog, i )

	local watchUpdates =
		rxTest:observe(
			catalog:docStore():scannedFilteredMappedChangesFeed(
					"PixelProviderDocstoreInfoTaskTest",
					"docValueHasId",
					localAssetId( i ) )
				:take( 1 ) )

	rxTest:expectSingleValue(

		catalog:docStore():bulkApplyOverlay {
			{
				_id = localAssetId( i ),
				develop = {
					croppedHeight = 10 * i,
					croppedWidth = 10 * i,
				},
			},
		},

		':ignore:' )

	watchUpdates:expectData( ':ignore:' )
	watchUpdates:expectCompleted()

end

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

local function isTestMessage( msg, renderInfoChangeType )

	return function( testMsg, testRenderInfoType, ... )

		return testMsg == msg and testRenderInfoType == renderInfoChangeType 

	end

end

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

local function takeNth( n )
	
	return function( ... )

		return select( n, ... )

	end

end

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

local function sortArray( arr, maybeSortFn )

	local sorted = KSTableUtils.deepCopy( arr )

	table.sort( sorted, maybeSortFn )

	return sorted

end

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

local function justReturn( ... )

	return ...

end

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

local function getNFromTestInfoStream( testStream, n, filterFunc, 
										maybeTransformFunc )

	local transformTestData = maybeTransformFunc or justReturn

	return testStream
			:where( filterFunc )
			:map( transformTestData )
			:take( n )
			:toArray()

end

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

local function rxTestHappyPath( rxTest )
	
	rxTest:setTimeLimit( 5 )
	local scheduler, catalog = makeSchedulerForTest( rxTest )

	local SchedulerTestAPI = scheduler:_expose_test_API()

	local newRecordCount = 20 

	local expectCheckPixelCacheLocalAssetIds = {}

	for i = 1, newRecordCount do

		for j = 1, 3 do

			expectCheckPixelCacheLocalAssetIds[ #expectCheckPixelCacheLocalAssetIds + 1 ] = localAssetId( i )

			scheduler:addRequest( pixStreamId( j ), localAssetId( i ), 
									PixelPathProviderRequestTypeNames.grid_visible, 1 )

		end
	
	end

	local watchUpdates = rxTest:observe( 
									getNFromTestInfoStream(
											scheduler._testData.testInfoStream,
											newRecordCount,
											isTestMessage( "gotDocstoreInfo", "batch" ),
											takeNth( 3 ) )
										:map( sortArray ) )

	local expectedNewRecords = {}

	for i = 1, newRecordCount + 1 do

		addRecordToDocStore( rxTest, catalog, i )

		if i <= newRecordCount then

			expectedNewRecords[ #expectedNewRecords + 1 ] = localAssetId( i )

		end

	end

	rxTest:log( "before watchUpdates" )

	SchedulerTestAPI.addRunLoopTaskToAllowlist( scheduler, 
												"PixelProviderDocstoreInfoTask" )

	SchedulerTestAPI.runScheduledRequests( scheduler )
	
	-- make sure we got doc store data for all the records
	watchUpdates:expectData( sortArray( expectedNewRecords ) )
	
	rxTest:log( "after watchUpdates" )
	
	-- all requests are now either in NeedCheckRenderSettings (1 pass through scheduled requests) or
	-- NeedCheckForPixelPathInCache (2 passes) sleeping for a moment should allow all to get 
	-- processed through NeedCheckForPixelPathInCache
	rxTest:sleep( 2 )

	rxTest:expectEqual( 
		sortArray( expectCheckPixelCacheLocalAssetIds ),
		sortArray( 
			scheduler:_mapAllRequestsWithState( 
						PixelPathProviderStateMachine.PixelCacheCheckStates.NeedCheckForPixelPathInCache,
						extractLocalAssetIdFromRequest ) ) )

end

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

local function rxTestUpdatesSeen( rxTest )
	
	local scheduler, catalog = makeSchedulerForTest( rxTest )

	local SchedulerTestAPI = scheduler:_expose_test_API()

	local newRecordCount = 20

	local pixStreamIdsCount = 3

	local expectedNewRecords = {}

	for i = 1, newRecordCount do

		for j = 1, pixStreamIdsCount do

			scheduler:addRequest( pixStreamId( j ), localAssetId( i ), 
									PixelPathProviderRequestTypeNames.grid_visible, 1 )

		end
	
	end

	local watchAdds = rxTest:observe( 
									getNFromTestInfoStream(
											scheduler._testData.testInfoStream,
											newRecordCount,
											isTestMessage( "gotDocstoreInfo", "batch" ),
											takeNth( 3 ) )
										:map( sortArray ) )

	for i = 1, newRecordCount + 1 do

		addRecordToDocStore( rxTest, catalog, i )

		if i <= newRecordCount then

			expectedNewRecords[ #expectedNewRecords + 1 ] = localAssetId( i )

		end

	end

	SchedulerTestAPI.addRunLoopTaskToAllowlist( scheduler, 
												"PixelProviderDocstoreInfoTask" )

	SchedulerTestAPI.runScheduledRequests( scheduler )

	rxTest:log( "before wait expectedNewRecords" )

	-- make sure we got doc store data for all the records
	watchAdds:expectData( sortArray( expectedNewRecords ) )

	rxTest:log( "after wait expectedNewRecords" )

	local origIsLostAssetIds = {}

	for i = 1, newRecordCount do

		for j = 1, pixStreamIdsCount do
	
			origIsLostAssetIds[ #origIsLostAssetIds + 1 ] = localAssetId( i )

			SchedulerTestAPI.setState( scheduler, pixStreamId( j ), localAssetId( i ), 
						PixelPathProviderStateMachine.EndStates.NoDownloadableRenderOrBinary )


		end
	
	end

	-- all requests are now on NoDownloadableRenderOrBinary
	rxTest:expectEqual( 
		sortArray( origIsLostAssetIds ),
		sortArray( 
			scheduler:_mapAllRequestsWithState( 
						PixelPathProviderStateMachine.EndStates.NoDownloadableRenderOrBinary,
						extractLocalAssetIdFromRequest ) ) )

	SchedulerTestAPI.runScheduledRequests( scheduler )

	-- update some records, but not all

	local updateCount = newRecordCount - 3

	local watchUpdatedRecords = rxTest:observe( 
									getNFromTestInfoStream(
											scheduler._testData.testInfoStream,
											updateCount,
											isTestMessage( "gotDocstoreInfo", "changedRenderInfos" ),
											takeNth( 3 ) )
										:map( sortArray ) )

	local expectedUpdateRecords = {}

	local expectedFetchingAssetInfoStates = {}

	local expectedUnchangedStates = {}

	for i = 1, newRecordCount do

		if i <= updateCount then

			expectedUpdateRecords[ #expectedUpdateRecords + 1 ] = localAssetId( i ) 
		
			updateDocstoreRecord( rxTest, catalog, i )

		end

		for _ = 1, pixStreamIdsCount do

			if i <= updateCount then

				table.insert( expectedFetchingAssetInfoStates, localAssetId( i ) )

			else

				table.insert( expectedUnchangedStates, localAssetId( i ) )

			end

		end

	end

	rxTest:log( "before watchUpdatedRecords" )

	SchedulerTestAPI.runScheduledRequests( scheduler )

	-- make sure we got doc store data for all updated records
	watchUpdatedRecords:expectData( sortArray( expectedUpdateRecords ) )

	rxTest:log( "after watchUpdatedRecords" )

	-- all requests for updated records are now on NeedCheckForPixelPathInCache
	rxTest:expectEqual( 
		sortArray( expectedFetchingAssetInfoStates ),
		sortArray( 
			scheduler:_mapAllRequestsWithState( 
						PixelPathProviderStateMachine.PixelCacheCheckStates.NeedCheckForPixelPathInCache,
						extractLocalAssetIdFromRequest ) ) )

	-- all other requests are still on NoDownloadableRenderOrBinary
	rxTest:expectEqual( 
		sortArray( expectedUnchangedStates ),
		sortArray( 
			scheduler:_mapAllRequestsWithState( 
						PixelPathProviderStateMachine.EndStates.NoDownloadableRenderOrBinary,
						extractLocalAssetIdFromRequest ) ) )

end

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

return WFRxTestCase.wrapSuite {

	rxTestHappyPath = rxTestHappyPath,

	rxTestUpdatesSeen = rxTestUpdatesSeen,

	-- used to make sure docStore

	docValueHasId = docValueHasId,
}

