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

NmbMRUDatabase.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.

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

	-- RxLua
local Rx = require 'Rx'

	-- WF.core.utils
local WFArguments = require 'WFArguments'
local WFCloseSupport = require 'WFCloseSupport'
local WFWorkerThread = require 'WFWorkerThread'
local KSJsonEncoder = require 'KSJsonEncoder'
local WFRxRunUntilDone = require 'WFRxRunUntilDone'

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

local runUntilDone = WFRxRunUntilDone

local check = assert( WFArguments.check )


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

-- @mark NmbMRUDatabase namespace

local NmbMRUDatabase = {

	_mruDatabaseImp = false,
		-- (WFWorkerThread)

	WFCloseSupport.addCloseSupportMethods,	
	
}

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

--[[!

	\brief Creates a persistent SQLite database for tracking binary data access.

	\param dbPath (string) The path to the db.

	\return NmbMRUDatabase instance object
	
--]]

function NmbMRUDatabase:__init( docStorePath, indexSpecScript, fileAccessDbPath, optArgs )

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

	local workerThreadArgs = KSJsonEncoder.encodeOzUploadJson( 
								{ 
									docStorePath,
									indexSpecScript,
									fileAccessDbPath,
									optArgs
								} )
								
	local mruDatabaseThreadName = "NmbMRUDatabaseMessageProcessor.lua"

	self:setOwned {

		_mruDatabaseImp = WFWorkerThread( mruDatabaseThreadName, workerThreadArgs )

	}
	
end

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

function NmbMRUDatabase:close()
										
	check( 'NmbMRUDatabase' )
	
end

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

function NmbMRUDatabase:haltAndDelete()

	return self._mruDatabaseImp:request( 'haltAndDelete' )

end


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

-- File Access Database APIs

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

function NmbMRUDatabase:getContinueFromChangeCounter()

	check( 'NmbMRUDatabase' )
	
	return self._mruDatabaseImp:request( 'getContinueFromChangeCounter' )
	
end

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

--[[!

	\brief Add a new entry, or update an existing entry in the db.

	\param localId (number) Local ID of the asset on disk.
	\param optDate (timestamp) optionally override the last access date.
	
	\return <code>Observable< nil ></code> that completes on success. There's
  		no need to subscribe to this observable; it's only used by unit tests.
	
--]]

function NmbMRUDatabase:touch( localIdOrDocId, optDate )

	check( 'NmbMRUDatabase', 'number|string', '?number' )
	
	return self._mruDatabaseImp:request( 
			'touch', 
			localIdOrDocId, optDate )
			:call( runUntilDone )
			
end

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

--[[!

	\brief Add multiple new entries, or ignore the updates if they already exists.

	\param localIdsOrDocIds (table) table of local ids or doc ids
	\param optDate (timestamp) optionally override the last access date.
	
	\return <code>Observable< nil ></code> that completes on success. There's
  		no need to subscribe to this observable; it's only used by unit tests.
	
--]]

function NmbMRUDatabase:touchMultipleIfNew( localIdsOrDocIds, optDate )

	check( 'NmbMRUDatabase', 'table', '?number' )
		
	return self._mruDatabaseImp:request( 
			'touchMultipleIfNew', 
			localIdsOrDocIds, optDate )
			:call( runUntilDone )
			
end

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

--[[!

	\brief Update the change counter for this DB.

	\param changerCounter (number) The change counter at the time of the update.
	
	\return <code>Observable< nil ></code> that completes on success. There's
  		no need to subscribe to this observable; it's only used by unit tests.
	
--]]

function NmbMRUDatabase:updateChangeCounter( changeCounter )

	check( 'NmbMRUDatabase', 'number' )
	
	return self._mruDatabaseImp:request( 
			'updateChangeCounter', 
			changeCounter )
			:call( runUntilDone )
	
end

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

--[[!

	\brief Update an existing entry's isImportant status in the db.

	\param localIdOrDocId (number|string) localId or docId of the asset.
	\param isImportant (boolean) Indicates if the asset is important.
	
	\return <code>Observable< nil ></code> that completes on success. There's
  		no need to subscribe to this observable; it's only used by unit tests.
	
--]]

function NmbMRUDatabase:updateAssetInfo( assetInfo )

	check( 'NmbMRUDatabase', 'table' )
	
	return self._mruDatabaseImp:request( 'updateAssetInfo', assetInfo )
			:call( runUntilDone )
			
end

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

--[[!

	\brief Delete an asset from the database for a given local Id.

	\param localIdOrDocId (number|string) localId or docId of the asset.
	
	\return <code>Observable< nil ></code> that completes on success. There's
  		no need to subscribe to this observable; it's only used by unit tests.
	
--]]

