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

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

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

local KSPrefs = require 'KSPrefs'

local WFArguments = require 'WFArguments'

local WFConfiguration = require 'WFConfiguration'

local WFFileUtils = require 'WFFileUtils'

local WFTableArchiving = require "WFTableArchiving"

	-- libs.json
local KSJsonDecoder = require 'KSJsonDecoder'
local KSJsonEncoder = require 'KSJsonEncoder'

--============================================================================--
--[[!
	\class NmbFileManagementSettings
	\brief This namespace provides access to the settings that control disk space management.
--]]

-- @mark NmbFileManagementSettings namespace

local DEFAULT_TARGET_DISK_SPACE_USAGE = 0.25
local DEFAULT_KEEP_ALL_ORIGINALS = false
local DEFAULT_KEEP_ALL_PROXIES = false

local NmbFileManagementSettings = {}

local _NmbFileManagementSettings = {

	_targetNimbusDiskSpacePercentage = WFConfiguration.Nimbus.targetNimbusDiskSpacePercentage or DEFAULT_TARGET_DISK_SPACE_USAGE,
		--[[ 
		
			The percentage of disk space that Nimbus should try not to exceed. 
			
			Please note that Nimbus can exceed the disk space usage target if the user has a 
			lot of smart previews, or if they explicitly choose to mark a lot of images for
			offline use; the purge system will not remove smart previews or offline originals
			to try to stay within the target disk space usage window.

			For Example:
			
			If the hard drive is aleady 50% full, we'll use up to N% of the remaining space. If N
			is equal to 40%, and the HD is 100GB is size, Nimbus will try to use less than 20GB.

		]]
		
	_minimumFreeSpace = ( WFConfiguration.Nimbus.minimumFreeSpace or 2048 ) * ( 1024 * 1024 ),
		--[[ 
		
			Never let Nimbus use disk space when there is less than `N` amount left.
			If Nimbus exceeds this amount, we're in a no-disk-space state.

		]]
		
	_maxPollingInterval = 30,
		--[[
		
			Recalculate disk space needs, at most, every n seconds.
		
		]]

	_keepAllOriginals = DEFAULT_KEEP_ALL_ORIGINALS,
		-- Whether or not Nimbus should store all originals locally.
		
	_keepAllProxies = DEFAULT_KEEP_ALL_PROXIES,
		-- Whether or not Nimbus should store all proxies locally.
		
	_relativeOriginalStorageLocation = false,
		-- Custom location where originals are stored, relative to the volume identfier.
		-- If false, there's no custom location defined.
		
	_originalStorageVolumeGuid = false,
		-- The GUID of the volume where the originals are stored.
		-- If false, there's no custom location defined.
		
	_relativeProxyStorageLocation = false,
		-- Custom location where proxies are stored, relative to the volume identfier.
		-- If false, there's no custom location defined.
		
	_proxyStorageVolumeGuid = false,
		-- The GUID of the volume where the originals are stored.
		-- If false, there's no custom location defined.
		
	_volumeReadableIdentifiers = false,
		-- (table) Key: Volume GUID Value: Readable Identifier
		-- The UI volume identifier strings, such as the user-defined volumes' names.
		-- This is used in the UI to identify the drive to the user.
		
	_lastUpdateTime = os.time(),
		-- time (in UNIX epoch) of the last update
}

-- getter/setter metatable pattern to monitor changes the the table
-- only elements whose names begin with an "_" are monitored
local function get(t, key)

	if key:sub(1,1) == "_" then
		-- print("NmbFileManagementSettings.get: ", key, _NmbFileManagementSettings[key])
		return _NmbFileManagementSettings[key]
	else
		return rawget(t, key)
	end

end

local function set(t, key, value)

	if key:sub(1,1) == "_" then
		-- print("NmbFileManagementSettings.set: ", key, value)
		_NmbFileManagementSettings[key] = value
	else
		rawset(t, key, value)
	end

	if key:sub(1,1) == "_" and type(NmbFileManagementSettings.saveSecondaryPrefs) == "function" then
		_NmbFileManagementSettings["_lastUpdateTime"] = os.time()
		KSPrefs.NmbFileManagementSettings_lastUpdateTime = NmbFileManagementSettings._lastUpdateTime
		pcall( NmbFileManagementSettings.saveSecondaryPrefs )
	end

end

setmetatable(NmbFileManagementSettings, {__index = get, __newindex = set} )


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

