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

WFCatLocalStorage.lua

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

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

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

	-- Rx
local Rx = require 'Rx'
local Observable = Rx.Observable

	-- WF.core
local KSPathUtils = require 'KSPathUtils'
local KSStringUtils = require 'KSStringUtils'
local KSTableUtils = require 'KSTableUtils'
local WFArguments = require 'WFArguments'
local WFAsyncFileUtils = require 'WFAsyncFileUtils'
local WFFileUtils = require 'WFFileUtils'
local WFLogger = require 'WFLogger'
local WFPlatformInfo = require 'WFPlatformInfo'

	-- WF.oz_client.managed_catalog
local WFCatVolumeAgentQueryHelper = require 'WFCatVolumeAgentQueryHelper' 

local NmbFileManagementSettings, LrmVolumeAgentSettings, LrmConfig

if WFPlatformInfo.isDesktop then
	NmbFileManagementSettings = require 'NmbFileManagementSettings'
elseif WFPlatformInfo.isMobile() then
	LrmVolumeAgentSettings = require 'LrmVolumeAgentSettings'
	LrmConfig = require 'LrmConfig'
end

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

local logger = WFLogger "WFCatLocalStorage"

local check = assert( WFArguments.check )

local storageByteString = assert( KSStringUtils.storageByteString )

local kPhotoCache = 'PhotoCache'
local kAppData = 'AppData'

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

local WFCatLocalStorage = {}

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

-- @mark Helper Functions

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

local function getReservedSpace()

	local reservedSpace = WFPlatformInfo.isMobile()
			and LrmVolumeAgentSettings.minimumFreeSpace()
			or NmbFileManagementSettings.minimumFreeSpace()
			
	return reservedSpace

end

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

local function platformAwareGbToBytes( gb )

	-- This function calculates the size in bytes based on the OS interpretation of
	-- 1GB. This is only necessary because this value is user visible within the
	-- local storage settings.
	
	local multiplier = WFPlatformInfo.bytesInKilobyte()
	
	return gb * multiplier * multiplier * multiplier

end

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

local function calculateStoredLocallySize( sizes, storeOriginalsOnMainVolume, keepAllOriginals, keepAllProxies )

	local sum = 0

	if storeOriginalsOnMainVolume then
	
		if keepAllOriginals then
			sum = sum + sizes.keepAllOriginalsOnMainVolumeSize
		else
			sum = sum + sizes.originalsOnMainVolumeSize
		end
		
	end

	if keepAllProxies then
		sum = sum + sizes.keepAllProxiesOnMainVolumeSize
	else
		sum = sum + sizes.proxiesOnMainVolumeSize
	end

	-- if we are keeping all proxies or originals then we are also keep the xmp_develop and aux files.
	if keepAllOriginals or keepAllProxies then
		sum = sum + sizes.keepAllXmpDevelopSize + sizes.keepAllAuxSize
	else
		sum = sum + sizes.xmpDevelopSize + sizes.auxSize
	end

	return sum

end

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

local function calculatePhotoCacheSize( sizes, keepAllOriginals, keepAllProxies )
		
	local photoCacheSize = 0
					
	if not keepAllOriginals then
		photoCacheSize = photoCacheSize + sizes.originalsOnMainVolumeSize
	end
	
	if not keepAllProxies then
		photoCacheSize = photoCacheSize + sizes.proxiesOnMainVolumeSize
	end

	-- Only include xmp_develop and aux binaries in photo cache when both keepAllOriginals and keepAllProxies are false
	if not keepAllOriginals and not keepAllProxies then
		photoCacheSize = photoCacheSize + sizes.xmpDevelopSize + sizes.auxSize
	end

	return photoCacheSize

end

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

-- @mark Photo Cache & App Data (Unsynced Binaries) Size

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

