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

PixelProviderDownloadToFileUtils.lua

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

ADOBE CONFIDENTIAL
------------------
Copyright 2024 Adobe
All Rights Reserved.

NOTICE: All information contained herein is, and remains
the property of Adobe and its suppliers, if any. The intellectual
and technical concepts contained herein are proprietary to Adobe
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.
------------------------------------------------------------------------------]]
	
	-- WF.core
local WFFunctionUtils = require 'WFFunctionUtils'
local KSTableUtils = require 'KSTableUtils'

	-- pixel_provider
local PixelProviderSharedBlobCache = require 'PixelProviderSharedBlobCache'
local PixelPathProviderSchedulerPrivateMethods = 
		require 'PixelPathProviderSchedulerPrivateMethods'

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

local function reduceT( init, accumf, t, ... )

	local ret = init

	for k, v in pairs( t ) do

		ret = accumf( ret, k, v, ... )

	end

	return ret
	
end

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

local function makeBooleanSet( array )

	return WFFunctionUtils.reduce( 
			{},
			function( set, v ) 

				set[ v ] = true

				return set

			end, 
			array )

end

--==============================================================================
-- @mark needs download to file source states
--==============================================================================

local _needsDownloadToFileTransitionsForBinaryType = { 

	proxy = 'need_download_proxy_to_file',

	original = 'need_download_original_to_file'

}

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

local _needsDownloadSourceStatesForBinaryType = {}

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

local _getNeedsDownloadToFileSourceStateArrayFuncsForBinaryType = 
	reduceT( {}, 
		function( getNeedsDownloadToFileSourceStateArrayFuncs, binaryType, startDownloadTransition )

			getNeedsDownloadToFileSourceStateArrayFuncs[ binaryType ] = 
				function( scheduler )

					if not _needsDownloadSourceStatesForBinaryType[ binaryType ] then

						_needsDownloadSourceStatesForBinaryType[ binaryType ] =
							PixelPathProviderSchedulerPrivateMethods.getSourceStatesForTransition(
								scheduler, startDownloadTransition )

					end

					return _needsDownloadSourceStatesForBinaryType[ binaryType ]

				end

			return getNeedsDownloadToFileSourceStateArrayFuncs

		end, 
		_needsDownloadToFileTransitionsForBinaryType )

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

local getNeedsProxyDownloadToFileSourceStateArray =
	assert( _getNeedsDownloadToFileSourceStateArrayFuncsForBinaryType[ 'proxy' ],
				"Missing getNeedsProxyDownloadToFileStateArray" )

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

local getNeedsOriginalDownloadToFileSourceStateArray =
	assert( _getNeedsDownloadToFileSourceStateArrayFuncsForBinaryType[ 'original' ],
				"Missing getNeedsOriginalDownloadToFileStateArray" )

--==============================================================================
-- @mark needs download to file states
--==============================================================================

local _startDownloadToFileTransitionsForBinaryType = { 

	proxy = 'start_proxy_download_to_file',

	original = 'start_original_download_to_file' 

}

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

local _needsDownloadStatesForBinaryType = {}

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

local _getNeedsDownloadToFileStateArrayFuncsForBinaryType = 
	reduceT( {}, 
		function( getNeedsDownloadToFileStateArrayFuncs, binaryType, startDownloadTransition )

			getNeedsDownloadToFileStateArrayFuncs[ binaryType ] = 
				function( scheduler )

					if not _needsDownloadStatesForBinaryType[ binaryType ] then

						_needsDownloadStatesForBinaryType[ binaryType ] =
							PixelPathProviderSchedulerPrivateMethods.getSourceStatesForTransition(
								scheduler, startDownloadTransition )

					end

					return _needsDownloadStatesForBinaryType[ binaryType ]

				end

			return getNeedsDownloadToFileStateArrayFuncs

		end, 
		_startDownloadToFileTransitionsForBinaryType )

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

local getNeedsProxyDownloadToFileStateArray =
	assert( _getNeedsDownloadToFileStateArrayFuncsForBinaryType[ 'proxy' ],
				"Missing getNeedsProxyDownloadToFileStateArray" )

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

local getNeedsOriginalDownloadToFileStateArray =
	assert( _getNeedsDownloadToFileStateArrayFuncsForBinaryType[ 'original' ],
				"Missing getNeedsOriginalDownloadToFileStateArray" )

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

local _needsDownloadToFileStates

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