do
	
	if not WFConfiguration.Nimbus.targetNimbusDiskSpacePercentage then
	
		local mpref = KSPrefs.NmbFileManagementSettings_targetNimbusDiskSpacePercentage
		
		if mpref and type( mpref ) == 'number' and mpref >= 0 then
		
			NmbFileManagementSettings._targetNimbusDiskSpacePercentage = mpref
			
		end

	end

	if not WFConfiguration.Nimbus.keepAllOriginals then

		local mpref = KSPrefs.NmbFileManagementSettings_KeepAllOriginals

		if mpref then
			
			NmbFileManagementSettings._keepAllOriginals = mpref
			
		end

	end

	if not WFConfiguration.Nimbus.keepAllProxies then

		local mpref = KSPrefs.NmbFileManagementSettings_KeepAllProxies

		if mpref then
			
			NmbFileManagementSettings._keepAllProxies = mpref
			
		end

	end

	local mpref = KSPrefs.NmbFileManagementSettings_relativeOriginalStorageLocation

	if mpref and type( mpref ) == 'string' then
		
		NmbFileManagementSettings._relativeOriginalStorageLocation = mpref
		
	end

	mpref = KSPrefs.NmbFileManagementSettings_relativeProxyStorageLocation

	if mpref and type( mpref ) == 'string' then
		
		NmbFileManagementSettings._relativeProxyStorageLocation = mpref
		
	end
	
	mpref = KSPrefs.NmbFileManagementSettings_originalStorageVolumeGuid

	if mpref and type( mpref ) == 'string' then
		
		NmbFileManagementSettings._originalStorageVolumeGuid = mpref
		
	end
	
	mpref = KSPrefs.NmbFileManagementSettings_proxyStorageVolumeGuid

	if mpref and type( mpref ) == 'string' then
		
		NmbFileManagementSettings._proxyStorageVolumeGuid = mpref
		
	end

	mpref = KSPrefs.NmbFileManagementSettings_volumeReadableIdentifiers

	if mpref and type( mpref ) == 'string' then
		
		NmbFileManagementSettings._volumeReadableIdentifiers = KSJsonDecoder.decodeWebSafeJson( mpref )
		
	else
	
		NmbFileManagementSettings._volumeReadableIdentifiers = {}
	
	end

	mpref = KSPrefs.NmbFileManagementSettings_lastUpdateTime

	if mpref and type( mpref ) == 'string' then
		
		NmbFileManagementSettings._lastUpdateTime = mpref
		
	else
	
		NmbFileManagementSettings._lastUpdateTime = 0
	
	end

end

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

function NmbFileManagementSettings.setSecondaryPrefsLocation( secondaryPrefsLocation)

	NmbFileManagementSettings.secondaryPrefsLocation = secondaryPrefsLocation

	if not WFFileUtils.exists( secondaryPrefsLocation ) then
		-- This causes us to create the file even if the prefs are just read
		NmbFileManagementSettings._lastUpdateTime = os.time()
	end

end

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

--[[!
    \brief Save all file management settings to the backup location which allows
	regular LrCC and MAS LrCC to interoperate
]]--

function NmbFileManagementSettings.saveSecondaryPrefs()

	-- Any caller who changes a property must provide the location to write the prefs file to
	assert( NmbFileManagementSettings.secondaryPrefsLocation )

	if NmbFileManagementSettings.secondaryPrefsLocation then

		_NmbFileManagementSettings._lastUpdateTime = os.time()

		local prefsString = WFTableArchiving.tableToString( _NmbFileManagementSettings or {} )

		-- print("SAVE: ", NmbFileManagementSettings.secondaryPrefsLocation, prefsString)

		WFFileUtils.saveToFile( NmbFileManagementSettings.secondaryPrefsLocation, prefsString )

	end

end

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

--[[!
    \brief Load all file management settings from the backup location which allows
	regular LrCC and MAS LrCC to interoperate
	Only do this if the update time on the secondary prefs is later than the update
	time on the normal prefs
]]--

