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

ADOBE CONFIDENTIAL
------------------
Copyright 2016 Adobe Systems Incorporated
All Rights Reserved.

NOTICE: All information contained herein is, and remains 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 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 Systems Incorporated.

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

local Rx = require 'Rx'

local KSTableUtils = require "KSTableUtils"
local WFPlatformInfo = require 'WFPlatformInfo'
local WFCapabilities = require 'WFCapabilities'

local LrmAssetUtils = require 'LrmAssetUtils'
local WFCatAssetUtils = require 'WFCatAssetUtils'

local LrmCoreInfoIndexDefinitions = require "LrmCoreInfoIndexDefinitions"

local LrmOzRenditionServerUtils
if WFPlatformInfo.isiOS then
	LrmOzRenditionServerUtils = require "LriOzRenditionServerUtils"
else
	LrmOzRenditionServerUtils = require "LrmOzRenditionServerUtils"
end

local WFRxIndexStoreSchema = require 'WFRxIndexStoreSchema'

local WFFileUtils = require 'WFFileUtils'

local OzFilePaths = require 'OzFilePaths'

local WFCatAssetAux = require 'WFCatAssetAux'

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

local derefOpt = assert( KSTableUtils.derefOpt )

local assetIsDisplayable = assert( LrmCoreInfoIndexDefinitions.assetIsDisplayable )

local enableVideoStoreLocally = WFCapabilities.getCapability( "WFCatVideo.enableVideoStoreLocally" ) == 'true'

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

local LrmCatalogIndexDefinitions = {}

--------------------------------------------------------------------------------
-- @mark assetProfileLinks
--------------------------------------------------------------------------------

local kAssetProfileLinks = {

	sql = [[CREATE TABLE assetProfileLinks( subtype, fileName, masterUrl, uniqueId )]],

	updateLazily = WFPlatformInfo.isAndroid and false or nil, -- don't wait for the table in Android to be used. Do indexing ASAP

	relevantTypeSubtypeTuples = {
		{ 'asset', 'image' },
		{ 'asset', 'video' },
	},

	map = function( docValue, emit )

		if docValue._deleted then
			return
		end
		if not assetIsDisplayable( docValue ) then
			return
		end

		local cameraProfileInfo = LrmAssetUtils.getCameraProfileInfo( docValue)
		if cameraProfileInfo then
			emit( "camera", cameraProfileInfo.filename, cameraProfileInfo.href, cameraProfileInfo.uniqueId )
		end

		local lensProfileInfo = LrmAssetUtils.getLensProfileInfo( docValue )
		if lensProfileInfo then
			emit( "lens", lensProfileInfo.filename, lensProfileInfo.href, lensProfileInfo.uniqueId )
		end
	end,
}

local kAssetProfileLinks_uniqueId = [[CREATE INDEX assetProfilesLink_uniqueId ON assetProfileLinks( uniqueId )]]

--------------------------------------------------------------------------------
-- @mark profileRegistration
--------------------------------------------------------------------------------

local kProfileRegistration = {

	sql = [[CREATE TABLE profileRegistration( subtype, fileName, uniqueId )]],

	updateLazily = WFPlatformInfo.isAndroid and false or nil, -- don't wait for the table in Android to be used. Do indexing ASAP

	relevantTypeSubtypeTuples = {
		{ 'asset', 'camera_profile' },
		{ 'profile', 'NULL' },
	},

	hasOneRowPerDocument = true,

	map = function( docValue, emit )

		if (docValue.type == 'asset' and docValue.subtype == 'camera_profile') or docValue.type == 'profile'
			and not docValue._deleted then

			local fileName
			local uniqueId
			local subtype

			if docValue.type == 'asset' and not WFPlatformInfo.isAndroid then
				-- Custom DCPs

				subtype = 'camera'
				fileName = docValue.fileName
				uniqueId = derefOpt( docValue, '_serverProvidedMetadata', 'links', '/rels/master', 'href' )

			else
				-- Adobe standard camera matching profiles

				if WFPlatformInfo.isAndroid and docValue.type ~= 'profile' then
					return
				end

				local localOnly = docValue._localOnly

				subtype = localOnly.subtype
				fileName = localOnly.fileName
				uniqueId = localOnly.uniqueId

			end

			if uniqueId then
				emit( subtype, fileName, uniqueId )
			end
		end
	end,
}