local function getNeedsDownloadToFileStates( scheduler )

	if not _needsDownloadToFileStates then
	
		for _, getNeedsDownloadToFileStateArrayFunc in pairs( _getNeedsDownloadToFileStateArrayFuncsForBinaryType ) do

			_needsDownloadToFileStates =
				PixelProviderSharedBlobCache.appendT(
					_needsDownloadToFileStates or {},
					getNeedsDownloadToFileStateArrayFunc( scheduler ) )

		end

	end

	return _needsDownloadToFileStates

end

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

local _needsDownloadToFileStateMap 

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

local function isNeedsDownloadToFileState( scheduler, state )

	if not _needsDownloadToFileStateMap then

		_needsDownloadToFileStateMap = 
			makeBooleanSet( getNeedsDownloadToFileStates( scheduler ) )

	end

	return _needsDownloadToFileStateMap[ state ] or false

end

--==============================================================================
-- @mark is downloading to file states
--==============================================================================

local _isDownloadingStatesForBinaryType = {}

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

local _getIsDownloadingToFileStateArrayFuncsForBinaryType = 
	reduceT( {}, 
		function( getIsDownloadingToFileStateArrayFuncs, binaryType, startDownloadTransition )

			getIsDownloadingToFileStateArrayFuncs[ binaryType ] = 
				function( scheduler )

					if not _isDownloadingStatesForBinaryType[ binaryType ] then

						_isDownloadingStatesForBinaryType[ binaryType ] =
							PixelPathProviderSchedulerPrivateMethods.getDestStatesForTransition(
								scheduler, startDownloadTransition )

					end

					return _isDownloadingStatesForBinaryType[ binaryType ]

				end

			return getIsDownloadingToFileStateArrayFuncs

		end, 
		_startDownloadToFileTransitionsForBinaryType )

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

local getIsDownloadingProxyToFileStates =
	assert( _getIsDownloadingToFileStateArrayFuncsForBinaryType[ 'proxy' ],
				"Missing getNeedsProxyDownloadToFileStateArray" )

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

local getIsDownloadingOriginalToFileStates =
	assert( _getIsDownloadingToFileStateArrayFuncsForBinaryType[ 'original' ],
				"Missing getNeedsOriginalDownloadToFileStateArray" )

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

local _isDownloadingToFileStates

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

local function getIsDownloadingToFileStates( scheduler )

	if not _isDownloadingToFileStates then
	
		for _, getIsDownloadingToFileStateArrayFunc in pairs( _getIsDownloadingToFileStateArrayFuncsForBinaryType ) do

			_isDownloadingToFileStates =
				PixelProviderSharedBlobCache.appendT(
					_isDownloadingToFileStates or {},
					getIsDownloadingToFileStateArrayFunc( scheduler ) )

		end

	end

	return _isDownloadingToFileStates

end

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

local function applyNeedsDownloadTransitionToPendingNeedDownloadLocalIdRequests( 
				scheduler, ozBinaryType, localAssetId )

	local needsDownloadTransition = 
		_needsDownloadToFileTransitionsForBinaryType[ ozBinaryType ]

	if not needsDownloadTransition then

		return

	end

	local needsDownloadStatesForBinaryType = 
			( ozBinaryType == 'proxy' and
				getNeedsProxyDownloadToFileSourceStateArray( scheduler ) or
				nil ) or
			( ozBinaryType == 'original' and
				getNeedsOriginalDownloadToFileSourceStateArray( scheduler ) or
				nil )

	local pendingNeedsDownloadToFileRequests =
			scheduler
				:_getLocalAssetIdRequestsWithState(
					localAssetId, unpack( needsDownloadStatesForBinaryType ) )

	-- transition those requests to the need download proxy to file state
	scheduler
		:_applyTransitionAllRequests( 
			pendingNeedsDownloadToFileRequests, needsDownloadTransition )

end

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

local function maybeGetNeedsDownloadToFileRequests( scheduler, ozBinaryType,
														localAssetId )

	if ozBinaryType == 'proxy' then

		return scheduler
				:_getLocalAssetIdRequestsWithState(
					localAssetId, 
					unpack( getNeedsProxyDownloadToFileStateArray(
								scheduler ) ) )
		
	end

	if ozBinaryType == 'original' then

		return scheduler
				:_getLocalAssetIdRequestsWithState(
					localAssetId, 
					unpack( getNeedsOriginalDownloadToFileStateArray(
								scheduler ) ) )

	end

end

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