function NmbFileManagementSettings.maybeLoadSecondaryPrefs()

	-- To load caller must provide the location to read the prefs file from
	assert( NmbFileManagementSettings.secondaryPrefsLocation )

	if NmbFileManagementSettings.secondaryPrefsLocation then

		local prefsString = WFFileUtils.getFileContents( NmbFileManagementSettings.secondaryPrefsLocation )

		if prefsString then
			-- there is no WFTableArchiving.tableFromString( )

			local tempSettings
			local status

			local f = loadstring( "return " .. prefsString )
			if f then
				local nullEnv = {}
				setfenv( f, nullEnv ) -- simplistic sandbox
				status, tempSettings = pcall( f )
			end

			if not status or not tempSettings then
				-- secondary prefs file is corrupt, save current settings so we have a valid set
				NmbFileManagementSettings.saveSecondaryPrefs()

				return
			end

			if tempSettings._lastUpdateTime > _NmbFileManagementSettings._lastUpdateTime then
			
				_NmbFileManagementSettings = tempSettings
				
				-- Make sure the settings in KSPrefs are aligned with the settings in the secondary
				-- prefs location. 
				
				KSPrefs.NmbFileManagementSettings_targetNimbusDiskSpacePercentage 
						= _NmbFileManagementSettings._targetNimbusDiskSpacePercentage
					
				KSPrefs.NmbFileManagementSettings_KeepAllOriginals 
						= _NmbFileManagementSettings._keepAllOriginals
					
				KSPrefs.NmbFileManagementSettings_KeepAllProxies
						= _NmbFileManagementSettings._keepAllProxies
				  
				KSPrefs.NmbFileManagementSettings_relativeOriginalStorageLocation
						= _NmbFileManagementSettings._relativeOriginalStorageLocation
					
				KSPrefs.NmbFileManagementSettings_relativeProxyStorageLocation
						= _NmbFileManagementSettings._relativeProxyStorageLocation
					
				KSPrefs.NmbFileManagementSettings_originalStorageVolumeGuid
						= _NmbFileManagementSettings._originalStorageVolumeGuid 
					
				KSPrefs.NmbFileManagementSettings_proxyStorageVolumeGuid
						= _NmbFileManagementSettings._proxyStorageVolumeGuid
					
				KSPrefs.NmbFileManagementSettings_volumeReadableIdentifiers
						= _NmbFileManagementSettings._volumeReadableIdentifiers 
								and KSJsonEncoder.encodeWebSafeJson( _NmbFileManagementSettings._volumeReadableIdentifiers )
				
			end

		end

	end

end

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

function NmbFileManagementSettings.originalStorageLocationInfo()

	return {
		relativePath = NmbFileManagementSettings._relativeOriginalStorageLocation,
		volumeGuid = NmbFileManagementSettings._originalStorageVolumeGuid,
		readableIdentifier = NmbFileManagementSettings._volumeReadableIdentifiers[ NmbFileManagementSettings._originalStorageVolumeGuid ]
	}
	
end

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

function NmbFileManagementSettings.proxyStorageLocationInfo()

	return {
		relativePath = NmbFileManagementSettings._relativeProxyStorageLocation,
		volumeGuid = NmbFileManagementSettings._proxyStorageVolumeGuid,
		readableIdentifier = NmbFileManagementSettings._volumeReadableIdentifiers[ NmbFileManagementSettings._proxyStorageVolumeGuid ]
	}
	
end

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

function NmbFileManagementSettings.keepAllOriginals()

	return NmbFileManagementSettings._keepAllOriginals
	
end

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

function NmbFileManagementSettings.keepAllProxies()

	return NmbFileManagementSettings._keepAllProxies
	
end

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

function NmbFileManagementSettings.targetNimbusDiskSpacePercentage()

	return NmbFileManagementSettings._targetNimbusDiskSpacePercentage
	
end

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

function NmbFileManagementSettings.minimumFreeSpace()

	return NmbFileManagementSettings._minimumFreeSpace
	
end

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

function NmbFileManagementSettings.maxPollingInterval()

	return NmbFileManagementSettings._maxPollingInterval
	
end

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

function NmbFileManagementSettings.lastUpdateTime()

	return NmbFileManagementSettings._lastUpdateTime
	
end

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

--[[!
    \brief Sets the target amount of free space that Nimbus should use on the given system.
	This is persistent across sessions and is stored in a preference.
	
	\param targetDiskSpacePercentage (number) The target percent of free disk space.
]]--

function NmbFileManagementSettings.setTargetDiskSpacePercentage( targetDiskSpacePercentage )

	WFArguments.check( 'number' )
	
	if targetDiskSpacePercentage < 0 or targetDiskSpacePercentage > 1 then
		error( "NmbFileManagementSettings.setTargetFreeDiskSpace: targetDiskSpacePercentage must be a positive number, but less than 1", 2 )
	end
	
	NmbFileManagementSettings._targetNimbusDiskSpacePercentage = targetDiskSpacePercentage
	KSPrefs.NmbFileManagementSettings_targetNimbusDiskSpacePercentage = targetDiskSpacePercentage

end

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

--[[!
    \brief Sets whether or not we should store all originals locally.
	
	\param value (boolean)
]]--

function NmbFileManagementSettings.setKeepAllOriginals( value )

	WFArguments.check( 'boolean' )
	
	NmbFileManagementSettings._keepAllOriginals = value
	KSPrefs.NmbFileManagementSettings_KeepAllOriginals = value
	
end

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

--[[!
    \brief Sets whether or not we should store all proxies locally.
	
	\param value (boolean)
]]--

function NmbFileManagementSettings.setKeepAllProxies( value )

	WFArguments.check( 'boolean' )
	
	NmbFileManagementSettings._keepAllProxies = value
	KSPrefs.NmbFileManagementSettings_KeepAllProxies = value
	