function NmbMRUDatabase:delete( localIdOrDocId )

	check( 'NmbMRUDatabase', 'number|string' )

	return self._mruDatabaseImp:request( 
			'delete', 
			localIdOrDocId )
			:call( runUntilDone )
			
end

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

--[[!

	\brief TEST USE ONLY - dump the contents of the db.

	\param void
	
	\return Contents of the db
	
--]]

function NmbMRUDatabase:DEBUG_dumpLastAccessContents()

	check( 'NmbMRUDatabase' )

	return self._mruDatabaseImp:request( 'DEBUG_dumpLastAccessContents' )

end


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

function NmbMRUDatabase:waitUntilUpToDate()

	check( 'NmbMRUDatabase' )
	
	return self._mruDatabaseImp:request( 'waitUntilUpToDate' )
	
end

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

--[[!

	\brief Retrieve the current list of purge candidates from the db, sorted
	by time of last access, but only for assets that haven't been accessed within
	N days.

	\param volumeGuid (string) Only return assets that live on this specific volume.
	\param timeWindow (table)
	\param limit (optional number) Limit the number of assets to search for.
	\param skipNMostRecent (optional number) Skip the N most recent assets from the query.

	\return <code>Observable< array ></code> An array of purge candidates, along with a function for getting the next
	batch of limited data. Example:
	
		local fiveRecentlyAccessed, getNextBatchFunc = NmbMRUDatabase.getLeastRecentlyAccessedInWindow( 10, 5 )
		...
		fiveRecentlyAccessed, getNextBatchFunc = getNextBatchFunc()
		...
		fiveRecentlyAccessed, getNextBatchFunc = getNextBatchFunc()
		...
		fiveRecentlyAccessed, getNextBatchFunc = getNextBatchFunc()
		...
		
		and so on.
	
--]]

function NmbMRUDatabase:getLeastRecentlyAccessedInWindow( volumeGuid, timeWindow, limit, skipNMostRecent, offset )

	check( 'NmbMRUDatabase', 'string', 'table', '?number', '?number', '?number' )

	return self._mruDatabaseImp:request( 
			'getLeastRecentlyAccessedInWindow', 
			volumeGuid, timeWindow, limit, skipNMostRecent, offset )

		:map( function( assets, nextBatchOffset )
		
			local getNextBatchFn = function()
			
				return self:getLeastRecentlyAccessedInWindow( 
						volumeGuid, timeWindow, limit, skipNMostRecent, nextBatchOffset )
			
			end
		
			return assets, getNextBatchFn
		
		end )

end

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

function NmbMRUDatabase:getLeastRecentlyAccessedProxiesInWindow( volumeGuid, timeWindow, limit, skipNMostRecent, offset )

	check( 'NmbMRUDatabase', 'string', 'table', '?number', '?number', '?number' )
	
	return self._mruDatabaseImp:request( 
			'getLeastRecentlyAccessedProxiesInWindow', 
			volumeGuid, timeWindow, limit, skipNMostRecent, offset )
	
		:map( function( assets, nextBatchOffset )
		
			local getNextBatchFn = function()
			
				return self:getLeastRecentlyAccessedProxiesInWindow( 
						volumeGuid, timeWindow, limit, skipNMostRecent, nextBatchOffset )
			
			end
		
			return assets, getNextBatchFn
		
		end )
	
end

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

function NmbMRUDatabase:getLeastRecentlyAccessedOriginalsInWindow( volumeGuid, timeWindow, limit, skipNMostRecent, offset )

	check( 'NmbMRUDatabase', 'string', 'table', '?number', '?number', '?number' )
	
	return self._mruDatabaseImp:request( 
			'getLeastRecentlyAccessedOriginalsInWindow', 
			volumeGuid, timeWindow, limit, skipNMostRecent, offset )
	
		:map( function( assets, nextBatchOffset )
		
			local getNextBatchFn = function()
			
				return self:getLeastRecentlyAccessedOriginalsInWindow( 
						volumeGuid, timeWindow, limit, skipNMostRecent, nextBatchOffset )
			
			end
		
			return assets, getNextBatchFn
		
		end )
	
end

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