local function getStartDownloadingTransition( binaryType )

	return _startDownloadToFileTransitionsForBinaryType[ binaryType ]

end

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

local function applyStartTransitionToNeedsDownloadRequests( 
				scheduler, ozBinaryType,
				needsDownloadToFileForBinaryTypeRequests )

	local startDownloadTransitionForBinaryType = 
			getStartDownloadingTransition( ozBinaryType )

	if not startDownloadTransitionForBinaryType then

		return

	end

	scheduler
		:_applyTransitionAllRequests( 
			needsDownloadToFileForBinaryTypeRequests, 
			startDownloadTransitionForBinaryType )

end

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

local function applyStartTransitionToNeedsDownloadLocalIdRequests( scheduler, ozBinaryType, localAssetId )

	local needsDownloadStatesForBinaryType =
			( ozBinaryType == 'proxy' and
				getNeedsProxyDownloadToFileStateArray( scheduler ) or
				nil ) or
			( ozBinaryType == 'original' and
				getNeedsOriginalDownloadToFileStateArray( scheduler ) or
				nil )

	if not needsDownloadStatesForBinaryType then

		return

	end

	local needsDownloadToFileForBinaryTypeRequests =
			scheduler
				:_getLocalAssetIdRequestsWithState(
					localAssetId, unpack( needsDownloadStatesForBinaryType ) )

	applyStartTransitionToNeedsDownloadRequests( 
		scheduler, ozBinaryType, needsDownloadToFileForBinaryTypeRequests )

end

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

local function getIsDownloadingToFileRequests( scheduler, localAssetId, 
												ozBinaryType )
						
	if ozBinaryType == 'proxy' then
		
		return scheduler
				:_getLocalAssetIdRequestsWithState(
					localAssetId, 
					unpack( getIsDownloadingProxyToFileStates( scheduler ) ) )

	end

	if ozBinaryType == 'original' then

		return scheduler
				:_getLocalAssetIdRequestsWithState(
					localAssetId, 
					unpack( getIsDownloadingOriginalToFileStates( 
								scheduler ) ) )

	end

end

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

local function addStateAndBinaryTypeToMap( map, state, binaryType )

	map[ state ] = binaryType

	return map

end

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

local function mergeSourceAndDestStateMapForBinaryTypeTransition( 
				stateToBinaryTypeMap, scheduler, binaryType,
				transitionForBinaryType )

	stateToBinaryTypeMap = stateToBinaryTypeMap or {}

	local sourceStatesForBinaryType = 
			PixelPathProviderSchedulerPrivateMethods.getSourceStatesForTransition(
				scheduler, transitionForBinaryType )

	stateToBinaryTypeMap = 
		WFFunctionUtils.reduce( 
			stateToBinaryTypeMap,
			addStateAndBinaryTypeToMap,
				sourceStatesForBinaryType, binaryType )

	local destStatesForBinaryType = 
			PixelPathProviderSchedulerPrivateMethods.getDestStatesForTransition(
				scheduler, transitionForBinaryType )

	return WFFunctionUtils.reduce( 
				stateToBinaryTypeMap,
				addStateAndBinaryTypeToMap,
					destStatesForBinaryType, binaryType )


end

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

local _binaryTypeToDownloadToFileStateMap

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

local function getBinaryTypeToDownloadToFileStateMap( scheduler )

	if not _binaryTypeToDownloadToFileStateMap then

		_binaryTypeToDownloadToFileStateMap = 
			reduceT( {}, 
				function( binaryTypeToDownloadToFileStateMap, binaryType, needsDownloadTransitionForBinaryType )

					binaryTypeToDownloadToFileStateMap[ binaryType ] = 
						mergeSourceAndDestStateMapForBinaryTypeTransition( 
							binaryTypeToDownloadToFileStateMap[ binaryType ],
							scheduler, binaryType,
							needsDownloadTransitionForBinaryType )

					return binaryTypeToDownloadToFileStateMap

				end,
				_needsDownloadToFileTransitionsForBinaryType )

		_binaryTypeToDownloadToFileStateMap = 
			reduceT( _binaryTypeToDownloadToFileStateMap, 
				function( binaryTypeToDownloadToFileStateMap, binaryType, startDownloadTransitionForBinaryType )

					binaryTypeToDownloadToFileStateMap[ binaryType ] = 
						mergeSourceAndDestStateMapForBinaryTypeTransition( 
							binaryTypeToDownloadToFileStateMap[ binaryType ],
							scheduler, binaryType,
							startDownloadTransitionForBinaryType )

					return binaryTypeToDownloadToFileStateMap

				end,
				_startDownloadToFileTransitionsForBinaryType )

	end

	return _binaryTypeToDownloadToFileStateMap