local kProfileRegistration_uniqueId = [[CREATE INDEX profileRegistration_uniqueId ON profileRegistration( uniqueId )]]
local kProfileRegistration_fileName = [[CREATE INDEX profileRegistration_fileName ON profileRegistration( fileName COLLATE NOCASE )]]

--------------------------------------------------------------------------------
-- @mark TEMP_searchFilterCount
--------------------------------------------------------------------------------

local kTEMP_searchFilterCount =  {

	sql = [[CREATE TEMPORARY TABLE TEMP_searchFilterCount( filterType, filterCount )]],

	isManualTable = true

}

--------------------------------------------------------------------------------
-- @mark renditionRevisions
--------------------------------------------------------------------------------

local function emitExternalDevelopXmpRendition( assetId, docValue, emit )

	local timestamp = docValue
		and docValue.importSource
		and docValue.importSource.importTimestamp

	local hasExternalXmpDevelop = LrmAssetUtils.hasDownloadableExternalXmpDevelop( docValue )
	local hasExternalXmpDevelopLocally =  LrmAssetUtils.hasExternalXmpDevelopLocally( docValue )

	-- Android: xmpCameraRawFiles might be in the docValue but is missing (user accessible storage)
	local externalXmpDevelopMissing
	if WFPlatformInfo.isAndroid and not WFPlatformInfo.isUnitTest() then
		externalXmpDevelopMissing = hasExternalXmpDevelop and not ( hasExternalXmpDevelopLocally
			and WFFileUtils.exists( OzFilePaths.getAbsoluteXmpDevelopPath( docValue ) ) )
	else
		externalXmpDevelopMissing = hasExternalXmpDevelop and not hasExternalXmpDevelopLocally
	end

	if externalXmpDevelopMissing then

		local invalid = derefOpt( docValue, '_serverProvidedMetadata', 'links', '/rels/xmp/develop', 'invalid' )

		if not invalid then

			local fileSize = derefOpt( docValue, '_serverProvidedMetadata', 'links', '/rels/xmp/develop', 'fileSize' )
				or 16384

			emit( assetId, 'xmp_develop', timestamp, fileSize )
		end
	end

end

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

local function emitAuxRendition( assetId, docValue, emit )

	local timestamp = docValue
			and docValue.importSource
			and docValue.importSource.importTimestamp

	local imageTypes = { 'original', 'proxy-2560' }

	for _, imageType in ipairs( imageTypes ) do

		local renditionName = 'aux-' .. imageType
		local auxMissingForOriginalFromDevice = WFCatAssetAux.getListOfAuxThatAreNotLocal( docValue, imageType ) or {}

		for _, auxInfo in ipairs( auxMissingForOriginalFromDevice ) do

			local ozHasExternalXmpDevelopLink
					= not not LrmAssetUtils.hasDownloadableExternalXmpDevelop( docValue )

			if docValue.type == 'version' then

				-- Version Document do not contain links to auxiliary binaries. However,
				-- because there is strict upload ordering -- auxiliaries are always
				-- uploaded _before_ develop settings -- we know that if there is a link
				-- to external develop settings then there is also a link to the auxiliary
				-- objects referred to by the develop settings.
				
				local hasAuxLink = ozHasExternalXmpDevelopLink

				local fileSize = 0
					-- Size of auxiliaries is not tracked in Version documents. We may need
					-- to account for this in code that uses the fileSize column.
			
				if hasAuxLink then

					emit( assetId, renditionName, timestamp, fileSize )

				end
				
			else

				local hasAuxLink = not not WFCatAssetAux.getDownloadLink( docValue, auxInfo.digest )

				local canDownloadAux = ozHasExternalXmpDevelopLink and hasAuxLink
					-- If there is no develop settings link on Oz, but there are auxiliary
					-- links, then the links section is wrong due to a since fixed Oz-bug:
					-- https://jira.corp.adobe.com/browse/OZ-10914. Trying to access these
					-- auxiliary links will result in InsufficientPermissionError issues, and
					-- so we only emit aux entries when the develop settings link exists as well.
					-- This is safe to do, because of our strict upload ordering. There's no
					-- need to download auxiliaries if you cannot download develop settings.
					--
					-- NOTE: If ever there exists auxiliaries that are not related to the
					-- develop settings, we may need to reevaluate this approach.

				if canDownloadAux then
				
					local fileSize

					if auxInfo.resample then
						fileSize = auxInfo.estimatedFileSize
					else
						fileSize = WFCatAssetAux.getFileSize( docValue, auxInfo.digest )
					end

					emit( assetId, renditionName, timestamp, fileSize )
					
				end
			
			end
		
		end

	end