local function getBucketSize_Desktop( wfCatalog, bucket, volumeGuid, optOverrides )

	local indexStore = wfCatalog:indexStore()

	local getSizeFn
	
	if bucket == kPhotoCache then
	
		getSizeFn = WFCatVolumeAgentQueryHelper.getSizeOfPurgeableBinariesForType
	
	elseif bucket == kAppData then
	
		getSizeFn = WFCatVolumeAgentQueryHelper.getSizeOfUnsyncedBinariesForType
	
	else
	
		return Observable.just( {
				originalsOnMainVolumeSize = 0,
				proxiesOnMainVolumeSize = 0,
				xmpDevelopSize = 0,
				auxSize = 0,
			} )
	
	end

	-- Size of Originals

	local originalVolumeGuid = volumeGuid
	
	if optOverrides.debugOriginalDirNotOnMainVolume then
		originalVolumeGuid = 'fakeVolumeGuid' -- Only used for testing
	end

	local originalsOnMainVolumeSizeObs = getSizeFn( indexStore, 'original', originalVolumeGuid )
					
	-- Size of Proxies

	local proxiesOnMainVolumeSizeObs = getSizeFn( indexStore, 'proxy', volumeGuid )

	-- Size of XMP Develop
	local xmpDevelopSizeObs = getSizeFn( indexStore, 'xmp_develop', volumeGuid )

	-- Size of Aux
	local auxSizeObs = getSizeFn( indexStore, 'aux', volumeGuid )

	return Observable.combineLatest(
			originalsOnMainVolumeSizeObs,
			proxiesOnMainVolumeSizeObs,
			xmpDevelopSizeObs,
			auxSizeObs )
	
		:map( function( originalsOnMainVolumeSize, proxiesOnMainVolumeSize,
				xmpDevelopSize, auxSize )
		
			return {
				originalsOnMainVolumeSize = originalsOnMainVolumeSize,
				proxiesOnMainVolumeSize = proxiesOnMainVolumeSize,
				xmpDevelopSize = xmpDevelopSize,
				auxSize = auxSize,
			}
		
		end )

end

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

