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

NmbStorage.lua

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

ADOBE CONFIDENTIAL
------------------
Copyright 2018 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'
local Observable = Rx.Observable

	-- WF.core.os
local KSPathUtils = require 'KSPathUtils'
local WFAsyncFileUtils = require 'WFAsyncFileUtils'
local WFFileUtils = require 'WFFileUtils'

	-- WF.core.utils
local WFArguments = require 'WFArguments'
local WFConfiguration = require "WFConfiguration"

	-- WF.core.diagnostic_log
local WFDiagnosticLog = require 'WFDiagnosticLog'

local NmbFileManagementSettings = require 'NmbFileManagementSettings'
	
--------------------------------------------------------------------------------

local CHECK_FOR_LEAKED_FILES_IN_LOG = WFConfiguration.Shared.checkForLeakedFilesInLog

local check = assert( WFArguments.check )

local child = assert( KSPathUtils.child )

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

local NmbStorage = {}

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

local function sizeOfAllAssets( wfCatalog )
	
	check( 'WFCatalog' )
	
	local sizeOfMarkedForOfflineAssetsQuery = wfCatalog:indexStore():query( { 
			sql = [[SELECT SUM( originalSize ), 
						SUM( proxySize ), 
						( SELECT COUNT( * ) FROM assetPathsAndInfo WHERE proxySize IS NULL AND isMarkedForOffline == 1 ) as proxiesWithoutSize
					FROM assetPathsAndInfo
					WHERE isMarkedForOffline == 1 ]],
			calm = 1,
		} )
	
	local sizeOfAllAssetsQuery = wfCatalog:indexStore():query( { 
			sql = [[SELECT SUM( originalSize ), 
						SUM( proxySize ), 
						( SELECT COUNT( * ) FROM assetPathsAndInfo WHERE proxySize IS NULL ) as proxiesWithoutSize
					FROM assetPathsAndInfo]],
			calm = 1,
		} )
		
	return Observable.combineLatest( sizeOfAllAssetsQuery, sizeOfMarkedForOfflineAssetsQuery )
		:map( function( allAssets, markedForOfflineAssets )
			
			-- Process All Assets
			
			local allAssetsResult = allAssets[ 1 ] or {}
			
			local totalSizeOfOriginals = allAssetsResult[ 1 ] or 0
			local totalSizeOfProxies = allAssetsResult[ 2 ] or 0
			local proxiesWithoutSize = allAssetsResult[ 3 ] or 0
			
			-- Oz does _not_ store the size of the proxy. The only way we can get this
			-- info is by downloading the asset. For the proxies we have not downloaded,
			-- we estimate the size instead.
			
			local averageSizeOfProxy = 1331692
				-- https://docs.google.com/spreadsheets/d/1YD7jh7GgWJQHe2VUfMD5liGQyxJeadap40WU8TWl4mg
			
			local estimatedSizeOfAllUntrackedProxies = ( proxiesWithoutSize * averageSizeOfProxy ) or 0
			
			totalSizeOfProxies = totalSizeOfProxies + estimatedSizeOfAllUntrackedProxies
			
			-- Process Marked For Offline Use Assets
			
			local markedForOfflineAssetsResult = markedForOfflineAssets[ 1 ] or {}
			
			local markedForOfflineSizeOfOriginals = markedForOfflineAssetsResult[ 1 ] or 0
			local markedForOfflineSizeOfProxies = markedForOfflineAssetsResult[ 2 ] or 0
			local markedForOfflineProxiesWithoutSize = markedForOfflineAssetsResult[ 3 ] or 0
			
			local estimatedSizeOfUntrackedMarkedForOfflineProxies = ( markedForOfflineProxiesWithoutSize * averageSizeOfProxy ) or 0
			local sizeOfMarkedForOfflineOriginals = markedForOfflineSizeOfOriginals
			local sizeOfMarkedForOfflineProxies = markedForOfflineSizeOfProxies + estimatedSizeOfUntrackedMarkedForOfflineProxies
			
			-- Return result
			
			return totalSizeOfOriginals, totalSizeOfProxies, sizeOfMarkedForOfflineOriginals, sizeOfMarkedForOfflineProxies
			
		end )