end

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

local function emitVersionRenditions( docValue, emit )

	local assetId = derefOpt( docValue, 'asset', 'id' )

	emitExternalDevelopXmpRendition( assetId, docValue, emit )

	emitAuxRendition( assetId, docValue, emit )

end

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

local kRenditionRevisionsDefinition = {

	sql = [[CREATE TABLE renditionRevisions( assetId, renditionName, timestamp, size )]],

	relevantTypeSubtypeTuples = {
		{ 'asset', 'image' },
		{ 'asset', 'video' },
		{ 'version', 'edit' },
	},

	map = function( docValue, emit )

		if docValue._deleted then
			return
		end

		if docValue.type == 'version' and docValue.subtype == 'edit' then
			emitVersionRenditions( docValue, emit )
			return
		end

		if not assetIsDisplayable( docValue ) then return end

		local timestamp = docValue
			and docValue.importSource
			and docValue.importSource.importTimestamp

		if LrmOzRenditionServerUtils.shouldDownloadOzRendition( docValue, "thumbnail2x" ) then

			local fileSize = derefOpt( docValue, '_serverProvidedMetadata', 'links', '/rels/rendition_type/thumbnail2x', 'fileSize' )
				or 48000

			emit( docValue._id, 'thumbnail2x', timestamp, fileSize )
		end

		if LrmOzRenditionServerUtils.shouldDownloadOzRendition( docValue, "preview" ) then

			local fileSize = derefOpt( docValue, '_serverProvidedMetadata', 'links', '/rels/rendition_type/2048', 'fileSize' )
				or 461824

			emit( docValue._id, 'preview', timestamp, fileSize )
		end

		local isVideo = LrmAssetUtils.isVideo( docValue )

		local hasProxyOnOz = LrmAssetUtils.hasProxyOnOz( docValue )
		local hasProxyLocally = LrmAssetUtils.hasProxyLocally( docValue, isVideo and '1080p' or nil )
		local hasMasterOnOz = WFPlatformInfo.isiOS and LrmAssetUtils.hasDownloadableMasterOnOz( docValue ) or LrmAssetUtils.hasMasterOnOz( docValue )
		local hasMasterLocally = LrmAssetUtils.hasMasterLocally( docValue )

		if not isVideo or enableVideoStoreLocally or WFPlatformInfo.isUnitTest() then
		
			local originalFileSize = derefOpt( docValue, 'importSource', 'fileSize' )
			if not originalFileSize or originalFileSize <= 0 then
				originalFileSize = nil
			end
	
			if hasMasterOnOz and not hasMasterLocally then
	
				local fileSize = originalFileSize or 20971520
	
				emit( docValue._id, 'master', timestamp, fileSize )
	
			end

			if hasProxyOnOz and not hasProxyLocally then
	
				local fileSize
				
				if isVideo then
					fileSize =  derefOpt( docValue, '_serverProvidedMetadata', 'links', '/rels/proxy_type/1080p', 'fileSize' )
								or WFCatAssetUtils.estimateProxySize( docValue )
				else
					fileSize = derefOpt( docValue, '_serverProvidedMetadata', 'links', '/rels/proxy_type/2560', 'fileSize' )
								or derefOpt( docValue, '_localOnly', 'proxy', 'fileSize' )
								or WFCatAssetUtils.estimateProxySize( docValue )
				end
				
				emit( docValue._id, 'proxy', timestamp, fileSize )
	
				if not hasMasterOnOz and not hasMasterLocally and not docValue._spaceId then
					emit( docValue._id, 'proxyWithoutMaster', timestamp, fileSize )
				end
	
			end
		
			if hasMasterOnOz and not hasProxyOnOz and isVideo then
			
				-- This is for store locally cache size estimation 
				-- if video proxy hasn't been generated by Oz yet
				
				local fileSize = WFCatAssetUtils.estimateProxySize( docValue )
				
				emit( docValue._id, 'proxyPreGenerated', timestamp, fileSize )
	
			end
	
			if not isVideo then
	
				emitExternalDevelopXmpRendition( docValue._id, docValue, emit )
	
				emitAuxRendition( docValue._id, docValue, emit )
	
			end
			
		end

	end
}

