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

WFRxThrottledBatchedBulkApplyOverlay.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 KSDate = require 'KSDate'
local KSTableUtils = require 'KSTableUtils'
local WFArguments = require 'WFArguments'
local WFFunctionUtils = require 'WFFunctionUtils'

	-- WF.core.rxlua
local WFRxScheduler = require 'WFRxScheduler'

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

local check = assert( WFArguments.check )

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

local WFFunctionUtils_reduce = WFFunctionUtils.reduce

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

local function addToChunkedArray( chunks, val, chunkSize )

	local currChunk = chunks[ #chunks ]

	local currChunkSize = #currChunk
	
	if currChunkSize > 0 and currChunkSize  % chunkSize == 0 then

		currChunk = {}

		chunks[ #chunks + 1 ] = currChunk

	end
		
	currChunk[ #currChunk + 1 ] = val

	return chunks

end

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

local function chunkedArray( arr, chunkSize )

	return WFFunctionUtils_reduce( { {} }, addToChunkedArray, arr, chunkSize )

end

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

local _lastBulkApplyOverlayDeliveryTimes = KSTableUtils.makeWithMode( "kv" )

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

--[[!

	\brief A function used to batch/throttle calls to the BulkApplyOverlay doc 
		store API.
	
	\param desc (string) description of operation to use for debugging and diagnostic logs
	\param docStore (docstore) A valid WFRxDocStore.
	\param overlays (table) A table of Oz document overlays.
	\param batchSize (number) The batch size. AKA the number of overlays
		sent on each call to bulkApplyOverlay.
	\param callsPerSecondLimit (number) The max number of API
		calls that we'll make to the doc store per second.
	
	\return <code>Observable</code> An observable that completes when all of the
		overlays have been applied.
		
--]]

return function( desc, docStore, overlays, batchSize, callsPerSecondLimit )

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

	local count = #overlays

	local label = overlays.label
	local preserveLabels = overlays.preserveLabels

	if count < 1 then
		return Observable.just( true )
	end

	if count <= batchSize then
		return docStore:bulkApplyOverlay( overlays, desc )
	end
	
	if batchSize and batchSize < 1 then
		batchSize = 1
	end
	
	local minWaitUpperBound = 1 / callsPerSecondLimit

	local scheduler = WFRxScheduler.sharedScheduler()

	local chunkedOverlays = chunkedArray( overlays, batchSize )

	return Rx.Observable.from( chunkedOverlays )

		:map( function( batchOfOverlays )

			return Rx.Observable.defer( function()

				local lastDeliveredTime =
						_lastBulkApplyOverlayDeliveryTimes[ docStore ]
					
				local delayInSecs =
						not lastDeliveredTime and
							0 or
							math.max(
								minWaitUpperBound -
									( KSDate.timeIntervalSinceAppLaunch() -
										lastDeliveredTime ),
								0 )

				if label then
					batchOfOverlays.label = label
				end

				batchOfOverlays.preserveLabels = preserveLabels

				return docStore 
					:bulkApplyOverlay( batchOfOverlays, desc )
					:delay( delayInSecs, scheduler )
					:tap( function()

						_lastBulkApplyOverlayDeliveryTimes[ docStore ] =
							KSDate.timeIntervalSinceAppLaunch()

					end )

			end )

		end )
		
		:flatMerge( 1 )

		:concat( Observable.just( true ) )
		
		:takeLast( 1 )

end
