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

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

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

local Rx = require "Rx"
local WFRxRunUntilDone = require "WFRxRunUntilDone"
local KSTableUtils = require "KSTableUtils"
local WFRxScheduler = require "WFRxScheduler"
local WFLogger = require "WFLogger"

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

local NmbAcr

local logger = WFLogger 'NmbStyleFavorites'
local kDeleteToken = { _delete = true }
local derefOpt = assert( KSTableUtils.derefOpt )

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

local QUERY_CALM_INTERVAL = 3 --In secs. Arbitrary value.
local CR_DIR_SETTING_DELAY = 5 --In secs. Arbitrary value.
local SLURP_ERROR_RETRY_INTERVAL = 60 --In secs. Arbitrary value.
local FAV_SLURP_MARKER_DOC_ID = "favoritesSlurpDone"

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

local function distinctTableCompare( _, tbl1, _, tbl2 )

	return KSTableUtils.deepEquals( tbl1, tbl2 )

end
--------------------------------------------------------------------------------

local function createFavoritesTableForOz( favorites )
	local favsTable = {}
	for fp, isFav in pairs( favorites ) do		
		favsTable[ fp ] = isFav
 	end
	return favsTable
end

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

local function isSlurpDone( localSlurpDocValue )

	return localSlurpDocValue and localSlurpDocValue[ FAV_SLURP_MARKER_DOC_ID ] == true

end

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

local function waitForCatalogDocumentToBeAvailable( catalog )

	-- Catalog document will either already be available
	-- or will eventually become available when the metadata
	-- puller is able to get and save it. Wait till
	-- its done.
	return catalog:docStore():ongoingDocValue( catalog:ozCatalogId() )
	
		:filter( function( docValue )
			return docValue and not docValue._empty
		end )
		
		:take( 1 )

end


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

local NmbStyleFavorites = {}

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

function NmbStyleFavorites.changeFavoriteStatus( catalog, 
	fingerprint, isProfile, isFavorite, isDefaultFavorite )
	
	return catalog:docStore():localDocValue( FAV_SLURP_MARKER_DOC_ID )
		
		:flatMapLatest( function( docValue )
			
			if not isSlurpDone( docValue ) then
				-- Slurp is not yet done.
				-- If slurp is done, it will ensure that
				-- the catalog document is available locally
				return Rx.Observable.empty()
			end
	
		local section = isProfile and "profiles" or "presets"
		
		local favToken = ( isFavorite == isDefaultFavorite ) 
			and kDeleteToken -- The style is favorite and is favorite by default or vice versa
			or isFavorite -- Favorite status doesn't match with default status
		
		local docOverlay = {
			_id = catalog:ozCatalogId(),
			
			[ section ] = {
				favorites = {
					[ fingerprint ] = favToken,
				},
			}
		}
		
		return catalog:docStore():applyOverlay( docOverlay )
			:tapOnError( function( err )
			
				logger:errorf( "Changing favoriting status failed for fp:%s. Error:%s", fingerprint, err )
			end )
	end )
end

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

function NmbStyleFavorites.removeFavoriteInfo( catalog, 
	fingerprint, isProfile )
	
	if not fingerprint then
		return Rx.Observable.just( true )
	end
	
	return catalog:docStore():localDocValue( FAV_SLURP_MARKER_DOC_ID )
		
		:flatMapLatest( function( docValue )
			
			if not isSlurpDone( docValue ) then
				-- Slurp is not yet done.
				-- If slurp is done, it will ensure that
				-- the catalog document is available locally
				return Rx.Observable.just( true )
			end
	
		local section = isProfile and "profiles" or "presets"
		
		local docOverlay = {
			_id = catalog:ozCatalogId(),
			
			[ section ] = {
				favorites = {
					[ fingerprint ] = kDeleteToken,
				},
			}
		}
		
		return catalog:docStore():applyOverlay( docOverlay )
			:catch( function( err )
				-- Return a value irrespective of the error
				logger:errorf( "Changing favoriting status failed for fp:%s. Error:%s", fingerprint, err )
				return Rx.Observable.just( false )
			end )
	end )
end

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

