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

PixelProviderSharedTaskUtils.lua

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

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

------------------------------------------------------------------------------]]
	-- Rx
local Rx = require 'Rx'
	
	-- wf.core
local KSTableUtils = require 'KSTableUtils'
local WFFunctionUtils = require 'WFFunctionUtils'
local WFFileUtils = require 'WFFileUtils'
local KSDate = require 'KSDate'
local KSIdGenerator = require 'KSIdGenerator'

	-- wf.oz_client
local WFCatUser = require 'WFCatUser'
local WFCatSpec = require 'WFCatSpec'
local WFCatFileSpec = require 'WFCatFileSpec'
local WFCatalog = require 'WFCatalog'
	
	-- pixel provider
local PixelProviderTaskCache = require 'PixelProviderTaskCache'
local PixelPathProviderStateMachine = require 'PixelPathProviderStateMachine'

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

local logger = require 'WFLogger'( "PixelProviderSharedTaskUtils" )

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

local WFFunctionUtils_reduce = assert( WFFunctionUtils.reduce,
			"WFFunctionUtils.reduce does not exist" )

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

local KSTableUtils_derefOpt = KSTableUtils.derefOpt

--------------------------------------------------------------------------------
-- key:
--
-- 	localAssetId
--
-- value:
-- 
-- { ozAssetId = assetId,
-- 		changeCounter = newChangeCounter,
-- 		revId = newRevId,
-- 		digest = newDigest,
-- 		croppedLongSide = newCroppedLongSide,
-- 		originalPath = newOriginalPath,
-- 		proxyPath = newProxyPath, 
-- 		renditions = newRenditions }
--
-- lifetime:
--
--  stays in cache as long as there is at least one localAssetId request in
--  scheduler queue
--------------------------------------------------------------------------------
	
local function makeBaseRenderInfoCache()

	return PixelProviderTaskCache.makeLocalAssetSimpleDataCache()

end

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

local NmbLocalRenditionDigest_isUniqueEmptySettingsForDownloadOnlyAsset =
		assert( require "NmbLocalRenditionDigest".isUniqueEmptySettingsForDownloadOnlyAsset,
				"NmbLocalRenditionDigest.isUniqueEmptySettingsForDownloadOnlyAsset is missing!" )

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