end

--==============================================================================
-- @mark private unit test export funcs
--==============================================================================

local function getNeedsDownloadToFileSourceStateArray( scheduler )

	return PixelProviderSharedBlobCache.appendT(
				getNeedsProxyDownloadToFileSourceStateArray( scheduler ),
				getNeedsOriginalDownloadToFileSourceStateArray( scheduler ) )

end

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

local function maybeGetBinaryTypeForDownloadToFileState( scheduler, downloadToFileState )

	local binaryTypeToDownloadToFileStateMap = 
			getBinaryTypeToDownloadToFileStateMap( scheduler )

	for binaryType, binaryTypeStateMap in pairs( binaryTypeToDownloadToFileStateMap ) do

		if binaryTypeStateMap[ downloadToFileState ] then

			return binaryType

		end

	end
end

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

local function maybeGetNeedDownloadTransitionForBinaryType( binaryType )

	return _needsDownloadToFileTransitionsForBinaryType[ binaryType ]

end

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

local function isBinaryTypeForDownloadToFileState( scheduler, downloadToFileState, binaryType )

	local binaryTypeToDownloadToFileStateMap = 
			getBinaryTypeToDownloadToFileStateMap( scheduler )

	if not binaryTypeToDownloadToFileStateMap[ binaryType ] then

		error( string.format( "Unsupported binary type %s for download to file state: %s", 
											binaryType, downloadToFileState ) )

	end

	if binaryTypeToDownloadToFileStateMap[ binaryType ][ downloadToFileState ] then

		return true

	end

	return false

end

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

local function getDownloadToFileSuccessDestStates( scheduler )

	return PixelPathProviderSchedulerPrivateMethods.getDestStatesForTransition(
				scheduler, 'download_to_file_finished' )

end

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

local function getDownloadToFileFailureDestStates( scheduler )

	return KSTableUtils.extractKeys(
			makeBooleanSet(
				PixelProviderSharedBlobCache.appendT(
					PixelPathProviderSchedulerPrivateMethods.getDestStatesForTransition(
						scheduler, 'download_to_file_fatal_fail' ),
					PixelPathProviderSchedulerPrivateMethods.getDestStatesForTransition(
						scheduler, 'download_to_file_transient_fail' ) ) ) )

end

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

return {

	getNeedsDownloadToFileStates = getNeedsDownloadToFileStates,

	isNeedsDownloadToFileState = isNeedsDownloadToFileState,

	getIsDownloadingToFileStates = getIsDownloadingToFileStates,

	getIsDownloadingProxyToFileStates = getIsDownloadingProxyToFileStates,

	getIsDownloadingOriginalToFileStates =
		getIsDownloadingOriginalToFileStates,

	applyNeedsDownloadTransitionToPendingNeedDownloadLocalIdRequests = 
		applyNeedsDownloadTransitionToPendingNeedDownloadLocalIdRequests,

	maybeGetNeedsDownloadToFileRequests = maybeGetNeedsDownloadToFileRequests,

	getStartDownloadingTransition = getStartDownloadingTransition,

	applyStartTransitionToNeedsDownloadRequests = 
		applyStartTransitionToNeedsDownloadRequests,

	applyStartTransitionToNeedsDownloadLocalIdRequests = 
		applyStartTransitionToNeedsDownloadLocalIdRequests,

	getIsDownloadingToFileRequests = getIsDownloadingToFileRequests,

	maybeGetBinaryTypeForDownloadToFileState = 
		maybeGetBinaryTypeForDownloadToFileState,

	maybeGetNeedDownloadTransitionForBinaryType = 
		maybeGetNeedDownloadTransitionForBinaryType,

	privateUnitTestExports = {

		getNeedsOriginalDownloadToFileStateArray =
			getNeedsOriginalDownloadToFileStateArray,

		getNeedsDownloadToFileSourceStateArray = 
			getNeedsDownloadToFileSourceStateArray,

		isBinaryTypeForDownloadToFileState = 
			isBinaryTypeForDownloadToFileState,

		getDownloadToFileSuccessDestStates = 
			getDownloadToFileSuccessDestStates,

		getDownloadToFileFailureDestStates = 
			getDownloadToFileFailureDestStates,

	},

}
