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

WFCatPhotoImportance.lua

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

ADOBE CONFIDENTIAL
------------------
Copyright 2016 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.utils
local KSTableUtils = require 'KSTableUtils'
local KSDate = require 'KSDate'
local WFLogger = require 'WFLogger'
local WFConfiguration = require 'WFConfiguration'

	-- WF.managed_catalog
local WFCatFileSpec = require 'WFCatFileSpec'
local WFCatIndexUtils = require 'WFCatIndexUtils'

local isTrashAssetDocument = assert( WFCatIndexUtils.isTrashAssetDocument )

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

local derefOpt = assert( KSTableUtils.derefOpt )
local KSDate_timeNow = assert( KSDate.timeNow, "KSDate.timeNow does not exist" )

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

local logger = WFLogger( 'WFCatPhotoImportance' )

local kImportantUpload = 'IMPORTANT'
local kCriticalUpload = 'CRITICAL'

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

local function minutesToDays( minutes )

	if minutes then
		return minutes * 60 * 24
	end

end


--============================================================================--
--[[!

	\class WFCatPhotoImportance
	
	\brief This namespace is used by file management when determing what to download
	and what to purge.
	
--]]

-- @mark WFCatPhotoImportance namespace

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

local WFCatPhotoImportance = {
	
	_ratingThreshold = 4, 
		-- purge photos rated below n stars

	_originalLastAccessedThreshold = 
		WFConfiguration.Nimbus.lastAccessedThreshold or
		minutesToDays( WFConfiguration.Nimbus.lastAccessThresholdInMinutes ) or
		15,
		-- proactively purge inactive originals that haven't been accessed in n days 
		
	_proxyLastAccessedThreshold = 
		WFConfiguration.Nimbus.lastAccessedThreshold or
		minutesToDays( WFConfiguration.Nimbus.lastAccessThresholdInMinutes ) or
		60,
		-- proactively purge inactive proxies that haven't been accessed in n days 
		
	_modifiedDateThreshold = 
		WFConfiguration.Nimbus.modifiedDateThreshold or
		minutesToDays( WFConfiguration.Nimbus.modifiedDateThresholdInMinutes ) or
		15,
		-- purge photos not modified within n days

	_importDateThreshold = 
		WFConfiguration.Nimbus.importDateThreshold or
		minutesToDays( WFConfiguration.Nimbus.importDateThresholdInMinutes ) or
		15,
		-- purge photos not imported within n days

}

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

local function dateIsWithinNDays( date, days )

	local daysToSeconds = function( days )
		return days * 24 * 60 * 60
	end

	local nDaysAgoInSeconds = KSDate_timeNow() - daysToSeconds( days )

	local ok, dateInSeconds = pcall( KSDate.iso8601ToTimeIntervalSinceReference, date )
	
	if not ok then 
		logger:warnf( "failed to parse date %s: %s", date, dateInSeconds )
		return false
	end
	
	return dateInSeconds >= nDaysAgoInSeconds
	
end

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

function WFCatPhotoImportance.isPhotoImportant( docValue )

	-- A Photo is considered important if ANY of the following conditions are met:

	-- 1. Photo is flagged as a "Pick".
	
	local flags = derefOpt( docValue, 'reviews' ) or {}
	local flaggedAsPick = false
	
	for _, flagT in pairs( flags ) do
	
		if derefOpt( flagT, 'flag' ) == 'pick' then
			flaggedAsPick = true
			break
		end
		
	end
	
	if flaggedAsPick then
		return true
	end
	
	-- 2. Photo is rated above n stars.
	
	local ratings = derefOpt( docValue, 'ratings' ) or {}
	local ratedAboveNStars = false
	
	for _, ratingT in pairs( ratings ) do
	
		if derefOpt( ratingT, 'rating' ) >= WFCatPhotoImportance._ratingThreshold then
			ratedAboveNStars = true
			break
		end
		
	end

	if ratedAboveNStars then
		return true
	end

	-- 3. Photo has been modified within n days.
	
	local modifiedDate = derefOpt( docValue, '_serverProvidedMetadata', 'updated' )
	local modifiedWithinNDays = modifiedDate and dateIsWithinNDays( modifiedDate, WFCatPhotoImportance._modifiedDateThreshold ) or false
	
	if modifiedWithinNDays then
		return true
	end
	
	-- 4. Photo was imported within n days.
	
	local importDate = derefOpt( docValue, 'importSource', 'importTimestamp' )
	local importedWithinNDays = importDate and dateIsWithinNDays( importDate, WFCatPhotoImportance._importDateThreshold ) or false
	
	if importedWithinNDays then
		return true
	end
	
	-- 5. Photo is marked for Offline Use
	local markedForOfflineUse = WFCatFileSpec.getMarkedForOfflineUseFromDocValue( docValue )
	
	if markedForOfflineUse then
		return true
	end

	return false

