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

PixelProviderGridPixelPathsModel.lua

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

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

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

local Rx = require "Rx"

	-- wf.core
local WFArguments = require 'WFArguments'
local WFRxRunUntilDone = require 'WFRxRunUntilDone'

	-- WF.oz_client.managed_catalog.pixel_provider.grid
local GridRangeUtils = require 'GridRangeUtils'

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

local GridRangeUtils_getBufferedRange = GridRangeUtils.getBufferedRange

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

local GridRangeUtils_computeScrollDirection = 
		GridRangeUtils.computeScrollDirection

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

local GridRangeUtils_getFetchAheadAndBehindRanges = 
		GridRangeUtils.getFetchAheadAndBehindRanges

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

local WFArguments_check = assert( WFArguments.check,
										"missing WFArguments.check" )

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

local Rx_Observable_just = Rx.Observable.just

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

local function makeCallWithProcessorStream( desc, f )

	WFArguments_check( "string", "function" )

	return function( pixelProviderProcessorStream, ... )

		local argsN = select( '#', ... )

		local args = { ... }

		return pixelProviderProcessorStream
			:map(
				function( pixelProviderProcessor )
				
					if not pixelProviderProcessor then
						return Rx.Observable.just( {} )
					end

					f( pixelProviderProcessor, unpack( args, 1, argsN ) )

					return Rx_Observable_just( "called " .. desc )

				end )
			:switch()

	end

end

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

local function abandonAllRenditionRequestsForPixstreamId( 
				pixelProviderProcessor, pixStreamId )

	pixelProviderProcessor
		:runUntilDone( 
			"abandonAllPixStreamRequests", 
				pixStreamId )

end

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

local function updateAssetIdListOnScheduler( pixelProviderProcessor,
													pixStreamId, prevAssetIds,
													newAssetIds )

	pixelProviderProcessor:updateAssetIdList( pixStreamId, prevAssetIds,
												newAssetIds )

end

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

local updateAssetIdListOnSchedulerStream =
		makeCallWithProcessorStream( "updateAssetIdListOnScheduler",
										updateAssetIdListOnScheduler )

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

local function justPixelProviderProcessor( _, pixelProviderProcessor )

	return pixelProviderProcessor

end

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

local function finishStreamWhenNil( v )

	if v == nil then

		return Rx.Observable.empty()

	end

	return Rx.Observable.just( v )

end

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

local function runFunctionOnPixelProcessor( f, pixelProviderProcessorStream,
												... )

	local args, argsN = { ... }, select( "#", ... )

	WFRxRunUntilDone(
		pixelProviderProcessorStream 
			:flatMapLatest( finishStreamWhenNil )
			:map(
				function( pixelProviderProcessor )

					return f( pixelProviderProcessor, unpack( args, 1, argsN ) )
					
				end ) )

end

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

local function abandonAllLoupeRenditionRequests( assetIdsSubmodel )

	runFunctionOnPixelProcessor(
		abandonAllRenditionRequestsForPixstreamId,
			assetIdsSubmodel._pixelProviderProcessorStream,
			assetIdsSubmodel._pixStreamId )

end

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

local function updateLoupeOnScreenRangeOnScheduler( pixelProviderProcessor,
									pixStreamId, assetIds, 
									defaultLoupeRenditionSize,
									prevOnScreenRange, onScreenRange,
									prevBufferedRange, bufferedRange,
									scrollDirection, 
									prevFetchAheadRange, fetchAheadRange,
									prevFetchBehindRange, fetchBehindRange )

	pixelProviderProcessor
		:updateForLoupeScrolling(
			pixStreamId, assetIds, defaultLoupeRenditionSize,
			prevOnScreenRange, onScreenRange,
			prevBufferedRange, bufferedRange,
			scrollDirection, 
			prevFetchAheadRange, fetchAheadRange,
			prevFetchBehindRange, fetchBehindRange)

end

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

local updateLoupeOnScreenRangeOnSchedulerStream =
		makeCallWithProcessorStream( "updateLoupeOnScreenRangeOnScheduler",
										updateLoupeOnScreenRangeOnScheduler )

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