--[[!

	\brief Retrieve the current list of inactive, proactive purge candidates from the 
	db, sorted by time of last access, but only for assets that haven't been accessed 
	within N days.

	\param volumeGuid (string) Only return assets that live on this specific volume.
	\param timeWindow (table)
	\param limit (optional number) Limit the number of assets to search for.
	\param skipNMostRecent (optional number) Skip the N most recent assets from the query.

	\return <code>Observable< array ></code> An array of purge candidates, along with a function for getting the next
	batch of limited data. Example:
	
		local fiveRecentlyAccessed, getNextBatchFunc = NmbMRUDatabase.getInactiveAssetsInWindow( ... )
		...
		fiveRecentlyAccessed, getNextBatchFunc = getNextBatchFunc()
		...
		fiveRecentlyAccessed, getNextBatchFunc = getNextBatchFunc()
		...
		fiveRecentlyAccessed, getNextBatchFunc = getNextBatchFunc()
		...
		
		and so on.
	
--]]

function NmbMRUDatabase:getInactiveAssetsInWindow( volumeGuid, timeWindow, limit, skipNMostRecent, offset )

	check( 'NmbMRUDatabase', 'string', 'table', '?number', '?number', '?number' )
	
	return self._mruDatabaseImp:request( 
			'getInactiveAssetsInWindow', 
			volumeGuid, timeWindow, limit, skipNMostRecent, offset )
	
		:map( function( assets, nextBatchOffset )
		
			local getNextBatchFn = function()
			
				return self:getInactiveAssetsInWindow( 
						volumeGuid, timeWindow, limit, skipNMostRecent, nextBatchOffset )
			
			end
		
			return assets, getNextBatchFn
		
		end )
	
end

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

function NmbMRUDatabase:getInactiveProxiesInWindow( volumeGuid, timeWindow, limit, skipNMostRecent, offset )

	check( 'NmbMRUDatabase', 'string', 'table', '?number', '?number', '?number' )

	return self._mruDatabaseImp:request( 
			'getInactiveProxiesInWindow', 
			volumeGuid, timeWindow, limit, skipNMostRecent, offset )
	
		:map( function( assets, nextBatchOffset )
		
			local getNextBatchFn = function()
			
				return self:getInactiveProxiesInWindow(
						volumeGuid, timeWindow, limit, skipNMostRecent, nextBatchOffset )
			
			end
		
			return assets, getNextBatchFn
		
		end )
	
end

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

function NmbMRUDatabase:getInactiveOriginalsInWindow( volumeGuid, timeWindow, limit, skipNMostRecent, offset )

	check( 'NmbMRUDatabase', 'string', 'table', '?number', '?number', '?number' )
	
	return self._mruDatabaseImp:request( 
			'getInactiveOriginalsInWindow', 
			volumeGuid, timeWindow, limit, skipNMostRecent, offset )
	
		:map( function( assets, nextBatchOffset )
		
			local getNextBatchFn = function()
			
				return self:getInactiveOriginalsInWindow( 
						volumeGuid, timeWindow, limit, skipNMostRecent, nextBatchOffset )
			
			end
		
			return assets, getNextBatchFn
		
		end )
	
end

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

--[[!

	\brief Retrieve the current list of purge candidates from the db, sorted
	by time of last access.

	\param limit (optional number) Limit the number of assets to search for.
	
	\return An array of purge candidates, along with a function for getting the next
	batch of limited data. Example:
	
		local fiveRecentlyAccessed, getNextBatchFunc = NmbMRUDatabase.getLeastRecentlyAccessed( 5 )
		...
		fiveRecentlyAccessed, getNextBatchFunc = getNextBatchFunc()
		...
		fiveRecentlyAccessed, getNextBatchFunc = getNextBatchFunc()
		...
		fiveRecentlyAccessed, getNextBatchFunc = getNextBatchFunc()
		...
		
		and so on.
	
--]]

function NmbMRUDatabase:getLeastRecentlyAccessed( limit, offset )

	check( 'NmbMRUDatabase', '?number', '?number' )

	return self._mruDatabaseImp:request( 'getLeastRecentlyAccessed', limit, offset )
	
		:map( function( leastRecentlyAccessed, nextBatchOffset )
		
			local getNextBatchFn = function()
			
				return self:getLeastRecentlyAccessed( limit, nextBatchOffset )
			
			end
		
			return leastRecentlyAccessed, getNextBatchFn
		
		end )
	
end

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

--[[!

	\brief Retrieve the N most recently accessed assets.

	\param limit (optional number) Limit the number of assets to search for.
	
	\return An array of purge candidates, along with a function for getting the next
	batch of limited data.
	
--]]

function NmbMRUDatabase:getMostRecentlyAccessed( limit, offset )

	check( 'NmbMRUDatabase', '?number', '?number' )
	
	return self._mruDatabaseImp:request( 'getMostRecentlyAccessed', limit, offset )

		:map( function( mostRecentlyAccessed, nextBatchOffset )
		
			local getNextBatchFn = function()
			
				return self:getMostRecentlyAccessed( limit, nextBatchOffset )
			
			end
		
			return mostRecentlyAccessed, getNextBatchFn
		
		end )