end

--------------------------------------------------------------------------------
	
function WFCatPhotoImportance.increaseUploadPriorityForAsset( wfCatalog, docId, isTimeCritical )

	return wfCatalog:docStore():applyOverlay {
			_id = docId,
			_localOnly = {
				universal = {
					uploadPriorityModifier = isTimeCritical and kCriticalUpload or kImportantUpload
				}
			}
		}

end

--------------------------------------------------------------------------------
	
function WFCatPhotoImportance.removeUploadPriorityModifier( wfCatalog, docId )

	return wfCatalog:docStore():applyOverlay {
			_id = docId,
			_localOnly = {
				universal = {
					uploadPriorityModifier = { _delete = true }
				}
			}
		}

end

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

function WFCatPhotoImportance.getUploadPriorityModifier( docValue )

	return derefOpt( docValue, '_localOnly', 'universal', 'uploadPriorityModifier' )

end

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

function WFCatPhotoImportance.getPriorityForUpload( docValue )

	-- Normal asset range is -1 for rejected photo to 2 for picked photo with 5 stars

	-- If asset in trash we subtract 4 to make sure trash asset priority falls
	-- below normal assets. (a picked 5 star asset in trash will be -2,
	-- and a rejected 0 star asset will be -5 ).
	
	-- If asset is in a space rather than a catalog we subtract 10 to make sure all space 
	-- assets are uploaded after catalog assets.  (A picked 5 star asset in a space 
	-- will be -8 and a rejected 0 star asset will be -11.) This a) ensures that the 
	-- asset is backed up for the user ASAP and allows us to use the Oz copy APIs as
	-- much as possible in order to add the asset to the space, thereby saving
	-- user bandwidth. This does mean though that rejected assets in a user's catalog
	-- get uploaded before contributed assets with stars which isn't ideal. An improvement
	-- might be to mark contributed assets (the original) and give them the highest priority
	-- so that the copy that is added to the space can have a reasonable priority and still
	-- be ensured that the original is present.

	local priority = 0

	-- Add priority if the photo is flagged as a "Pick".
	-- Remove priority if the photo is a "Reject"
	
	local flags = derefOpt( docValue, 'reviews' ) or {}
	
	for _, flagT in pairs( flags ) do
	
		local flag = derefOpt( flagT, 'flag' ) 
		
		if flag == 'pick' then
			priority = priority + 1
			break
		elseif flag == 'reject' then
			priority = priority - 1
			break
		end
		
	end
	
	-- Add in priority based on user-rating.
	
	local ratings = derefOpt( docValue, 'ratings' ) or {}

	for _, ratingT in pairs( ratings ) do
	
		local rating = derefOpt( ratingT, 'rating' ) 
		
		if rating and rating >= 0 then
			priority = priority + ( rating / 5 )
			break
		end
		
	end

	if isTrashAssetDocument( docValue ) then
		priority = priority - 4
	end
	
	if docValue._spaceId then
		priority = priority - 10
	end
	
	-- Adjust the priority based on any client-provided importance.
	
	local uploadPriorityModifier = WFCatPhotoImportance.getUploadPriorityModifier( docValue )
	
	if uploadPriorityModifier == kImportantUpload then
	
		priority = 100
		
	elseif uploadPriorityModifier == kCriticalUpload then
	
		priority = 1000
		
	end

	return priority

end

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

return WFCatPhotoImportance