local function updateLoupeRenditionsOnScreenRange( assetIdsSubmodel, 
													bufferMultiplier,
													onScreenRange )

	WFArguments_check( "table", "number", "table" )

	local prevOnScreenRange = assetIdsSubmodel._currOnScreenRange or {}

	assetIdsSubmodel._currOnScreenRange = onScreenRange

	local scrollDirection = GridRangeUtils_computeScrollDirection( 
								prevOnScreenRange, onScreenRange )

	local prevBufferedRange = assetIdsSubmodel._bufferedRange

	assetIdsSubmodel._bufferedRange =
		GridRangeUtils_getBufferedRange( 
							bufferMultiplier, prevOnScreenRange, onScreenRange,
							#assetIdsSubmodel._assetIds )

	local prevFetchAheadRange, prevFetchBehindRange = 
			assetIdsSubmodel._fetchAheadRange or {}, 
			assetIdsSubmodel._fetchBehindRange or {}

	assetIdsSubmodel._fetchAheadRange, 
		assetIdsSubmodel._fetchBehindRange = 
			GridRangeUtils_getFetchAheadAndBehindRanges( 
				onScreenRange, assetIdsSubmodel._bufferedRange )

	WFRxRunUntilDone(
		updateLoupeOnScreenRangeOnSchedulerStream(
				assetIdsSubmodel._pixelProviderProcessorStream,
				assetIdsSubmodel._pixStreamId,
				assetIdsSubmodel._assetIds,
				assetIdsSubmodel._defaultLoupeRenditionSize,
				prevOnScreenRange, onScreenRange,
				prevBufferedRange, assetIdsSubmodel._bufferedRange,
				scrollDirection, 
				prevFetchAheadRange, assetIdsSubmodel._fetchAheadRange,
				prevFetchBehindRange, assetIdsSubmodel._fetchBehindRange ) )

end

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

local function updateLoupeAssetIndicesOnScheduler( pixelProviderProcessor,
									pixStreamId, assetIds, 
									defaultLoupeRenditionSize,
									prevAssetIdIndices, assetIdIndices )

	pixelProviderProcessor
		:updateForLoupeWalk(
			pixStreamId, assetIds, defaultLoupeRenditionSize,
			prevAssetIdIndices, assetIdIndices )

end

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

local updateLoupeAssetIndicesOnSchedulerStream =
		makeCallWithProcessorStream( "updateLoupeAssetIndicesOnScheduler",
										updateLoupeAssetIndicesOnScheduler )

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

local function updateLoupeRenditionsAssetIndices( assetIdsSubmodel, assetIdIndices )

	WFArguments_check( "table", "table" )
	
	local prevAssetIdIndices = assetIdsSubmodel._currAssetIdIndices or {}
	
	assetIdsSubmodel._currAssetIdIndices = assetIdIndices
	
	WFRxRunUntilDone(
		updateLoupeAssetIndicesOnSchedulerStream(
				assetIdsSubmodel._pixelProviderProcessorStream,
				assetIdsSubmodel._pixStreamId,
				assetIdsSubmodel._assetIds,
				assetIdsSubmodel._defaultLoupeRenditionSize,
				prevAssetIdIndices, assetIdIndices ) )

end

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

local function makeLoupeRenditionAssetIdsSubmodel( pixStreamIdModel, assetIds )

	WFArguments_check( "table", "table" )

	local prevAssetIds = pixStreamIdModel._prevAssetIds

	pixStreamIdModel._prevAssetIds = assetIds

	return {

		-- model state
		_pixelProviderProcessorStream = 
			pixStreamIdModel._pixelProviderProcessorStream,

		_pixStreamId = pixStreamIdModel._pixStreamId,

		_assetIds = assetIds,

		_defaultLoupeRenditionSize =
			pixStreamIdModel._defaultLoupeRenditionSize,
			
		priority = 'high',
		
		-- model value
		value = updateAssetIdListOnSchedulerStream( 
					pixStreamIdModel._pixelProviderProcessorStream,
					pixStreamIdModel._pixStreamId, 
					prevAssetIds, assetIds ),

		-- events
		events = {

			abandonAll = abandonAllLoupeRenditionRequests,

			updateOnScreenRange = updateLoupeRenditionsOnScreenRange,
			
			updateAssetIndices = updateLoupeRenditionsAssetIndices,

		},

	}

end

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

local function make( wfCatalogPixelProviderProcessorStream, 
								pixStreamId, defaultLoupeRenditionSize )

	local pixelProviderProcessorStream = 
			wfCatalogPixelProviderProcessorStream
				:map( justPixelProviderProcessor )

	return {

		_pixelProviderProcessorStream = pixelProviderProcessorStream,

		_pixStreamId = pixStreamId,

		_defaultLoupeRenditionSize = defaultLoupeRenditionSize,

		_prevAssetIds = {},
		
		value = Rx.Observable.just( pixStreamId ),

		-- submodels
		models = {

			assetIds = makeLoupeRenditionAssetIdsSubmodel,

		},

	}

end

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

return {

	make = make,

}
