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

WFRxBatchedBulkApplyOverlay.lua

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

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

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

	-- RxLua
local Rx = require 'Rx'
local Observable = Rx.Observable

	-- WF.core.utils
local WFArguments = require 'WFArguments'

	-- WF.core.logger
local WFLogger = require 'WFLogger'

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

local check = assert( WFArguments.check )

local logger = WFLogger( 'WFRxBatchedBulkApplyOverlay' )

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

local function getBatch( t, first, last )
	
	local sub = {}
	
	for i = first, last do
		sub[ #sub + 1 ] = t[ i ]
	end
	
	return sub
	
end

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

--[[!

	\brief A function used to batch calls to the BulkApplyOverlay doc 
		store API.
	
	\param docStore (docstore) A valid WFRxDocStore.
	\param overlays (table) A table of Oz document overlays.
	\param batchSize (optional, number) The batch size. AKA the number of overlays
		sent on each call to bulkApplyOverlay.

	\return <code>Observable</code> An observable that completes when all of the
		overlays have been applied.
		
--]]

return function( desc, docStore, overlays, batchSize )

	check( 'string', 'WFRxDocStore', 'table', '?number' )

	local count = #overlays

	local label = overlays.label
		
	if count < 1 then
		return Observable.just( true )
	end
	
	if batchSize and batchSize < 1 then
		batchSize = 1
	end
	
	batchSize = batchSize or 100
	
	local totalBatches = math.ceil( count / batchSize )

	if count > 200 then

		-- 2018.09.04 bkruse
		-- 
		-- WARNING: despite the batched nature of this API, it will effectively
		-- schedule all the updates at once and may starve the docStore.
		-- 
		-- If you don't need atomic updates (i.e. don't need all the overlays
		-- applied immediately), consider using
		-- WFRxThrottledBatchedBulkApplyOverlay, which waits for each batch to
		-- complete before starting the next.
		-- 
		-- 
		logger:warnf( "%s is applying a large number (%s) of docstore updates in %s batches of %s.",
						count, totalBatches, batchSize, desc )

	end

	local completedBatches = 0
	
	local applyOverlaySubscriptions = {}
	
	local batchesCompleteStream = Rx.Subject()
	
	local function cleanup( i )
	
		local subscription = applyOverlaySubscriptions[ i ]
		applyOverlaySubscriptions[ i ] = nil
		
		if subscription then
			subscription:dispose()
		end
		
		completedBatches = completedBatches + 1
		
		logger:infof( "Finished batch %d of %d total batches.", completedBatches, totalBatches )
		
		if completedBatches == totalBatches then
			
			batchesCompleteStream:onNext( true )
			batchesCompleteStream:onCompleted()
			
		end
	
	end
	
	for i = 1, count, batchSize do 
		
		logger:infof( "Now processing overlays from range %d through %d.", i, i + batchSize - 1 )
		
		local batchOfOverlays = getBatch( overlays, i, i + batchSize - 1 )
		
		if label then
			batchOfOverlays.label = label
		end
		
		applyOverlaySubscriptions[ i ] = 
			docStore:bulkApplyOverlay( batchOfOverlays, desc )
			
				:tapOnError( function( maybeErrorMessage )
						
					if maybeErrorMessage then
						logger:errorf( "WFRxBatchedBulkApplyOverlay Failed: %s", maybeErrorMessage )
					end
		
				end )
			
				:finally( function()
					cleanup( i )
				end )
				
				:subscribe()
				
	end 

	return batchesCompleteStream

end