end

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

local function getStorageInfo( maybeLocation )

	local storageInfo = {}
	
	if maybeLocation then
	
		local volumeGuid = WFFileUtils.getVolumeGuidForPath( maybeLocation )
		
		if volumeGuid then
		
			local relativePath = WFFileUtils.removeVolumeFromPath( maybeLocation )
			local volumeName = WFFileUtils.getVolumeNameFromGuid( volumeGuid )
			
			storageInfo.volumeGuid = volumeGuid
			storageInfo.relativePath = relativePath
			storageInfo.readableIdentifier = volumeName
			
		end
		
	end

	return storageInfo

end

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

--[[!
     \brief Sets the user-facing volume identifier for a specified volume.
	
	\param readableIdentifier (string) A user readable string containing the name of the
	volume or other identifible characteristics.
]]--

function NmbFileManagementSettings.setVolumeReadableIdentifier( volumeGuid, readableIdentifier )

	WFArguments.check( '?string', '?string' )
	
	if volumeGuid then
	
		local volumeReadableIdentifiers = NmbFileManagementSettings._volumeReadableIdentifiers
		volumeReadableIdentifiers[ volumeGuid ] = readableIdentifier
		
		KSPrefs.NmbFileManagementSettings_volumeReadableIdentifiers = KSJsonEncoder.encodeWebSafeJson( volumeReadableIdentifiers )
			-- Encode the table as a JSON string, since KSPrefs only supports scalar values.
			
		NmbFileManagementSettings._volumeReadableIdentifiers = volumeReadableIdentifiers

	end

end

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

--[[!
    \brief Get the user-facing volume identifier for the given volume GUID.
	
	\param volumeGuid (string) The volume GUID to retrieve the readable identifier for.
	
	\return (?string) If we've cached a readable identifier for the volume, return it.
]]--

function NmbFileManagementSettings.getReadableIdentifierForVolumeGuid( volumeGuid )

	WFArguments.check( '?string' )
	
	if volumeGuid then
	
		return NmbFileManagementSettings._volumeReadableIdentifiers[ volumeGuid ]
	
	end
	
end

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

--[[!
    \brief Sets a custom storage location for originals.
	
	\param maybeLocation (string) The custom storage location. If nil, the custom
	location is removed and we go back to using the default storage location.
]]--

function NmbFileManagementSettings.setOriginalStorageLocation( maybeLocation )

	WFArguments.check( '?string' )
	
	local storageInfo = getStorageInfo( maybeLocation )
	
	KSPrefs.NmbFileManagementSettings_relativeOriginalStorageLocation = storageInfo.relativePath
	NmbFileManagementSettings._relativeOriginalStorageLocation = storageInfo.relativePath

	KSPrefs.NmbFileManagementSettings_originalStorageVolumeGuid = storageInfo.volumeGuid
	NmbFileManagementSettings._originalStorageVolumeGuid = storageInfo.volumeGuid
	
	NmbFileManagementSettings.setVolumeReadableIdentifier( storageInfo.volumeGuid, storageInfo.readableIdentifier )

end

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

--[[!
    \brief Sets a custom storage location for proxies.
	
	\param maybeLocation (string) The custom storage location. If nil, the custom
	location is removed and we go back to using the default storage location.
]]--

function NmbFileManagementSettings.setProxyStorageLocation( maybeLocation )

	WFArguments.check( '?string' )
		
	local storageInfo = getStorageInfo( maybeLocation )
	
	KSPrefs.NmbFileManagementSettings_relativeProxyStorageLocation = storageInfo.relativePath
	NmbFileManagementSettings._relativeProxyStorageLocation = storageInfo.relativePath
	
	KSPrefs.NmbFileManagementSettings_proxyStorageVolumeGuid = storageInfo.volumeGuid
	NmbFileManagementSettings._proxyStorageVolumeGuid = storageInfo.volumeGuid
	
	NmbFileManagementSettings.setVolumeReadableIdentifier( storageInfo.volumeGuid, storageInfo.readableIdentifier )
	
end

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

--[[!
    \brief Reset all file management settings to the default settings.
]]--

function NmbFileManagementSettings.reset()

	NmbFileManagementSettings.setTargetDiskSpacePercentage( DEFAULT_TARGET_DISK_SPACE_USAGE )
	NmbFileManagementSettings.setKeepAllOriginals( DEFAULT_KEEP_ALL_ORIGINALS )
	NmbFileManagementSettings.setKeepAllProxies( DEFAULT_KEEP_ALL_PROXIES )
	NmbFileManagementSettings.setOriginalStorageLocation( nil )
	NmbFileManagementSettings.setProxyStorageLocation( nil )
	
end

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

return NmbFileManagementSettings
