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

LrmOzPurgeAgentUtils.lua


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

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

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

This class monitors the content of a single catalog's data cache and periodically
purges it.

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

	-- RxLua
local Rx = require 'Rx'

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

	-- WF.core.rxlua
local WFArguments = require 'WFArguments'

	-- WF.core.utils
local KSTableUtils = require 'KSTableUtils'

	-- WF.os.prefs
local WFPlatformInfo = require 'WFPlatformInfo'

local OzCatalogUploadStatus = require 'OzCatalogUploadStatus'

	-- WF.lrmobile.modules.source
local LrmConfig = require 'LrmConfig'
local WFCatAssetUtils = require 'WFCatAssetUtils'

	-- WF.oz_client.utils
local OzFilePaths = require 'OzFilePaths'

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

LrmConfig.registerBinaryStrategyCheck()

local check = assert( WFArguments.check )

local derefUniversal = assert( WFCatAssetUtils.derefUniversal )
local universalPath = assert( WFCatAssetUtils.universalPath )

local derefOpt = KSTableUtils.derefOpt

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

local LrmOzPurgeAgentUtils = {}

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

-- platform dependent forward declarations

local cacheReferencesPathKey

local userUpdatedKey

local removeCacheEntriesAndXmpsForDocument

local absolutePath

local purgeBinariesDirectly

local maybePurgeXmpDevelopFiles

if WFPlatformInfo.isiOS then

	cacheReferencesPathKey = 'path'

	userUpdatedKey = 'userUpdated'

	purgeBinariesDirectly = { 
		original = true, 
	 	proxy = true,
		thumbnail2x = true,
		preview = true,
		xmp_develop = true,
		presetProfile = true
	}

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

	absolutePath = function( catalogId, relativePath )
	
		check( 'string', 'string' )
	
		local firstChar = string.sub( relativePath, 1, 1 )
		if firstChar == '/' then
			return relativePath
		else
			local basePath = OzFilePaths.getCatalogBasePath( catalogId )
			
			return KSPathUtils.child( basePath, relativePath )
			
		end
	
	end
	
	--------------------------------------------------------------------------------

	removeCacheEntriesAndXmpsForDocument = function( _, _, docValue )

		return Rx.Observable.just( docValue )
	
	end
	
	--------------------------------------------------------------------------------

	maybePurgeXmpDevelopFiles = function( _, _ )
	
		return Rx.Observable.just( true )
	
	end 
	
	--------------------------------------------------------------------------------

	
elseif WFPlatformInfo.isAndroid then

	cacheReferencesPathKey = 'md5'

	userUpdatedKey = 'epoch'

	purgeBinariesDirectly = { 
		original = true, 
	 	proxy = true,
		presetProfile = true
	}

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

	local function cacheEntriesForDocument( docValue )

		local renditionNames = { 'thumbnail', 'thumbnail2x', 'preview' }
		local keys = {}
		
		for _, renditionName in ipairs( renditionNames ) do
			
			local md5 = derefOpt( docValue, '_localOnly', renditionName, 'md5' )
		
			if md5 then
				table.insert( keys, md5 )
			end
						
		end

		return keys

	end

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

	removeCacheEntriesAndXmpsForDocument = function( wfCatalog, dataCache, docValue )

		check( 'WFCatalog', 'KSDataCache', 'table' )

		local xmpRelativePath = derefUniversal( docValue, 'files', 'xmp_develop', 'relativePath' )

		local purgeXmpDevelop

		if xmpRelativePath then
			purgeXmpDevelop = maybePurgeXmpDevelopFiles( wfCatalog, { xmpRelativePath } ) 
		else
			purgeXmpDevelop = Rx.Observable.just( docValue )
		end

		local cacheEntries = cacheEntriesForDocument( docValue )

		if ( next( cacheEntries ) ) then

			-- remove the cache entries
			return purgeXmpDevelop
				:flatMap( function()
					return dataCache:removeBulk( cacheEntries )
				end )
				:defaultIfEmpty( docValue )
				:map( function()
					return docValue
				end )
		
		end

		return purgeXmpDevelop
			:map( function()
				return docValue
			end )
	end

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

	absolutePath = function( catalogId, relativePath )
		
		check( 'string', 'string' )

		local absolutePath

		if relativePath then

			if string.sub( relativePath, 1, 1 ) == '/' then
				absolutePath = relativePath
			else
				-- the relative path might have the catalogid already prepended or not
				if string.sub( relativePath, 1, catalogId:len() ) == catalogId then
					local documentsDir = KSPathUtils.getStandardFilePath( 'carouselDocuments' )
					absolutePath = KSPathUtils.child( documentsDir, relativePath )
				else
					absolutePath = OzFilePaths.getAbsolutePathInCatalog( catalogId, relativePath )
				end
			end

		end
		
		return OzCatalogUploadStatus.checkAndCorrectLocalPath( absolutePath )

	end

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

	maybePurgeXmpDevelopFiles = function( wfCatalog, developSettingsRelPathsToDelete )

		check( 'WFCatalog', 'table' )

		local documentsDir = KSPathUtils.getStandardFilePath 'carouselDocuments'

		if not developSettingsRelPathsToDelete or not next( developSettingsRelPathsToDelete ) then
			return Rx.Observable.just( true )
		end

		-- Count all the local develop settings files, grouped by the develop settings path(
		-- which has the file name as the SHA256 ).


		return wfCatalog:indexStore():queryOnce {
			sql = [[ SELECT cr.value, count( cacheReferences.docId )
						FROM CARRAY( ?, ?, 'char*' ) AS cr
						LEFT JOIN cacheReferences ON cr.value = cacheReferences.md5
						GROUP BY cr.value ]],
			developSettingsRelPathsToDelete,
			#developSettingsRelPathsToDelete,
			quiesceUntilUpToDate = true,
			
		}
			
		:map( function( rows )

			for _, row in ipairs( rows ) do
			
				local relativePath = row[ 1 ]
				local count = row[ 2 ]
				
				if count == 0 then
				
					-- If ever the count is 0, we know that the no document has 
					-- this specific SHA256 for the develop settings, meaning we can remove the associated local file.
				
					if relativePath and relativePath ~= '' then

						local filePath = KSPathUtils.child( documentsDir, relativePath )

						WFFileUtils.deleteFile( filePath )

					end
				
				end
			
			end
			
			return true
		
		end )

	end

