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

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

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

	-- wf.core
local WFCloseSupport = require "WFCloseSupport"
local KSPathUtils = require 'KSPathUtils'
local WFFileUtils = require 'WFFileUtils'
local WFCrypto = require 'WFCrypto'

	-- WF.core.lua.threading
local WFRequestDispatcher = require 'WFRequestDispatcher'

	-- WF.lightroom
local NmbFileAccessDatabase = require 'NmbFileAccessDatabase'
local NmbDeletionBacklog = require 'NmbDeletionBacklog'
local NmbPurgeDbUpdater = require 'NmbPurgeDbUpdater'

	-- WF.oz_client.managed_catalog
local WFRxDocStore = require 'WFRxDocStore'
local WFRxIndexStore = require 'WFRxIndexStore'

	-- WF.web.json
local KSJsonDecoder = require 'KSJsonDecoder'

	-- Rx
local Rx = require "Rx"

	-- WF.sqlite.database
local WFSQLiteDatabase = require 'WFSQLiteDatabase'
local WFSQLiteDatabaseSchema = require 'WFSQLiteDatabaseSchema'

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

local md5 = assert( WFCrypto.MD5, "Cannot find MD5 digest" )

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

local NmbMRUDatabaseMessageProcessorImp = {
	
	_docStore = false,
		-- (WFRxDocStore)
		
	_indexStore = false,
		-- (WFRxIndexStore)
		
	_mruSQLiteDatabase = false,
		-- (WFSQLiteDatabase)
		
	_mruSQLiteDatabasePath = false,
		-- (NmbFileAccessDatabase)
		
	_fileAccessDb = false,
		-- (NmbFileAccessDatabase)
		
	_deletionBacklog = false,
		-- (NmbDeletionBacklog)
		
	WFCloseSupport.addCloseSupportMethods,

}

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

local function buildMRUDatabaseSchema()

	local schema = WFSQLiteDatabaseSchema( '1.0' )

	-- Create a table to track the update status and schema of the tables.

	schema:addTable
			[[CREATE TABLE tableStatus( 
				tableName UNIQUE,
				needsUpdateFrom,
				sqlSchemaHash )]]
		:onConflict( 'replace' )

	-- Create the main table to track last access times

	local lastAccessTimestampsTableSqlSchema =
			[[CREATE TABLE lastAccessTimestamps( 
					localId PRIMARY KEY,
					accessTimestamp NOT NULL )]]

	schema:addTable( lastAccessTimestampsTableSqlSchema )
		:onConflict 'replace'
		
	-- Create the table to track asset info used for purging.

	local assetInfoTableSqlSchema =
			[[CREATE TABLE assetInfo( 
					localId PRIMARY KEY,
					docId,
					volumeGuidUsedByProxy,
					ozHasProxy,
					proxyFileSize,
					volumeGuidUsedByOriginal,
					ozHasOriginal,
					originalFileSize,
					xmpSidecarSize,
					isImportant,
					isMarkedForOfflineUse,
					sufficientRenditionFoundOnOz )]]

	schema:addTable( assetInfoTableSqlSchema )
		:onConflict 'replace'
		
	-- Add some indexes
		
	schema:addIndex
			[[CREATE INDEX assetInfo_proxySearchIndex
			ON assetInfo(
					isImportant,
					volumeGuidUsedByProxy, 
					ozHasProxy )]]
	
	schema:addIndex
			[[CREATE INDEX assetInfo_originalSearchIndex
			ON assetInfo(
					isImportant,
					volumeGuidUsedByOriginal, 
					ozHasOriginal )]]
	
	schema:addIndex
			[[CREATE INDEX lastAccessTimestamps_accessTimestamp
			ON lastAccessTimestamps( accessTimestamp )]]
	
	schema:addTable [[CREATE TEMPORARY TABLE TEMP_ids( docId PRIMARY KEY )]]
	
	-- Deletion Backlog
	
	schema:addTable
			[[CREATE TABLE deletionBacklog( volumeGuid, relativePath )]]

	schema:addTable
			[[CREATE TABLE deletionBacklogV2( localId, binaryType, volumeGuid, relativePath )]]
	
	return schema, lastAccessTimestampsTableSqlSchema

