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

WFCatAssetsTest.lua

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

ADOBE CONFIDENTIAL
------------------
Copyright 2016 Adobe Systems Incorporated
All Rights Reserved.

NOTICE: All information contained herein is, and remains 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 are protected by all applicable
intellectual property laws, including trade secret and copyright laws.
Dissemination of this information or reproduction of this material is
strictly forbidden unless prior written permission is obtained from
Adobe Systems Incorporated.

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

	-- WF.core.self_test
local KSTableUtils = require "KSTableUtils"
local WFRxTestCase = require 'WFRxTestCase'
local WFCatMockCatalog = require 'WFCatMockCatalog'
local KSIdGenerator = require 'KSIdGenerator'

local Rx = require 'Rx'
local WFCatAssets = require "WFCatAssets"

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

local function localAssetId( i )

	return i

end

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

local function addRecordToDocStore( i, rxTest, catalog )

	rxTest:expectSingleValue(

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

					links = {
						[ '/rels/rendition_type/thumbnail2x' ] = { href = '/href/bogus/testing' }
					}

				}
			},
		},

		':ignore:' )

end

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

local function updateDocstoreRecord( i, rxTest, catalog )

	rxTest:expectSingleValue(

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

		':ignore:' )

end

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

local function rxTestEmptyCatalog( rxTest )

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest )

	local observedAllRenderableLocalAssetIdsStream =
			rxTest:observe( WFCatAssets.allRenderableLocalAssetIdsStream( wfCatalog )
								:map( unpack ) )

	observedAllRenderableLocalAssetIdsStream:expectData( wfCatalog:ozCatalogId(), {}, {} )

end

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

local function recurseRange( first, last, f, ... )

	local function f_i( i, ... )

		i = i and ( i + 1 ) or first

		if i > last then

			return

		end

		f( i, ... )

		return f_i( i, ... )

	end

	return f_i( nil, ... )

end

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

local function addLocalAssetIdToSets( i, ... )

	for j = 1, select( "#", ... ) do

		local set = select( j, ... )

		set[ localAssetId( i ) ] = true

	end

end

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

local function removeLocalAssetIdFromSets( i, ... )

	for j = 1, select( "#", ... ) do

		local set = select( j, ... )

		set[ localAssetId( i ) ] = nil

	end

end

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

local function deleteLocalAssetId( i, wfCatalog, rxTest )

	rxTest:expectSingleValue(
		wfCatalog:docStore():deleteDoc( localAssetId( i ) ),
		':ignore:' )

end

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

local function makeCurrentLocalAssetIdsStream( wfCatalog, bufferInterval )

	local currLocalAssetIdsSet

	return WFCatAssets.allRenderableLocalAssetIdsStream( wfCatalog, bufferInterval ) 
				:map( unpack )
				:map( 
					function( _, adds, deletes )

						if not currLocalAssetIdsSet then

							currLocalAssetIdsSet = KSTableUtils.deepCopy( adds )

						else

							for k in pairs( adds ) do

								currLocalAssetIdsSet[ k ] = true

							end

							for k in pairs( deletes ) do

								currLocalAssetIdsSet[ k ] = nil

							end

						end

					return currLocalAssetIdsSet

				end )
			:replay( nil, 1 ):refCount()

end

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

local function makeWaitForMatchingSetStream( setStream, desiredSet )

	return Rx.Observable.create(
		function( observer )

			return setStream:subscribe(

				function( actualSet )

					if KSTableUtils.deepEquals( actualSet, desiredSet ) then

						observer:onCompleted()

					end

				end,

				function( errStr )

					observer:onError( errStr )

				end )

		end )

end
--------------------------------------------------------------------------------

local function setAsStr( set )

	local keys = {}

	for key in pairs( set ) do

		keys[ #keys + 1 ] = key

	end

	table.sort( keys )

	return string.format( "{ %s }", table.concat( keys, ", " ) )

end

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

local function rxTestNonEmptyCatalog( rxTest )

	rxTest:setTimeLimit( 100 )

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest )

	local bufferInterval = 0.25

	rxTest:log( "make set stream" )

	local currLocalAssetIdsSetStream = 
			makeCurrentLocalAssetIdsStream( wfCatalog, bufferInterval )

	local subscription =
			currLocalAssetIdsSetStream
				:subscribe( 
					function( set ) 

						rxTest:logf( "Got %s", setAsStr( set ) ) 
						
					end )

	local expectedLocalAssetIdSet = {}

	recurseRange( 1, 10, addLocalAssetIdToSets, expectedLocalAssetIdSet )

	recurseRange( 1, 10, addRecordToDocStore, rxTest, wfCatalog )

	rxTest:log( "Test initial results" )

	local waitForInitialResults = 
			rxTest:observe( makeWaitForMatchingSetStream(
								currLocalAssetIdsSetStream,
								expectedLocalAssetIdSet ) )

	waitForInitialResults:expectCompleted()

	rxTest:log( "Test adds, deletes, and updates" )

	recurseRange( 1, 5, deleteLocalAssetId, wfCatalog, rxTest )

	recurseRange( 1, 5, removeLocalAssetIdFromSets, expectedLocalAssetIdSet )

	recurseRange( 6, 10, updateDocstoreRecord, rxTest, wfCatalog )

	recurseRange( 11, 20, addRecordToDocStore, rxTest, wfCatalog )

	recurseRange( 11, 20, addLocalAssetIdToSets, expectedLocalAssetIdSet )

	local waitForExpectedResults = 
			rxTest:observe( makeWaitForMatchingSetStream(
								currLocalAssetIdsSetStream,
								expectedLocalAssetIdSet ) )

	waitForExpectedResults:expectCompleted()

	rxTest:log( "Test overlapping adds and deletes" )

	recurseRange( 21, 30, addRecordToDocStore, rxTest, wfCatalog )

	recurseRange( 21, 30, addLocalAssetIdToSets, expectedLocalAssetIdSet )

	recurseRange( 15, 25, deleteLocalAssetId, wfCatalog, rxTest )

	recurseRange( 15, 25, removeLocalAssetIdFromSets, expectedLocalAssetIdSet )

	waitForExpectedResults = 
			rxTest:observe( makeWaitForMatchingSetStream(
								currLocalAssetIdsSetStream,
								expectedLocalAssetIdSet ) )

	waitForExpectedResults:expectCompleted()

	rxTest:log( "Test near complete delete overlap doesn't add anything" )

	for i = 31, 36 do

		addRecordToDocStore( i, rxTest, wfCatalog )

		if i ~= 36 then

			rxTest:expectSingleValue(
					wfCatalog:docStore():deleteDoc( localAssetId( i ) ),
					':ignore:' )

		end

	end

	recurseRange( 36, 36, addLocalAssetIdToSets, expectedLocalAssetIdSet )

	waitForExpectedResults = 
			rxTest:observe( makeWaitForMatchingSetStream(
								currLocalAssetIdsSetStream,
								expectedLocalAssetIdSet ) )

	waitForExpectedResults:expectCompleted()

	subscription:dispose()

end

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

return WFRxTestCase.wrapSuite( {

	rxTestEmptyCatalog = rxTestEmptyCatalog,

	rxTestNonEmptyCatalog = rxTestNonEmptyCatalog,

} )