function NmbStyleFavorites.favoriteInfoStream( catalog )

	-- Wait for the favorites slurp to finish, so that
	-- we dont overwrite the local data with the data
	-- from server
	
	return catalog:docStore():ongoingLocalDocValue( FAV_SLURP_MARKER_DOC_ID )
		
		:filter( function( docValue )
			return isSlurpDone( docValue )
		end )
		
		:take( 1 ) -- We dont have to watch for ongoing values, once its true
		
		:flatMapLatest( function()
			return catalog:docStore():ongoingDocValue( catalog:ozCatalogId() )
		end )
		:calm( QUERY_CALM_INTERVAL, WFRxScheduler.sharedScheduler() )
		
		:map( function( docValue )
			
			local profileFavorites = derefOpt( docValue, "profiles", "favorites" )
			local presetFavorites = derefOpt( docValue, "presets", "favorites" )
			return {
				presetFavorites = presetFavorites or {}, 
				profileFavorites = profileFavorites or {},
			}
		end )
		:distinctUntilChanged( nil, distinctTableCompare )
end

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

function NmbStyleFavorites.maybeSlurpFavorites( catalog )
	
	return catalog:docStore():localDocValue( FAV_SLURP_MARKER_DOC_ID )
		
		:flatMapLatest( function( docValue )
			
			if isSlurpDone( docValue ) then
				-- If the slurp was done once, then return
				return Rx.Observable.empty()
			end
			
			if not NmbAcr then
				NmbAcr = require "NmbAcr"
			end

			local WFCrInfo = assert( NmbAcr.WFCrInfo )
			
			-- Don't call WFCrInfo.getUserPresetDir here, as that will lock
			-- the path at the cr_sdk level, which we definitely do NOT
			-- want if it hasn't been properly set yet. See NMB-22801.

			if not WFCrInfo.isUserPresetDirInitialized() then
				-- Style path is not yet set.
				-- Need to try after sometime.
				
				return Rx.Observable.timer( CR_DIR_SETTING_DELAY, WFRxScheduler.sharedScheduler() )
					:flatMapLatest( function()
						return NmbStyleFavorites.maybeSlurpFavorites( catalog )
					end )
				
			end

			local WFAcrOperations = assert( NmbAcr.WFAcrOperations )
			
			return Rx.Observable.combineLatest( 
				waitForCatalogDocumentToBeAvailable( catalog ),
				WFAcrOperations.getStyleFavorites() )
				
				:flatMap( function( _, styleFavorites )
				
					local docOverlay = {
						_id = catalog:ozCatalogId(),
					}
					
					local hasSomeFavorites = false
					
					if styleFavorites.presetFavorites and next( styleFavorites.presetFavorites ) then
						hasSomeFavorites = true
						docOverlay.presets = {
							favorites = createFavoritesTableForOz( styleFavorites.presetFavorites )
						}
					end
					
					if styleFavorites.profileFavorites and next( styleFavorites.profileFavorites ) then
						hasSomeFavorites = true
						docOverlay.profiles = {
							favorites = createFavoritesTableForOz( styleFavorites.profileFavorites )
						}
					end
					
					local updateFavObservable
					if hasSomeFavorites then
						updateFavObservable = catalog:docStore():applyOverlay( docOverlay ) 
					else
						updateFavObservable = Rx.Observable.just()
					end
					
					return updateFavObservable
						:flatMapLatest( function()
					
							return catalog:docStore():setLocalDocValue {
								_id = FAV_SLURP_MARKER_DOC_ID,
								[ FAV_SLURP_MARKER_DOC_ID ] = true,
							}
						end )
			
					end )
				end )
				:tapOnError( function( err )
		
					logger:errorf( "Favorites slurp failed. Error:%s", err )
				end )
				
				:catch( function()
					
					-- Setup retry for the error
					return Rx.Observable.timer( SLURP_ERROR_RETRY_INTERVAL, WFRxScheduler.sharedScheduler() )
						:flatMapLatest( function()
							return NmbStyleFavorites.maybeSlurpFavorites( catalog )
						end )
				end )
				
				:call( WFRxRunUntilDone )

end

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

return NmbStyleFavorites