end

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

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

	optArgs = optArgs or {}

	local docStore = WFRxDocStore( docStorePath )

	local indexStore = WFRxIndexStore( docStore, indexSpecScript, optArgs.indexStoreReadThreads )

	if fileAccessDbPath ~= ':memory:' then
		local parentDir = KSPathUtils.parent( fileAccessDbPath )
		WFFileUtils.mkdir( parentDir )
	end

	local dbUpdaterVersion = 2
		-- SPAM [jandrew 2017-10-25]: 
		-- If NmbPurgeDbUpdater changes, we may need to increment dbUpdaterVersion so
		-- that the MRU table is rebuilt.

	local schema, lastAccessTimestampsTableSqlSchema = buildMRUDatabaseSchema()

	local fileAccessSQLiteSchemaHash = md5.digest( string.format( '%s%d', lastAccessTimestampsTableSqlSchema, dbUpdaterVersion ) )

	self._mruSQLiteDatabasePath = fileAccessDbPath

	local mruSQLiteDatabase = WFSQLiteDatabase( fileAccessDbPath, schema )

	local deletionBacklog = NmbDeletionBacklog( docStore, mruSQLiteDatabase, optArgs )

	local fileAccessDb = NmbFileAccessDatabase( docStore, mruSQLiteDatabase, fileAccessSQLiteSchemaHash, optArgs )

	NmbPurgeDbUpdater.trackAssetUpdates( docStore, fileAccessDb, deletionBacklog, optArgs.writeInterval )

	self:setOwned {
		_docStore = docStore,
		_indexStore = indexStore,
		_fileAccessDb = fileAccessDb,
		_deletionBacklog = deletionBacklog,
		_mruSQLiteDatabase = mruSQLiteDatabase
	}
	
end

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

function NmbMRUDatabaseMessageProcessorImp:haltAndDelete()

	local path = self._mruSQLiteDatabasePath

	self:close() -- should cause DB object and friends to close

	if path ~= ':memory:' then

		WFFileUtils.deleteFile( path )

		WFFileUtils.deleteFile( path .. '-shm' )

		WFFileUtils.deleteFile( path .. '-wal' )

	end

	return Rx.Observable.empty()

end

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

local NmbMRUDatabaseMessageProcessor = 
		Rx.Class( 'NmbMRUDatabaseMessageProcessor', 
					NmbMRUDatabaseMessageProcessorImp )

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

local function initMessageProcessor( encodedPackedThreadArgs )

	local packedThreadArgs = KSJsonDecoder.decodeWebSafeJson(
								encodedPackedThreadArgs )

	local mruDbMessageProcessor = NmbMRUDatabaseMessageProcessor( unpack( packedThreadArgs ) )

	WFCloseSupport.mustCloseAtThreadTermination( mruDbMessageProcessor )

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

	local fileAccessDb = mruDbMessageProcessor._fileAccessDb
	
	local fileAccessDbMethods = {
	
		'getContinueFromChangeCounter',
		'touch',
		'touchMultipleIfNew',
		'updateChangeCounter',
		'updateAssetInfo',
		'delete',
		'waitUntilUpToDate',
		'getLeastRecentlyAccessedInWindow',
		'getLeastRecentlyAccessedProxiesInWindow',
		'getLeastRecentlyAccessedOriginalsInWindow',
		'getInactiveAssetsInWindow',
		'getInactiveProxiesInWindow',
		'getInactiveOriginalsInWindow',
		'getLeastRecentlyAccessed',
		'getMostRecentlyAccessed',
		'DEBUG_dumpLastAccessContents',
		'_DEBUG_numberOfLocalOriginals',
		'_DEBUG_sizeOfLocalOriginals',
		'_DEBUG_numberOfLocalProxies',
		'_DEBUG_sizeOfLocalProxies',
		'_DEBUG_simulateCorruption',
	}

	for _, fn in ipairs( fileAccessDbMethods ) do
	
		WFRequestDispatcher.onMessageDo {
			message = fn,
			target = fileAccessDb,
		}
	
	end

	----------------------------------------------------------------------------
	
	local deletionBacklog = mruDbMessageProcessor._deletionBacklog
	
	local deletionBacklogMethods = {
			
		'addToDeletionBacklog',
		'removeFromDeletionBacklog',
		'bulkAddToDeletionBacklog',
		'bringDeletionBacklogUpToDate',
		'processDeletionBacklog',
		'DEBUG_processedDeletionBacklogStream',
		'DEBUG_dumpDeletionBacklogContents',
		'DEBUG_dumpDeletionBacklogContentsV1',
		'DEBUG_buildDeletionBacklogWhereClause'
	
	}
	
	for _, fn in ipairs( deletionBacklogMethods ) do
	
		WFRequestDispatcher.onMessageDo {
			message = fn,
			target = deletionBacklog,
		}
	
	end
	
	----------------------------------------------------------------------------

	WFRequestDispatcher.onMessageDo {

		message = 'haltAndDelete',

		target = mruDbMessageProcessor,

	}

	WFRequestDispatcher.enableDiagnosticLogForRequestStats()

end

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

return {
	initMessageProcessor = initMessageProcessor,
}