local function mustUseOzForRendersForCachedBaseRenderInfo( baseRenderInfo )
	
	if NmbLocalRenditionDigest_isUniqueEmptySettingsForDownloadOnlyAsset(
				baseRenderInfo.xmpCameraRaw ) then

		-- download only assets have unique but empty xmpCameraRaw values so
		-- that we can properly generate a rendition digest for them.
		-- 
		-- However, we should never render using that xmpCameraRaw, so we
		-- return true 

		return true

	end

	-- At this point, we have renderable xmpCameraRaw

	if not( baseRenderInfo.ozValidOriginalLink or 
					baseRenderInfo.originalPath or 
					baseRenderInfo.proxyPath or 
					( type( baseRenderInfo.proxies ) == "table" and
						#baseRenderInfo.proxies > 0 ) ) then

		-- if all of the above checks failed, the baseRenderInfo
		-- contains neither local renderable source files nor 
		-- downloadable source file urls.
		--
		-- Return true so we don't try to render.

		return true

	end

	-- At this point we have renderable xmpCameraRaw and either
	-- a local source path or a downloadable source path
	--
	-- return false to indicate rendering is possible

	return false

end

--------------------------------------------------------------------------------
-- key:
--
-- 	localAssetId
--
-- value:
--
-- 	current dev settings for localAssetId
--
-- lifetime:
--
--  stays in cache as long as there is at least one localAssetId request in
--  scheduler queue.
--
--  TODO: bkruse 2015.11.03 we should consider refining this lifetime
-- 
--------------------------------------------------------------------------------
local function makeCurrRenderSettingsCache()

	return PixelProviderTaskCache.makeLocalAssetSimpleDataCache()

end

--------------------------------------------------------------------------------
-- key:
--
-- 	pixStream, localAssetId
--
-- value:
--
-- 	{ renditionPath, digest, renderSize, croppedLongSide, 
-- 		maybeDevSettings }
--
-- lifetime:
--
--  stays in cache until we send the renditionPath or enter one of EndStates
--  for pixStreamId, localAssetId
-- 
--------------------------------------------------------------------------------
local function makeActiveRenditionInfoCache()

	return PixelProviderTaskCache.makePerClientLocalAssetSimpleDataCache()

end

--------------------------------------------------------------------------------
-- key:
--
-- 	pixStream, localAssetId
--
-- value:
--
-- 	{ binaryPath, binarySize }
--
-- lifetime:
--
--  stays in cache until we send the binaryPath or enter one of EndStates
--  for pixStreamId, localAssetId
-- 
--------------------------------------------------------------------------------
local function makeLocalBinaryPathCache()

	return PixelProviderTaskCache.makePerClientLocalAssetSimpleDataCache()

end

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

local function isVideoRenderInfo( renderInfo )

	local subtype = renderInfo.subtype

	return subtype == 'video' or subtype == 'deleted_video'

end

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

local function findFirstMatchingHrefInfo( matchingOzHrefInfo, ozBinarySubtype, 
											ozHrefInfoList ) 

	if matchingOzHrefInfo then

		return matchingOzHrefInfo

	end
	
	for _, ozHrefInfo in ipairs( ozHrefInfoList ) do

		if tostring( ozHrefInfo.size ) == tostring( ozBinarySubtype ) then

			return ozHrefInfo

		end

	end
	
end

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

local function maybeGetHrefFromListForOzBinarySubtype( 
				ozBinarySubtype, maybeOzHrefInfoList )

	local ozHrefInfo = findFirstMatchingHrefInfo( 
						nil, ozBinarySubtype, maybeOzHrefInfoList or {} )

	local  maybeHref = KSTableUtils_derefOpt( ozHrefInfo, "href" )

	if not maybeHref then

		return false

	end

	return maybeHref

end

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

local function maybeGetHrefForValidRenditionWithSizeFromRenderInfo( 
				renderInfo, renditionSize )

	return maybeGetHrefFromListForOzBinarySubtype( 
				renditionSize, renderInfo.renditions )

end

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

local function maybeGetHrefForInvalidRenditionWithSizeFromRenderInfo( 
				renderInfo, renditionSize )

	return maybeGetHrefFromListForOzBinarySubtype( 
				renditionSize, renderInfo.invalid_renditions )

end

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

local function maybeGetHrefForProxyTypeFromRenderInfo( 
				renderInfo, proxyType )

	return maybeGetHrefFromListForOzBinarySubtype( 
				proxyType, renderInfo.proxies )

end

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

local function maybeGetFirstMatchingSubtypeFromList( 
				ozBinarySubtypes, ozHrefInfoList )

	local ozHrefInfo =
		WFFunctionUtils_reduce( nil,
			findFirstMatchingHrefInfo,
				ozBinarySubtypes, ozHrefInfoList )

	if ozHrefInfo then

		return ozHrefInfo.size

	end

	return false

end

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

local function maybeGetPreferredSizeSubtype( proxies, preferredSize )

	if preferredSize then
		for _, ozHrefInfo in ipairs( proxies ) do
			if type( ozHrefInfo ) == "table" and ozHrefInfo.size == preferredSize then
				return ozHrefInfo.size
			end
		end
	end
	
	return false
	
end
--------------------------------------------------------------------------------

local function maybeGetExactProxyBinarySubtype( renderInfo, subtypeList, preferredSize )

	-- for a given subtype list, this tries to find a matching href in the 
	-- proxies href list. 

	local proxies = KSTableUtils_derefOpt( renderInfo, "proxies" ) or {}
	
	local ozBinarySubtype = ( not preferredSize and maybeGetFirstMatchingSubtypeFromList( subtypeList, proxies ) ) or
							( preferredSize and maybeGetPreferredSizeSubtype( proxies, preferredSize ) )
	
	if not ozBinarySubtype and preferredSize then
		-- If we had a preferredSize but could not find it, get the next best one as a fallback
		ozBinarySubtype = maybeGetFirstMatchingSubtypeFromList( subtypeList, proxies )
	end

	if ozBinarySubtype then

		return ozBinarySubtype

	end

	return false
	
end

--------------------------------------------------------------------------------
--
-- 20220303 bkruse
--
-- 720p is actually a rendition that LrD treats as a proxy for legacy reasons:
--
-- Oz didn't support the 1080p sizes until 2022

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

local _preferredVideoProxySizes = { "1080p", "1080p709", "720p" }

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

local function maybeGetBinarySubtypeForVideoProxy( renderInfo, preferredSize )

	return maybeGetExactProxyBinarySubtype( 
			renderInfo, _preferredVideoProxySizes, preferredSize )
	
end

--------------------------------------------------------------------------------
-- we intentionally are using a string for the size here!

local _preferredImageProxySizes = { "2560" }

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

local function maybeGetBinarySubtypeForImageProxy( renderInfo )

	return maybeGetExactProxyBinarySubtype( 
			renderInfo, _preferredImageProxySizes )

end

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

local function maybeGetBinarySubtypeForProxyImp( maybeBaseRenderInfo, preferredSize )

	if type( maybeBaseRenderInfo ) ~= "table" then

		return false

	end

	local renderInfo = maybeBaseRenderInfo

	if isVideoRenderInfo( renderInfo ) then

		return maybeGetBinarySubtypeForVideoProxy( renderInfo, preferredSize )

	end

	return maybeGetBinarySubtypeForImageProxy( renderInfo )

end

--------------------------------------------------------------------------------
--[[!
	\brief maybe Returns the proxy sub-type matching the asset info provided.

	\param baseRenderInfoCache (renderInfo cache)
			localAssetId 

	\return proxy sub-type (string) Can be any of the following
		"2560"
		"1080p", "1080p709", "720p"

	Currently it doesn't return 'panorama' in any case. This logic
	will have to evolve to take into account 'panorama' and any
	other case which is not handled right now.
]]--

local function maybeGetBinarySubtypeForProxy( baseRenderInfoCache, localAssetId, preferredSize )

	local maybeBaseRenderInfo = baseRenderInfoCache:peekCacheItem( localAssetId )

	if type( maybeBaseRenderInfo ) ~= "table" then

		logger:warnf( "maybeGetBinarySubtypeForProxy has no render info for %s", localAssetId )

		return false

	end

	return maybeGetBinarySubtypeForProxyImp( maybeBaseRenderInfo, preferredSize )

end

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

local function renderInfoHasDownloadableProxy( renderInfo )
	
	if maybeGetBinarySubtypeForProxyImp( renderInfo ) then
		
		return true

	end

	return false

end

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

local function ozRenditionNameToRenderSize( renditionName ) 
	
	local renditionSizeInPixels = tonumber( renditionName ) 
	
	if renditionSizeInPixels then 

		return renditionSizeInPixels

	end
	
	if renditionName == 'thumbnail2x' then

		return 320

	end
	
	if renditionName == 'panorama' then

		return 8192

	end

	if renditionName == "fullsize" then

		return "full"

	end

	return renditionName

end

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

local function largerStandardDownloadableSize( currMaxRenderSize, renditionInfo )

	local ozRenditionSize = KSTableUtils_derefOpt( renditionInfo, 'size' )

	if not currMaxRenderSize and ozRenditionSize == "fullsize" then

		return currMaxRenderSize

	end

	local renderSizeInPixels = 
			tonumber( ozRenditionNameToRenderSize( ozRenditionSize ) )

	if not renderSizeInPixels then

		return currMaxRenderSize

	end

	local currMaxSizeInPixels = tonumber( currMaxRenderSize ) or 0

	return renderSizeInPixels > currMaxSizeInPixels and
			renderSizeInPixels or currMaxSizeInPixels

end

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

local function getMaxDownloadableStandardRenderSize( baseRenderInfo )

	local renditions = 
			KSTableUtils_derefOpt( baseRenderInfo, 'renditions' ) or {}

	return WFFunctionUtils_reduce( 
				nil, largerStandardDownloadableSize, renditions )

end

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

local requestStatesThatWantMaximumDownloadableRenditionSize =
	KSTableUtils.mapArrayToSet( {

		assert( PixelPathProviderStateMachine.SendDataStates.MaybeSendLoupeRenditionPathFromInMemoryCache ),
		assert( PixelPathProviderStateMachine.PixelCacheCheckStates.NeedCheckForLoupeRenditionInCache ),

	} )

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

local function maybeUpdateRequestLongSideForMaximumDownloadableSize( 
				scheduler, pixStreamId, localAssetId, requestState, 
				baseRenderInfoCache )

	local maybeBaseRenderInfo =
			baseRenderInfoCache:peekCacheItem( localAssetId )

	if not requestStatesThatWantMaximumDownloadableRenditionSize[ requestState ] then

		return maybeBaseRenderInfo

	end

	local maxDownloadableRenderSize =
			maybeBaseRenderInfo and 
			getMaxDownloadableStandardRenderSize( maybeBaseRenderInfo )

	if maxDownloadableRenderSize then

		scheduler
			:maybeUpdateLongSide( 
				pixStreamId, localAssetId, maxDownloadableRenderSize )

	end

	return maybeBaseRenderInfo, maxDownloadableRenderSize

end

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

local function makeNonSparseStringArray( ... )

	local ret = {}

	for i = 1, select( '#', ... ) do

		local maybeStr = select( i, ... )


		if type( maybeStr ) ~= "string" then

			error( string.format( 
					"parameter at position %s is not a string", i ) )

		end

		ret[ #ret + 1 ] = maybeStr

	end

	return ret

end

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

local function makeWFCatalog( serverBaseUrl, userId, userInfoCachePath,
								managedCatalogIdOrDoc, rootCatalogDataDir,
								catalogOptions )

	local wfCatUser = WFCatUser( serverBaseUrl, userId, userInfoCachePath )

	local wfCatSpec = WFCatSpec( wfCatUser, managedCatalogIdOrDoc )

	return WFCatalog( wfCatSpec, rootCatalogDataDir, catalogOptions )

end

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

local function calculateLocalProxyPath( fileSpecObj, assetId, ozBinarySizeName )

	return fileSpecObj:pathForProxy( assetId, ozBinarySizeName )

end

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

local function calculateLocalOriginalPath( fileSpecObj, docValue )

	local captureDate = docValue.captureDate
	
	captureDate = type( captureDate ) == 'string' and 
					KSDate.isKSDate( captureDate ) and
					captureDate or 
					'0000-00-00T00:00:00.000' -- Unknown date
	
	local fileName = KSTableUtils.derefOpt( docValue, 'importSource', 'fileName' )

	fileName = type( fileName ) == "string" and
					fileName or KSIdGenerator.uuidInLowercaseHex()

	return fileSpecObj:pathForOriginal( fileName, captureDate, true )

end

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

local function calculateLocalXmpSidecarPath( fileSpecObj, assetId )

	return fileSpecObj:pathForXmpSidecar( assetId )

end

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

local function makeneedDevelopAuxFileDownloadCache()

	return PixelProviderTaskCache.makeLocalAssetSimpleDataCache()

end

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

local function getValidOriginalDownloadInfoFromRenderInfo( baseRenderInfo )

	if baseRenderInfo.ozValidOriginalLink then

		return baseRenderInfo.ozValidOriginalLink, baseRenderInfo.sha256

	end

end

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

local function getOriginalDownloadInfoFromRenderInfo( baseRenderInfo )

	if getValidOriginalDownloadInfoFromRenderInfo( baseRenderInfo ) then

		return getValidOriginalDownloadInfoFromRenderInfo( baseRenderInfo )

	end

	if baseRenderInfo.ozInvalidOriginalLink then

		return baseRenderInfo.ozInvalidOriginalLink, "invalid"

	end

end

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

local function isKnownInvalidSha256( sha256 )

	return sha256 == "invalid"

end

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

local function updateDocStoreXmpDevelopInfoStream( 
				docStore, localAssetId, filePath, fileSize, sha256 )
	
	local localOnly = {
		files = {
			xmp_develop = {
				path = filePath,
				fileSize = fileSize,
				sha256 = sha256,
			}
		}
	}

	local overlay = { 
		_id = localAssetId,
		_localOnly = localOnly,
	}					
	
	return docStore
			:applyOverlay( overlay )
			:take( 1 )

end

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

local function makeFormattedErrStream( errStr, ... )

	return Rx.Observable.throw( string.format( errStr, ... ) )

end

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

local derefOpt = KSTableUtils.derefOpt

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

local function generateLocalFilePathForOriginal( fileSpecObj, docValue )

	local assetId = derefOpt( docValue, '_id' )

	return assetId and 
			calculateLocalOriginalPath( fileSpecObj, docValue )	

end

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

local function generateLocalFilePathForProxy( fileSpecObj, docValue, 
												ozBinarySizeName )

	local assetId = derefOpt( docValue, '_id' )

	return assetId and 
			calculateLocalProxyPath( fileSpecObj, assetId, ozBinarySizeName )	

end

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

local function canonicalAssetNegativeLocalBinaryPathStream( 
				docStore, fileSpecObj, localAssetId, ozBinaryType,
				ozBinarySizeName )

	if ozBinaryType ~= 'original' and ozBinaryType ~= 'proxy' then

		return makeFormattedErrStream( 
				"Wrong binary type sent to canonicalAssetNegativeLocalBinaryPathStream: %s", 
				ozBinaryType )

	end

	return docStore
			:docValue( localAssetId )
			:map( 

				function( docValue )

					if docValue._empty then
						local errStr = string.format( "Binary type %s for localAssetId %s has been deleted",
														ozBinaryType, localAssetId )
						error( errStr )
					end

					
					local filePath 
					local sha256
					
					if ozBinaryType == 'original' then
						filePath = generateLocalFilePathForOriginal( fileSpecObj, docValue )
						local _, isInvalid = WFCatFileSpec.getOriginalLinkFromDocValue( docValue )
						sha256 = isInvalid and 'invalid' or derefOpt( docValue, 'importSource', 'sha256' )
					elseif ozBinaryType == 'proxy' then
						filePath = generateLocalFilePathForProxy( fileSpecObj, docValue, ozBinarySizeName )
						local _, _, isInvalid = WFCatFileSpec.getProxyLinkFromDocValue( docValue )
						sha256 = isInvalid and 'invalid' or derefOpt( docValue, 'importSource', 'sha256' )
					end
					
					if not filePath then
					
						local errStr = string.format( "Unable to create a %s file path for localAssetId %s",
															ozBinaryType, localAssetId )
						error( errStr )
					
					end
					
					-- Ensure that the path is unique

					local uniquifiedFilePath = WFFileUtils.ensureFileNameIsUnique( filePath )

					if uniquifiedFilePath ~= filePath then

						-- By design originals must be uniquified by adding a numeric suffix.
						-- One example is duplicating doesn't change the importSource.fileName,
						-- but there may be other workflows that result in the same fileName.
						if ozBinaryType ~= 'original' then

							logger:warnf( "Saving multiple copies of binary %s for localAssetId %s",
														ozBinaryType, localAssetId )
						end

						filePath = uniquifiedFilePath

					end
				
					return filePath, sha256

				end )

end

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

-- updateAssetDocValueLocalOnlyNegativePathResultStream
-- 
-- 	This function has 2 intents
-- 	
-- 	1) (occurs always) Cause the docValue._changeCounter to increment so that
-- 	   both
-- 	   	
-- 	   	docValue
-- 				:ongoingDocValue()
-- 				
-- 			and
-- 			
-- 		docStore
-- 			:scannedFilteredMappedChangesFeed()
-- 				
-- 	   will emit a new docValue for localAssetId
-- 		
-- 	2) (occurs more rarely, usually when external storage present) make sure
-- 	   the docStore contains the actual final path written. Sometimes due
-- 	   to duplicate filenames, the final write path can differ from the before
-- 	   write path.
-- 	
--------------------------------------------------------------------------------

local function updateAssetDocValueLocalOnlyNegativePathResultStream( 
				docStore, localAssetId, ozBinaryType, ozBinarySizeName,
				finalSavePath, maybeOrigSHA256 )

	if ozBinaryType ~= 'original' and ozBinaryType ~= 'proxy' then

		return makeFormattedErrStream(
				"Wrong binary type sent to updateAssetDocValueLocalOnlyNegativePathResultStream: %s", 
				ozBinaryType )

	end

	if not ozBinarySizeName then

		return makeFormattedErrStream(
				"No binary size sent to updateAssetDocValueLocalOnlyNegativePathResultStream: %s", 
				ozBinarySizeName )

	end

	local perBinaryPostSaveLocalOnlyData = 
			{

				sha256 = maybeOrigSHA256,

			}

	if finalSavePath then
	
		local finalSaveVolumeGuid = WFFileUtils.getVolumeGuidForPath( finalSavePath )

		local finalSaveRelativeFilePath = WFFileUtils.removeVolumeFromPath( finalSavePath )

		if type( finalSaveVolumeGuid ) == 'string' and 
				type( finalSaveRelativeFilePath ) == 'string' then

			-- always set the perBinaryPostSaveLocalOnlyData path components
			-- based on finalSavePath. It's possible for those path components to
			-- be different from what they were before the save due to in use
			-- filename disambiguation. If they don't change, no harm in setting
			-- them to the same value as in the docStore.
			-- 
			perBinaryPostSaveLocalOnlyData.volumeGuid = finalSaveVolumeGuid

			perBinaryPostSaveLocalOnlyData.relativePath =
				finalSaveRelativeFilePath

		end
		
	end

	-- The lastSaveTimestamp field is intended to be uniquish every time we
	-- do a save for a binary. So, we use "now" as the value. This guarantees
	-- that perBinaryPostSaveLocalOnlyData will contain at least one value that
	-- is different from the cuurent docstore values and therefore the
	-- applyOverlay( ... ) call will update the docValue._changeCounter for the
	-- localAssetId
	perBinaryPostSaveLocalOnlyData.lastSaveTimestamp =
		KSDate.timeInIso8601Format()
	
	local perBinaryLocalOnlyAnnotationUpdateOverlay = { 

		_id = localAssetId,

		_localOnly = {
			files = {
				[ ozBinaryType ] = {
					[ tostring( ozBinarySizeName ) ] = 
						perBinaryPostSaveLocalOnlyData,
				}
			}
		}
	}
	
	return docStore
			-- we expect this :applyOverlay to always cause the
			-- docValue._changeCounter to change. If it does not, the UI client
			-- may hang in loupe view because it will not receive notifications
			-- that a new negative is available locally.
			:applyOverlay( perBinaryLocalOnlyAnnotationUpdateOverlay ) 
			:take( 1 )
		
end

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

return {

	makeBaseRenderInfoCache = makeBaseRenderInfoCache,

	mustUseOzForRendersForCachedBaseRenderInfo = 
		mustUseOzForRendersForCachedBaseRenderInfo,

	makeCurrRenderSettingsCache = makeCurrRenderSettingsCache,

	makeActiveRenditionInfoCache = makeActiveRenditionInfoCache,

	makeLocalBinaryPathCache = makeLocalBinaryPathCache,

	ozRenditionNameToRenderSize = ozRenditionNameToRenderSize,

	maybeUpdateRequestLongSideForMaximumDownloadableSize =
		maybeUpdateRequestLongSideForMaximumDownloadableSize,

	maybeGetHrefForValidRenditionWithSizeFromRenderInfo =
		maybeGetHrefForValidRenditionWithSizeFromRenderInfo,

	maybeGetHrefForInvalidRenditionWithSizeFromRenderInfo =
		maybeGetHrefForInvalidRenditionWithSizeFromRenderInfo,

	maybeGetHrefForProxyTypeFromRenderInfo =
		maybeGetHrefForProxyTypeFromRenderInfo,

	maybeGetBinarySubtypeForProxy = maybeGetBinarySubtypeForProxy,

	renderInfoHasDownloadableProxy = renderInfoHasDownloadableProxy,

	makeNonSparseStringArray = makeNonSparseStringArray,
	
	makeWFCatalog = makeWFCatalog,

	calculateLocalProxyPath = calculateLocalProxyPath,

	calculateLocalOriginalPath = calculateLocalOriginalPath,

	calculateLocalXmpSidecarPath = calculateLocalXmpSidecarPath,

	makeneedDevelopAuxFileDownloadCache = makeneedDevelopAuxFileDownloadCache,

	getValidOriginalDownloadInfoFromRenderInfo =
		getValidOriginalDownloadInfoFromRenderInfo,

	getOriginalDownloadInfoFromRenderInfo =
		getOriginalDownloadInfoFromRenderInfo,

	isKnownInvalidSha256 = isKnownInvalidSha256,

	updateDocStoreXmpDevelopInfoStream = updateDocStoreXmpDevelopInfoStream,

	canonicalAssetNegativeLocalBinaryPathStream =
		canonicalAssetNegativeLocalBinaryPathStream,

	updateAssetDocValueLocalOnlyNegativePathResultStream =
		updateAssetDocValueLocalOnlyNegativePathResultStream,

}

