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

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'

	-- 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 updateGridOnScreenRangeOnScheduler( pixelProviderProcessor,
									pixStreamId, assetIds, cellSizeData,
									prevOnScreenRange, onScreenRange,
									prevBufferedRange, bufferedRange,
									scrollDirection, 
									prevFetchAheadRange, fetchAheadRange,
									prevFetchBehindRange, fetchBehindRange )

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

end

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

local updateGridOnScreenRangeOnSchedulerStream =
		makeCallWithProcessorStream( "updateGridOnScreenRangeOnScheduler",
										updateGridOnScreenRangeOnScheduler )

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

local function makeOnScreenRangeSubmodel( cellSizeSubmodel, bufferMultiplier,
											onScreenRange )

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

	local prevOnScreenRange = cellSizeSubmodel._currOnScreenRange or {}

	cellSizeSubmodel._currOnScreenRange = onScreenRange

	local scrollDirection = GridRangeUtils_computeScrollDirection( 
								prevOnScreenRange, onScreenRange )

	local prevBufferedRange = cellSizeSubmodel._bufferedRange

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

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

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

	return {

		value = updateGridOnScreenRangeOnSchedulerStream(
					cellSizeSubmodel._pixelProviderProcessorStream,
					cellSizeSubmodel._pixStreamId,
					cellSizeSubmodel._assetIds,
					cellSizeSubmodel._cellSizeData,
					prevOnScreenRange, onScreenRange,
					prevBufferedRange, cellSizeSubmodel._bufferedRange,
					scrollDirection, 
					prevFetchAheadRange, cellSizeSubmodel._fetchAheadRange,
					prevFetchBehindRange, cellSizeSubmodel._fetchBehindRange ),

	}

end

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

local function updateCellSizesOnScheduler( pixelProviderProcessor,
											isHidden, pixStreamId, assetIds,
											cellSizeData )

	if not isHidden then

		pixelProviderProcessor:updateCellSizes( pixStreamId, assetIds, 
												cellSizeData )

	end

end

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

local updateCellSizesOnSchedulerStream = 
		makeCallWithProcessorStream( "updateCellSizesOnScheduler", 
											updateCellSizesOnScheduler )

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

local function makeCellSizeSubmodel( isHiddenSubmodel, cellSize )

	WFArguments_check( "table", "table" )

	return {

		-- model state

		_pixelProviderProcessorStream = 
			isHiddenSubmodel._pixelProviderProcessorStream,

		_pixStreamId = isHiddenSubmodel._pixStreamId,

		_assetIds = isHiddenSubmodel._assetIds,

		_cellSizeData = cellSize,
		
		_currOnScreenRange = {},
		
		_bufferedRange = {},
		
		_fetchAheadRange = {},

		_fetchBehindRange = {},

		-- model value
		value = updateCellSizesOnSchedulerStream(
					isHiddenSubmodel._pixelProviderProcessorStream,
					isHiddenSubmodel._isHidden,
					isHiddenSubmodel._pixStreamId,
					isHiddenSubmodel._assetIds,
					cellSize ),

		-- submodels
		models = {

			onScreenRange = makeOnScreenRangeSubmodel,

		},

	}

end

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

local function updateIsHiddenOnScheduler( pixelProviderProcessor, pixStreamId,
											isHidden )

	if isHidden then

		pixelProviderProcessor
			:runUntilDone( 
				"abandonAllPixStreamRequests", 
					pixStreamId )

	end

end

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

local updateIsHiddenOnSchedulerStream =
		makeCallWithProcessorStream( "updateIsHiddenOnScheduler", 
										updateIsHiddenOnScheduler )

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

local function makeIsHiddenSubmodel( assetIdsSubmodel, isHidden )

	WFArguments_check( "table", "boolean" )

	return {

		-- model state
		_pixelProviderProcessorStream = 
			assetIdsSubmodel._pixelProviderProcessorStream,

		_pixStreamId = assetIdsSubmodel._pixStreamId,

		_assetIds = assetIdsSubmodel._assetIds,

		_isHidden = isHidden,

		-- model value
		value = updateIsHiddenOnSchedulerStream( 
					assetIdsSubmodel._pixelProviderProcessorStream,
					assetIdsSubmodel._pixStreamId, isHidden ),

		-- submodels
		models = {

			cellSize = makeCellSizeSubmodel,

		},

	}

end

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

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

	pixelProviderProcessor:updateAssetIdList( pixStreamId, prevAssetIds,
												newAssetIds )

end

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

local updateAssetIdListOnSchedulerStream =
		makeCallWithProcessorStream( "updateAssetIdListOnScheduler",
										updateAssetIdListOnScheduler )

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

local function makeAssetIdsSubmodel( pixStreamIdModel, assetIds )

	WFArguments_check( "table", "table" )

	local prevAssetIds = pixStreamIdModel._prevAssetIds

	pixStreamIdModel._prevAssetIds = assetIds

	return {

		-- model state
		_pixelProviderProcessorStream = 
			pixStreamIdModel._pixelProviderProcessorStream,

		_pixStreamId = pixStreamIdModel._pixStreamId,

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

		-- submodels
		models = {

			isHidden = makeIsHiddenSubmodel,

		},

	}

end

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

local function justPixelProviderProcessor( _, pixelProviderProcessor )

	return pixelProviderProcessor

end

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

local function make( wfCatalogPixelProviderProcessorStream, pixStreamId )

	local pixelProviderProcessorStream = 
			wfCatalogPixelProviderProcessorStream
				:map( justPixelProviderProcessor )

	return {

		_pixelProviderProcessorStream = pixelProviderProcessorStream,

		_pixStreamId = pixStreamId,

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

		-- submodels
		models = {

			assetIds = makeAssetIdsSubmodel,

		},

	}
	
end

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

return {

	make = make,

}