end

LrmOzPurgeAgentUtils.cacheReferencesPathKey = assert( cacheReferencesPathKey )

LrmOzPurgeAgentUtils.removeCacheEntriesAndXmpsForDocument = assert( removeCacheEntriesAndXmpsForDocument )

LrmOzPurgeAgentUtils.absolutePath = assert( absolutePath )

LrmOzPurgeAgentUtils.purgeBinariesDirectly = assert( purgeBinariesDirectly )

LrmOzPurgeAgentUtils.userUpdatedKey = assert( userUpdatedKey )

LrmOzPurgeAgentUtils.maybePurgeXmpDevelopFiles = assert( maybePurgeXmpDevelopFiles )

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

function LrmOzPurgeAgentUtils.renditionPath( docValue, renditionName )

	local path

	if renditionName == 'thumbnail' or renditionName == 'thumbnail2x' then
		path = derefUniversal( docValue, 'files', 'renditions', renditionName, 'relativePath' )
	elseif renditionName == 'preview' then
		path = derefUniversal( docValue, 'files', 'renditions', '2048', 'relativePath' )
	elseif renditionName == 'proxy' then
		path = derefUniversal( docValue, 'files', 'proxy', '2560', 'relativePath' )
	elseif renditionName == 'original' then
		path = derefUniversal( docValue, 'files', 'original', 'master', 'relativePath' )
	elseif renditionName == 'latestOriginal' then
		path = derefUniversal( docValue, 'files', 'original', 'latestMaster', 'relativePath' )
	elseif renditionName == 'xmp_develop' then
		path = derefUniversal( docValue, 'files', renditionName, 'relativePath' )
	elseif renditionName == 'presetProfile' then
		path = derefUniversal( docValue, 'files', 'presetProfile', 'relativePath' )
	end
	
	return path

end

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

function LrmOzPurgeAgentUtils.updateOverlayWithRenditionName( overlay, renditionName )

	if renditionName == 'original' then

		KSTableUtils.setNested( overlay, { _delete = true },
			universalPath( 'files', 'original', 'master' ) )

		-- MEH [immisch 2019-03-13]: deal with the irregular layout in the old _localOnly/originalFilePath
		KSTableUtils.setNested( overlay, { _delete = true },
			unpack { '_localOnly', 'originalFilePath' } )

	elseif renditionName == 'latestOriginal' then

		KSTableUtils.setNested( overlay, { _delete = true },
			universalPath( 'files', 'original', 'latestMaster' ) )

		-- MEH [immisch 2019-03-13]: deal with the irregular layout in the old _localOnly/originalFilePath
		KSTableUtils.setNested( overlay, { _delete = true },
			unpack { '_localOnly', 'latestOriginal', 'filePath' } )

	elseif renditionName == 'proxy' then
	
		KSTableUtils.setNested( overlay, { _delete = true },
			universalPath( 'files', 'proxy', '2560' ) )
			
	elseif renditionName == 'thumbnail2x' or renditionName == 'thumbnail' then
	
		KSTableUtils.setNested( overlay, { _delete = true },
			universalPath( 'files', 'renditions', renditionName ) )
			
	elseif renditionName == 'preview' then
	
		KSTableUtils.setNested( overlay, { _delete = true },
			universalPath( 'files', 'renditions', '2048' ) )
			
	elseif renditionName == 'xmp_develop' then
	
		KSTableUtils.setNested( overlay, { _delete = true },
			universalPath( 'files', 'xmp_develop' ) )
			
	end

	return overlay

end

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

return LrmOzPurgeAgentUtils