end

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

local function getCurrentPathForOriginals( wfCatalog, rootPath )

	check( 'WFCatalog', '?string' )

	local originalsPathInfo = wfCatalog:fileSpec():originalsPathInfo() or {}
	local originalDirInRoot = true
	local originalsPath
	
	local success, originalsRootPath = pcall( 
					WFFileUtils.createFullPathFromRelativePathAndGuid,
					originalsPathInfo.relativePath, originalsPathInfo.volumeGuid )
					
	if success  -- no error on pcall
		and originalsRootPath and ( type( originalsRootPath ) == 'string' ) then -- drive is connected
		
		originalsPath = child( originalsRootPath, 'originals' )
		
		if not KSPathUtils.isPathParentOf( rootPath, originalsPath ) then -- path not under root
			originalDirInRoot = false
		end
		
	end

	local originalDirOnMainVolume = originalsPathInfo.volumeGuid == WFFileUtils.getVolumeGuidForPath( rootPath )

	return originalsPath, originalDirInRoot, originalDirOnMainVolume

end

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

local function getCurrentPathForProxies( wfCatalog, rootPath, originalsPath )

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

	local proxiesPathInfo = wfCatalog:fileSpec():proxiesPathInfo() or {}
	local proxyDirInRoot = true
	local proxiesPath

	local proxySuccess, proxiesRootPath = pcall( 
					WFFileUtils.createFullPathFromRelativePathAndGuid,
					proxiesPathInfo.relativePath, proxiesPathInfo.volumeGuid )
	
	if proxySuccess -- no error on pcall
		and proxiesRootPath and ( type( proxiesRootPath ) == 'string' ) then -- drive is connected
		
		proxiesPath = child( proxiesRootPath, 'proxies' )
		
		if not KSPathUtils.isPathParentOf( rootPath, proxiesPath ) and -- path not under root
			not KSPathUtils.isPathParentOf( originalsPath, proxiesPath ) then -- path not under originals
				proxyDirInRoot = false
		end
		
	end

	local proxyDirOnMainVolume = proxiesPathInfo.volumeGuid == WFFileUtils.getVolumeGuidForPath( rootPath )

	return proxiesPath, proxyDirInRoot, proxyDirOnMainVolume

end

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