local kRenditionRevisions_assetId = [[CREATE INDEX renditionRevisions_assetId ON renditionRevisions( assetId )]]



function LrmCatalogIndexDefinitions.tableDefinitions( tableDefinitions )

	tableDefinitions.renditionRevisions = kRenditionRevisionsDefinition
	tableDefinitions.assetProfileLinks = kAssetProfileLinks
	tableDefinitions.profileRegistration = kProfileRegistration
	tableDefinitions.TEMP_searchFilterCount = kTEMP_searchFilterCount

	tableDefinitions.new_asset_activity = nil
	tableDefinitions.tags = nil
	tableDefinitions.favorite_activity = nil
	tableDefinitions.selectedPhotos = nil
	tableDefinitions.space_user = nil

	return tableDefinitions
end

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

function LrmCatalogIndexDefinitions.indexDefinitions( indexDefinitions )

	indexDefinitions.profileRegistration_uniqueId = kProfileRegistration_uniqueId
	indexDefinitions.profileRegistration_fileName = kProfileRegistration_fileName
	indexDefinitions.assetProfileLinks_uniqueId = kAssetProfileLinks_uniqueId
	indexDefinitions.renditionRevisions_assetId = kRenditionRevisions_assetId

	return indexDefinitions

end

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

local tableDefinitions = {}
local indexDefinitions = {}

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

local LrmIndexStoreSchema = {

	WFRxIndexStoreSchema,
		--superclass
}

function LrmIndexStoreSchema:addTable( args )

	if args.updateLazily ~= true and not args.isManualTable then
		args.updateLazily = false
	end

	self:WFRxIndexStoreSchema_addTable( args )

end


local LrmIndexStoreSchemaClass = Rx.Class( 'WFRxIndexStoreSchema', LrmIndexStoreSchema )

local indexStoreSchema = LrmIndexStoreSchemaClass( )

indexStoreSchema:addDatabaseInitRoutine( require 'WFSQLiteSetLogicImp'.registerSetModule )

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

LrmCatalogIndexDefinitions.tableDefinitions( tableDefinitions )
LrmCatalogIndexDefinitions.indexDefinitions( indexDefinitions )

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

for k, definition in pairs( tableDefinitions ) do
	indexStoreSchema:addTable( definition )
end

for k, definition in pairs( indexDefinitions ) do
	indexStoreSchema:addIndex( definition )
end

local function getIndexSchemaGenerators( )
	local indexDefinitions = {
		'LrmCoreInfoIndexDefinitions',
		'LrmAlbumsIndexDefinitions',
		'LrmCacheIndexDefinitions',
		'LrFavoritesIndexDefinitions',
		'LrCommentsIndexDefinitions',
		'LrmKeywordsIndexDefinitions',
		'LrmPresetsProfilesIndexDefinitions',
		'LrmPeopleIndexDefinitions',
		'LrmTrashIndexDefinitions',
		'LrWatermarkIndexDefinitions',
		'WFCatSimpleDocStoreUpdater',
	}

	return indexDefinitions
end

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

function LrmCatalogIndexDefinitions.generateIndexSchema( indexStoreSchema )

	local indexSchemaGenerators = getIndexSchemaGenerators()

	for k, generator in pairs( indexSchemaGenerators ) do
		require ( generator ).generateIndexSchema( indexStoreSchema )
	end

end

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

LrmCatalogIndexDefinitions.generateIndexSchema ( indexStoreSchema )

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

require 'WFCatConflictResolver'.generateIndexSchema( indexStoreSchema )
require 'WFCatErrorHandler'.generateIndexSchema( indexStoreSchema )
require 'WFCatStorage'.generateIndexSchema( indexStoreSchema )
require 'WFCatCollaboration'.generateIndexSchema( indexStoreSchema )
require 'WFCatCollaborator'.generateIndexSchema( indexStoreSchema )
require 'WFCatBinaryUploader'.generateIndexSchema( indexStoreSchema )
require 'WFCatImportDuplicateFiles'.generateIndexSchema( indexStoreSchema )
require 'WFCatVersions'.generateIndexSchema( indexStoreSchema )
require 'WFCatVolumeAgentQueryHelper'.generateIndexSchema( indexStoreSchema )
require 'WFCatSyncStatus'.generateIndexSchema_mobile( indexStoreSchema )

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

LrmCatalogIndexDefinitions.indexStoreSchema = indexStoreSchema

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

return LrmCatalogIndexDefinitions