local function getBucketSize_Mobile( wfCatalog, bucket, binaryStrategy )

	local binaryTypes = {
		'original',
		'proxy',
		'xmp_develop',
		'aux'
	}

	local getSizeFn
	
	if bucket == kPhotoCache then
	
		binaryTypes[ #binaryTypes + 1 ] = 'fullsize'
		binaryTypes[ #binaryTypes + 1 ] = 'preview'

		getSizeFn = WFCatVolumeAgentQueryHelper.getSizeOfPurgeableBinariesForType
	
	elseif bucket == kAppData then
	
		getSizeFn = WFCatVolumeAgentQueryHelper.getSizeOfUnsyncedBinariesForType
	
	else
	
		return Observable.just( 0 )
	
	end

	local indexStore = wfCatalog:indexStore()

	local sizeStream = Observable.just( 0 )
	
	for _, binaryType in ipairs( binaryTypes ) do
	
		sizeStream = sizeStream:concat(
				getSizeFn( indexStore, binaryType, nil, binaryStrategy ) )
	
	end

	return sizeStream:sum()

end

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

local function getBucketSize( wfCatalog, bucket, optBinaryStrategy, optOverrides )

	if WFPlatformInfo.isDesktop then
	
		-- Only count binaries stored on a specific volume
		
		local catalogPath = wfCatalog:fileSpec():catalogPath()
		local volumeGuid = optOverrides.volumeGuid or WFFileUtils.getVolumeGuidForPath( catalogPath )
	
		return getBucketSize_Desktop( wfCatalog, bucket, volumeGuid, optOverrides )

	else

		return getBucketSize_Mobile( wfCatalog, bucket, optBinaryStrategy )

	end

end

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

-- @mark Marked For Local Storage Size

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

local function getMarkedForLocalStorageSize_Desktop( wfCatalog, onlyCountLocalBinaries, optOverrides )

	local indexStore = wfCatalog:indexStore()

	-- Size of Originals

	local keepAllOriginalsOnMainVolumeSizeObs =
			WFCatVolumeAgentQueryHelper.getSizeOfKeepAllBinariesForType( 
					indexStore, 'original', onlyCountLocalBinaries )
	
	local originalsOnMainVolumeSizeObs =
			WFCatVolumeAgentQueryHelper.getSizeOfStoredLocalBinariesForType( 
					indexStore, 'original', onlyCountLocalBinaries )

	-- Size of Proxies
	
	local keepAllProxiesOnMainVolumeSizeObs =
			WFCatVolumeAgentQueryHelper.getSizeOfKeepAllBinariesForType( 
					indexStore, 'proxy', onlyCountLocalBinaries )

	local proxiesOnMainVolumeSizeObs =
			WFCatVolumeAgentQueryHelper.getSizeOfStoredLocalBinariesForType( 
					indexStore, 'proxy', onlyCountLocalBinaries )

	-- Size of XMP Develop

	local keepAllXmpDevelopSizeObs =
			WFCatVolumeAgentQueryHelper.getSizeOfKeepAllBinariesForType(
					indexStore, 'xmp_develop', onlyCountLocalBinaries )

	local xmpDevelopSizeObs =
			WFCatVolumeAgentQueryHelper.getSizeOfStoredLocalBinariesForType(
					indexStore, 'xmp_develop', onlyCountLocalBinaries )

	-- Size of aux

	local keepAllAuxSizeObs =
			WFCatVolumeAgentQueryHelper.getSizeOfKeepAllBinariesForType(
					indexStore, 'aux', onlyCountLocalBinaries )

	local auxSizeObs =
			WFCatVolumeAgentQueryHelper.getSizeOfStoredLocalBinariesForType(
					indexStore, 'aux', onlyCountLocalBinaries )

	return Observable.combineLatest(
			originalsOnMainVolumeSizeObs,
			keepAllOriginalsOnMainVolumeSizeObs,
			proxiesOnMainVolumeSizeObs,
			keepAllProxiesOnMainVolumeSizeObs,
			xmpDevelopSizeObs,
			keepAllXmpDevelopSizeObs,
			auxSizeObs,
			keepAllAuxSizeObs )
	
		:map( function( originalsOnMainVolumeSize, keepAllOriginalsOnMainVolumeSize,
				proxiesOnMainVolumeSize, keepAllProxiesOnMainVolumeSize,
				xmpDevelopSize, keepAllXmpDevelopSize,
				auxSize, keepAllAuxSize )
		
			return {
			
				originalsOnMainVolumeSize = originalsOnMainVolumeSize,
				keepAllOriginalsOnMainVolumeSize = keepAllOriginalsOnMainVolumeSize,
				proxiesOnMainVolumeSize = proxiesOnMainVolumeSize,
				keepAllProxiesOnMainVolumeSize = keepAllProxiesOnMainVolumeSize,
				xmpDevelopSize = xmpDevelopSize,
				keepAllXmpDevelopSize = keepAllXmpDevelopSize,
				auxSize = auxSize,
				keepAllAuxSize = keepAllAuxSize,
			}
		
		end )

end

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

local function getMarkedForLocalStorageSize_Mobile( wfCatalog, onlyCountLocalBinaries, binaryStrategy, optAlbumId )

	local indexStore = wfCatalog:indexStore()

	local binaryTypes = {
		'proxy',
		'xmp_develop',
		'aux',
		'fullsize',
		'preview'
	}
	
	if binaryStrategy == LrmConfig.StrategyPreferMaster then

		binaryTypes[ #binaryTypes + 1 ] = 'original'
			-- See ozBinaryDownloaderMethods:startDownloading in LrmOzBinaryDownloader for more context.

	end

	local sizeStream = Observable.just( 0 )
	
	for _, binaryType in ipairs( binaryTypes ) do
	
		-- Count the size of binaries that are marked for local storage. This includes
		-- binaries that have yet to sync to Oz.
	
		sizeStream = sizeStream:concat(
				WFCatVolumeAgentQueryHelper.getSizeOfStoredLocalBinariesForType(
						indexStore, binaryType, onlyCountLocalBinaries, binaryStrategy, optAlbumId ) )
	
	end

	return sizeStream:sum()

end

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

local function getMarkedForLocalStorageSize( wfCatalog, optBinaryStrategy, optOverrides )

	if WFPlatformInfo.isDesktop then
	
		return getMarkedForLocalStorageSize_Desktop( wfCatalog, false, optOverrides )

	else

		local optAlbumId = optOverrides and optOverrides.albumId

		return getMarkedForLocalStorageSize_Mobile( wfCatalog, false, optBinaryStrategy, optAlbumId )

	end

end

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

local function getOnDeviceMarkedForLocalStorageSize( wfCatalog, optBinaryStrategy, optOverrides )

	if WFPlatformInfo.isDesktop then
	
		return getMarkedForLocalStorageSize_Desktop( wfCatalog, true, optOverrides )

	else

		local optAlbumId = optOverrides and optOverrides.albumId

		return getMarkedForLocalStorageSize_Mobile( wfCatalog, true, optBinaryStrategy, optAlbumId )

	end

end

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

-- @mark App & User Data Size

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

local function getAppFolderSize_Android()

	-- On Android, the app folder -- the folder that contains application data,
	-- databases (doc store, index store, etc) and other content -- is stored
	-- in a separarate folder than the originals, proxies et al. For the purposes
	-- of this utility, we need the size of the app folder and not the size of the
	-- folder that houses the binaries.
	
	-- This returns a path to: data/data/com.adobe.lrmobile

	local path =
			KSPathUtils.parent(
				KSPathUtils.parent(
					KSPathUtils.getStandardFilePath( 'carouselDocumentsInternal' ) ) )

	logger:infof( "App Folder Path: %s", path )

	return WFAsyncFileUtils.calculatePathSize( path )

end

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

local function getAppFolderSize( catalogPath )
   
   	-- SPAM [jandrew 2022-02-09]: This logic assumes that all catalog folders share
	-- a single parent folder. This is true for Desktop, and I believe it's true
	-- for mobile as well.
	
	local sizeStream
	
	local catalogPathParent = KSPathUtils.parent( catalogPath )

	logger:infof( "Catalog Parent Path: %s", catalogPathParent )

	if WFPlatformInfo.isDesktop then

		local originalsFolder = KSPathUtils.child( catalogPath, 'originals' )
		
		local proxiesFolder = KSPathUtils.child( catalogPath, 'proxies' )

		sizeStream = Observable.combineLatest(
				WFAsyncFileUtils.calculatePathSize( catalogPathParent ),
				WFAsyncFileUtils.calculatePathSize( originalsFolder ),
				WFAsyncFileUtils.calculatePathSize( proxiesFolder )
			)
			
			:map( function( parentSize, originalsFolderSize, proxiesFolderSize )
			
				return parentSize - originalsFolderSize - proxiesFolderSize
			
			end )

	elseif WFPlatformInfo.isiOS then

		sizeStream = WFAsyncFileUtils.calculatePathSize( catalogPathParent )
	
	elseif WFPlatformInfo.isAndroid then
	
		sizeStream = getAppFolderSize_Android()
	
	end

	return sizeStream

end

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

-- @mark LrA-Only Storage Used On Device

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

local function getAddlStorageUsedOnDeviceStream( catalogPath )

	if not WFPlatformInfo.isAndroid then
		return Observable.just( 0 ) -- This field is LrA-Only
	end

	return getAppFolderSize_Android()

end

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

-- @mark APIs

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

--[[!

	\brief Get the disk space usage of Lr

	\param wfCatalog (WFCatalog)
		
	\param optBinaryStrategy (optional string) LrM-Only: The download strategy.
		
	\param optOverrides (optional table) Optional overrides, primarly used in testing.
	
	\return <code>Observable< table ></code> The resulting table is platform specific:
	
		Mobile - Fires with a table of sizes. Example:
	
			{

				photoCache: The size of the purgeable cache of binaries. All binaries in this
					cache are synced with Oz. Locally stored binaries are not counted as part
					of the photo cache.
					
				storedLocally: The size of binaries that have been marked for local use,
					either globally (LrD) or through a parent album. The purge agent never
					removes binaries marked for local use.
					
				appAndUserData: The size of the application & required files. This consists
					of SQLite DBs (doc store, index store, etc), non-wf rendition caches,
					application data,as well as binaries that have yet to sync to Oz.
				
				addlStorageUsedOnDevice: LrA-Only: If an SD card is being used, it's the
					size of the catalog data on the device.
				
				freeSpace: The amount of free space on the volume that contains the photo cache.
				
			}
		
		Desktop - Fires with a table containing the bucket sizes for all possible permutations
			 of keepAllOriginals, keepAllProxies and storeOriginalsOnMainVolume:
			
			sizes = {
				false = {
					false = {
						false = {
						
							photoCache: The size of the purgeable cache of binaries. All binaries in this
								cache are synced with Oz. Locally stored binaries are not counted as part
								of the photo cache.
								
							storedLocally: The size of binaries that have been marked for local use,
								either globally (LrD) or through a parent album. The purge agent never
								removes binaries marked for local use.
								
							appAndUserData: The size of the application & required files. This consists
								of SQLite DBs (doc store, index store, etc), non-wf rendition caches,
								application data,as well as binaries that have yet to sync to Oz.
							
							addlStorageUsedOnDevice: LrA-Only: If an SD card is being used, it's the
								size of the catalog data on the device.
							
							freeSpace: The amount of free space on the volume that contains the photo cache.
							
						},
						true = {
							...
						}
					},
					true = {
						...
					}
				}
			true = {
				...
			}
		}
			
		LrD code is responsible for extracting the appropriate nested table using
		booleans as the table keys: 
		
		sizes[ storeOriginalsOnMainVolume ][ keepAllOriginals ][ keepAllProxies ]  
	
--]]

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

function WFCatLocalStorage.getLocalStorageSize( wfCatalog, optBinaryStrategy, optOverrides )

	check( 'WFCatalog', '?+string', '?table' )

	optOverrides = optOverrides or {}

	if WFPlatformInfo.isMobile() then
		optBinaryStrategy = optBinaryStrategy or LrmConfig.StrategyPreferProxy
	end

	local catalogPath = wfCatalog:fileSpec():catalogPath()
	
	-- Photo Cache
	
	local photoCacheSizesStream = getBucketSize( wfCatalog, kPhotoCache, optBinaryStrategy, optOverrides )

	-- Marked For Local Storage
	
	local markedForLocalStorageSizesStream = getMarkedForLocalStorageSize( wfCatalog, optBinaryStrategy, optOverrides )
			
	-- On-Device & Marked For Local Storage Size
	
	local onDeviceMarkedForLocalStorageSizesStream = getOnDeviceMarkedForLocalStorageSize( wfCatalog, optBinaryStrategy, optOverrides )

	-- Additional Storage Used on Device
	
	local addlStorageUsedOnDeviceStream = getAddlStorageUsedOnDeviceStream( catalogPath )
	
	-- App Folder Sizes
	
	local appFolderSizeStream = getAppFolderSize( catalogPath )
	
	-- Size of Unsynced Binaries
	
	local unsyncedBinariesSizesStream = getBucketSize( wfCatalog, kAppData, optBinaryStrategy, optOverrides )
	
	-- Bring it all together
	
	return Observable.combineLatest(
			photoCacheSizesStream,
			markedForLocalStorageSizesStream,
			onDeviceMarkedForLocalStorageSizesStream,
			addlStorageUsedOnDeviceStream, 
			appFolderSizeStream,
			unsyncedBinariesSizesStream )

		:map( function( photoCacheSize, markedForLocalStorageSize,
				onDeviceMarkedForLocalStorageSize, addlStorageUsedOnDevice, 
				appFolderSize, unsyncedBinariesSizes )
			
			-- Free Space
			
			local success, maybeFreeSpace = pcall( WFFileUtils.freeSpaceAtPath, catalogPath )
			local freeSpace = success and maybeFreeSpace or 0
			
			local reservedSpace = getReservedSpace()
			
			freeSpace = math.max( 0, freeSpace - reservedSpace )
			
			if WFPlatformInfo.isDesktop then

				-- Consider all 8 possible permutations of:
				-- storeOriginalsOnMainVolume = true|false
				-- keepAllOriginals = true|false
				-- keepAllProxies = true|false

				local permutations = {}

				for _, storeOriginalsOnMainVolume in ipairs( { false, true } ) do
				
					for _, keepAllOriginals in ipairs( { false, true } ) do
					
						for _, keepAllProxies in ipairs( { false, true } ) do
						
							local actualPhotoCacheSize = calculatePhotoCacheSize( 
									photoCacheSize, keepAllOriginals, keepAllProxies )
							
							local actualMarkedForLocalStorageSize = calculateStoredLocallySize( 
									markedForLocalStorageSize, storeOriginalsOnMainVolume, keepAllOriginals, keepAllProxies )

							local unsyncedBinariesSize = unsyncedBinariesSizes.originalsOnMainVolumeSize +
								unsyncedBinariesSizes.proxiesOnMainVolumeSize +
								unsyncedBinariesSizes.xmpDevelopSize +
								unsyncedBinariesSizes.auxSize

							-- Subtract purgeable xmp_develop and aux from appAndUserData since they are already counted in photo cache
							local appAndUserDataSize = math.max( 0, appFolderSize ) +
								(unsyncedBinariesSize - photoCacheSize.xmpDevelopSize - photoCacheSize.auxSize)
							
							logger:info( "-----------------------------------" )
							logger:infof( "Permutation( storeOriginalsOnMainVolume %s keepAllOriginals %s keepAllProxies %s", 
									storeOriginalsOnMainVolume, keepAllOriginals, keepAllProxies  )
									
							logger:infof( "Photo Cache size: %s", storageByteString( actualPhotoCacheSize ) )
							logger:infof( "Stored Locally size: %s", storageByteString( actualMarkedForLocalStorageSize ) )
							logger:infof( "App & User Data size: %s", storageByteString( appAndUserDataSize ) )
							logger:infof( "Free Space: %s", storageByteString( freeSpace ) )
							
							local buckets =  {
								
								photoCache = actualPhotoCacheSize,
								storedLocally = actualMarkedForLocalStorageSize,
								appAndUserData = appAndUserDataSize,
								freeSpace = freeSpace,
								addlStorageUsedOnDevice = 0

							}
								
							KSTableUtils.setNested(
									permutations, buckets,
									storeOriginalsOnMainVolume,
									keepAllOriginals, keepAllProxies )

						end
					
					end
					
				end
				
				return permutations

			else
			
				-- There's only one permutation on Mobile:
			
				local appAndUserDataSize

				if WFPlatformInfo.isiOS then

					appAndUserDataSize = math.max( 0, appFolderSize - photoCacheSize - onDeviceMarkedForLocalStorageSize + unsyncedBinariesSizes )

				else

					-- On Android, the photo cache and locally stored files are stored
					-- outside of the app folder.

					appAndUserDataSize = math.max( 0, appFolderSize ) + unsyncedBinariesSizes

				end

				logger:infof( "Photo Cache size: %s", storageByteString( photoCacheSize ) )
				logger:infof( "Stored Locally size: %s", storageByteString( markedForLocalStorageSize ) )
				logger:infof( "On Device & Stored Locally size: %s", storageByteString( onDeviceMarkedForLocalStorageSize ) )
				logger:infof( "App & User Data size: %s", storageByteString( appAndUserDataSize ) )
				logger:infof( "Free Space: %s", storageByteString( freeSpace ) )
				
				if WFPlatformInfo.isAndroid then
					logger:infof( "Additional Storage used on Device: %s", storageByteString( addlStorageUsedOnDevice ) )
				end
				
				return {
				
					photoCache = photoCacheSize,
					storedLocally = markedForLocalStorageSize,
					appAndUserData = appAndUserDataSize,
					freeSpace = freeSpace,
					addlStorageUsedOnDevice = addlStorageUsedOnDevice
					
				}
		
			end	
			
		end )
	
end

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

--[[!

	\brief Get the Size of the Photo Cache

	\param wfCatalog (WFCatalog)
	\param volumeGuid ( optional boolean ) LrD-Only: If specified, we will only count
		the binaries living on the specified volume. This is only used when calculating
		the photo cache size of an external volume.
	\param keepAllOriginals( optional boolean ) LrD-Only
	\param keepAllProxies( optional boolean ) LrD-Only
	\param optOriginalsPath( optional string ) LrD-Only: The locations where
		originals are stored.
	\param optBinaryStrategy (optional string) LrM-Only: The download strategy.
 
	\return <code>Observable< number></code> Fires once with the size of the 
		purgeable cache of binaries. All binaries in this cache are synced
		with Oz. Locally stored binaries are not counted as part of the 
		photo cache.

--]]

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

function WFCatLocalStorage.getPhotoCacheSizeStream(
		wfCatalog, volumeGuid, keepAllOriginals, keepAllProxies, optBinaryStrategy )

	check( 'WFCatalog', '?+string', '?boolean', '?boolean', '?+string' )

	local overrides = {
		volumeGuid = volumeGuid
	}
	
	if WFPlatformInfo.isMobile() then
		optBinaryStrategy = optBinaryStrategy or LrmConfig.StrategyPreferProxy
	end

	return getBucketSize( wfCatalog, kPhotoCache, optBinaryStrategy, overrides )
	
		:map( function( photoCacheSize )
		
			if WFPlatformInfo.isDesktop then
		
				return calculatePhotoCacheSize( 
						photoCacheSize, keepAllOriginals, keepAllProxies )
					
			else
			
				return photoCacheSize
			
			end
		
		end )
			
		:tap( function( size )
			logger:infof( "Size of photo cache: %s", storageByteString( size ) )
		end )
	
end

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

--[[!

	\brief Get the the size of a locally stored-locally album.

	\param wfCatalog (WFCatalog)
	\param albumId (string) The full document GUID of the album.
	\param optBinaryStrategy (optional string) LrM-Only: The download strategy.

	\return <code>Observable< number></code> Fires once with the size of the
		binaries associated with the stored-locally album.

--]]

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

function WFCatLocalStorage.getStoredLocallySizeForAlbum_Mobile(
		wfCatalog, albumId, optBinaryStrategy )

	check( 'WFCatalog', '+string', '?+string' )

	if WFPlatformInfo.isMobile() then
		optBinaryStrategy = optBinaryStrategy or LrmConfig.StrategyPreferProxy
	end

	local overrides = {
		albumId = albumId
	}

	return getMarkedForLocalStorageSize( wfCatalog, optBinaryStrategy, overrides )

		:tap( function( size )
			logger:infof( "Size of stored-locally album: %s", storageByteString( size ) )
		end )

end

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

--[[!

	\brief Returns a table containing cache size recommendations, as well as
		minimum and maximum cache size values.

	\param wfCatalog (WFCatalog)
	
	\param optOverrides (table) A table of optional overrides. Used for testing.
	
	\return <code>Observable< table></code> A table of cache size values:
	
		{
			{
				size = <max cache size in bytes>
				flag = 'max'
			},
			{
				size = <min cache size in bytes>
				flag = 'min'
			},
			{
				size = <current cache size in bytes>
				flag = 'current'
			},
			{
				size = <default cache size in bytes>
				flag = 'default'
			},
			{
				size = <suggestion 1 in bytes>
			},
			{
				size = <suggestion 2 in bytes>
			},
			...
		}

--]]

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

function WFCatLocalStorage.getRecommendedCacheSizes( wfCatalog, optOverrides )

	check( 'WFCatalog', '?table' )
    
	optOverrides = optOverrides or {}

	local catalogPath = wfCatalog:fileSpec():catalogPath()
	
	local _, totalSpace = WFFileUtils.freeSpaceAtPath( catalogPath )
	totalSpace = optOverrides.totalSpace or totalSpace

	local recommendedSizes = {}

	local currentMaximumCacheSize = WFPlatformInfo.isMobile()
			and LrmVolumeAgentSettings.targetCacheSize()
			or NmbFileManagementSettings.targetCacheSize()

	local reservedSpace = getReservedSpace()
	reservedSpace = optOverrides.reservedSpace or reservedSpace

	local maximumCacheSizeInBytes = totalSpace - reservedSpace
			
	recommendedSizes = {
		{
			size = maximumCacheSizeInBytes,
			flag = 'max'
		},
		{
			size = 0,
			flag = 'min'
		},
		{
			size = currentMaximumCacheSize,
			flag = 'current'
		},
		{
			size = platformAwareGbToBytes( 10 ),
			flag = 'default'
		},
		{
			size = platformAwareGbToBytes( 1 )
		},
		{
			size = platformAwareGbToBytes( 5 )
		},
		{
			size = platformAwareGbToBytes( 25 )
		},
		{
			size = platformAwareGbToBytes( 50 )
		}
	}
		

	logger:infof( "Recommended Cache Sizes: %s", recommendedSizes )

	return Observable.just( recommendedSizes )
	
end

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

return WFCatLocalStorage