end


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

-- Deletion Backlog APIs

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

--[[!

	\brief Retrieve the N most recently accessed assets.

	\param limit (optional number) Limit the number of assets to search for.
	
	\return An array of purge candidates, along with a function for getting the next
	batch of limited data.
	
--]]

function NmbMRUDatabase:addToDeletionBacklog( volumeGuid, relativePath, localId, binaryType )

	check( 'NmbMRUDatabase', 'string', 'string', '?number', '?string' )
	
	return self._mruDatabaseImp:request( 'addToDeletionBacklog',
			volumeGuid, relativePath, localId, binaryType )
			:call( runUntilDone )
	
end

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

function NmbMRUDatabase:removeFromDeletionBacklog( volumeGuid, relativePath )

	check( 'NmbMRUDatabase', 'string', 'string' )
	
	return self._mruDatabaseImp:request( 'removeFromDeletionBacklog', 
			volumeGuid, relativePath )
			:call( runUntilDone )
	
end

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

function NmbMRUDatabase:bulkAddToDeletionBacklog( localAssetsToDelete )

	check( 'NmbMRUDatabase', 'table' )
	return self._mruDatabaseImp:request( 'bulkAddToDeletionBacklog', localAssetsToDelete )
			:call( runUntilDone )
	
end

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

function NmbMRUDatabase:bringDeletionBacklogUpToDate()

	check( 'NmbMRUDatabase' )
	return self._mruDatabaseImp:request( 'bringDeletionBacklogUpToDate' )
			:call( runUntilDone )
			
end

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

function NmbMRUDatabase:processDeletionBacklog()

	check( 'NmbMRUDatabase' )
	return self._mruDatabaseImp:request( 'processDeletionBacklog' )
			:call( runUntilDone )
			
end

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

--[[!

	\brief TEST USE ONLY - dump the contents of the deletionBacklogV2 table.

	\param dontBringUpToDate (optional boolean) If true, this will not update the DB before duping the contents.
	
	\return Contents of the deletionBacklogV2 table.
	
--]]

function NmbMRUDatabase:DEBUG_dumpDeletionBacklogContents( dontBringUpToDate )

	check( 'NmbMRUDatabase', '?boolean' )
	return self._mruDatabaseImp:request( 'DEBUG_dumpDeletionBacklogContents', dontBringUpToDate )

end

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

--[[!

	\brief TEST USE ONLY - dump the contents of the deletionBacklog table.

	\return Contents of the deletionBacklog table.
	
--]]

function NmbMRUDatabase:DEBUG_dumpDeletionBacklogContentsV1()

	check( 'NmbMRUDatabase' )
	return self._mruDatabaseImp:request( 'DEBUG_dumpDeletionBacklogContentsV1' )

end

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

--[[!

	\brief TEST USE ONLY
	
--]]

function NmbMRUDatabase:DEBUG_processedDeletionBacklogStream()

	check( 'NmbMRUDatabase' )
	return self._mruDatabaseImp:request( 'DEBUG_processedDeletionBacklogStream' )
	
end

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

--[[!

	\brief TEST USE ONLY
	
--]]

function NmbMRUDatabase:DEBUG_buildDeletionBacklogWhereClause( onlineVolumeGuids )

	check( 'NmbMRUDatabase', 'table' )
	return self._mruDatabaseImp:request( 'DEBUG_buildDeletionBacklogWhereClause', onlineVolumeGuids )

end

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

function NmbMRUDatabase:_DEBUG_simulateCorruption()

	return self._mruDatabaseImp:request( '_DEBUG_simulateCorruption' )

end

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

function NmbMRUDatabase:_DEBUG_numberOfLocalOriginals()

	return self._mruDatabaseImp:request( '_DEBUG_numberOfLocalOriginals' )

end

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

function NmbMRUDatabase:_DEBUG_sizeOfLocalOriginals()

	return self._mruDatabaseImp:request( '_DEBUG_sizeOfLocalOriginals' )

end

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

function NmbMRUDatabase:_DEBUG_numberOfLocalProxies()

	return self._mruDatabaseImp:request( '_DEBUG_numberOfLocalProxies' )

end

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

function NmbMRUDatabase:_DEBUG_sizeOfLocalProxies()

	return self._mruDatabaseImp:request( '_DEBUG_sizeOfLocalProxies' )

end

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

return Rx.Class 'NmbMRUDatabase'( NmbMRUDatabase )