function NmbStorage.localStorage( wfCatalog )

	check( 'WFCatalog' )

	-- Calculate the size of the root path ( Nimbus directory )
	
	local rootPath = wfCatalog:fileSpec():catalogPath()
	local calculateRootPathSize = WFAsyncFileUtils.calculatePathSize( rootPath )
	
	-- Calculate the size of the originals directory if it lives outside of the root path.
	
	local originalsPath, originalDirInRoot, originalDirOnMainVolume = getCurrentPathForOriginals( wfCatalog, rootPath )
	local maybeCalculateOriginalSize = originalsPath and WFAsyncFileUtils.calculatePathSize( originalsPath ) or Observable.just( 0 )
	
	-- Calculate the size of the proxies directory if it lives outside of the root path.
	
	local proxiesPath, proxyDirInRoot, proxyDirOnMainVolume = getCurrentPathForProxies( wfCatalog, rootPath, originalsPath )
	local maybeCalculateProxySize = proxiesPath and WFAsyncFileUtils.calculatePathSize( proxiesPath ) or Observable.just( 0 )
	
	-- Free Space

	local successFreeSpace, fileSystemFreeSpace
	
	successFreeSpace, fileSystemFreeSpace = pcall( WFFileUtils.freeSpaceAtPath, rootPath )

	-- Size of ALL assets on Oz
	
	local sizeOfAllAssets = sizeOfAllAssets( wfCatalog )
	
	-- Bring it all together
	
	return Rx.Observable.combineLatest( 
			function( rootPathSize, maybeOriginalPathSize, maybeProxyPathSize, 
						sizeOfKeepAllOriginals, sizeOfKeepAllProxies, 
						sizeOfMarkedForOfflineOriginals, sizeOfMarkedForOfflineProxies )
			
				local used = rootPathSize or 0
				local required = rootPathSize or 0
				local sizeOnMainVolume = rootPathSize or 0
				
				maybeOriginalPathSize = maybeOriginalPathSize or 0
				maybeProxyPathSize = maybeProxyPathSize or 0
				
				if not originalDirInRoot and originalDirOnMainVolume then
					sizeOnMainVolume = sizeOnMainVolume + maybeOriginalPathSize
				end
				
				if not proxyDirInRoot and proxyDirOnMainVolume then
					sizeOnMainVolume = sizeOnMainVolume + maybeProxyPathSize
				end
				
				local freeSpace = successFreeSpace and fileSystemFreeSpace or 0
				
				freeSpace = freeSpace - NmbFileManagementSettings.minimumFreeSpace()
				
				if originalDirInRoot then
					required = required - maybeOriginalPathSize
				else
					used = used + maybeOriginalPathSize
				end
				
				if proxyDirInRoot then
					required = required - maybeProxyPathSize
				else
					used = used + maybeProxyPathSize
				end
			
				return {
						used = used,
						required = required,
						sizeOnMainVolume = sizeOnMainVolume,
						freeSpace = freeSpace,
						sizeOfKeepAllOriginals = sizeOfKeepAllOriginals,
						sizeOfKeepAllProxies = sizeOfKeepAllProxies,
						sizeOfMarkedForOfflineOriginals = sizeOfMarkedForOfflineOriginals,
						sizeOfMarkedForOfflineProxies = sizeOfMarkedForOfflineProxies
					}
			end,
			calculateRootPathSize,
			maybeCalculateOriginalSize,
			maybeCalculateProxySize,
			sizeOfAllAssets )
	
end

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

function NmbStorage.addDiagnosticLogSectionFunc( catalog )

	WFDiagnosticLog.addLogSectionFunc( function()

		local NmbLeakedFileDetector = require 'NmbLeakedFileDetector'
		
		local rootPath = catalog:fileSpec():catalogPath()
		
		local mainVolumeRootPathForOriginals = child( rootPath, 'originals' )
		
		local rootPathsToOriginals = {
			mainVolumeRootPathForOriginals
		}
		
		local rootPathForOriginals = getCurrentPathForOriginals( catalog, rootPath )
		
		if mainVolumeRootPathForOriginals ~= rootPathForOriginals then
			table.insert( rootPathsToOriginals, rootPathForOriginals )
		end

		local mainVolumeRootPathForProxies = child( rootPath, 'proxies' )

		local rootPathsToProxies = {
			mainVolumeRootPathForProxies
		}

		local rootPathForProxies = getCurrentPathForProxies( catalog, rootPath, rootPathForOriginals )
		
		if mainVolumeRootPathForProxies ~= rootPathForProxies then
			table.insert( rootPathsToProxies, rootPathForProxies )
		end

		local body = {
			localStorageInfo = NmbStorage.localStorage( catalog ):take( 1 )
		}

		if CHECK_FOR_LEAKED_FILES_IN_LOG then

			body.leakedAssets = NmbLeakedFileDetector.checkForLeakedFiles( catalog:indexStore(), 'original', rootPathsToOriginals )
				:flatMapLatest( function( leakedOriginals )

					return NmbLeakedFileDetector.checkForLeakedFiles( catalog:indexStore(), 'proxy', rootPathsToProxies )

						:map( function( leakedProxies )

							return {
								leakedOriginals = leakedOriginals,
								leakedProxies = leakedProxies
							}

						end )

				end )

		end

		return {

			name = "NmbStorage",
			id = 'NmbStorage',
			weight = 510,
			parentSection = WFDiagnosticLog.Sections.LOCAL_STORAGE,

			body = body,

		}

	end )

end

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

return NmbStorage
