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

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 NmbAcr = require "NmbAcr"
local WFLogger = require "WFLogger"

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

local WFAcrOperations = assert( NmbAcr.WFAcrOperations )
local WFCrInfo = assert( NmbAcr.WFCrInfo )


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 hasBeenInitialized( dir )

	-- Check if its a none empty string.
	-- That would be sufficient, since
	-- any non-empty string implies that
	-- the value was set.
	return dir and dir ~= ""

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 hasBeenInitialized( WFCrInfo.getUserPresetDir() ) 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
			
			return Rx.Observable.combineLatest( 
				waitForCatalogDocumentToBeAvailable( catalog ),
				WFAcrOperations.getStyleFavorites() )
				
				:flatMap( function( _, styleFavorites )
				
					local docOverlay = {
						_id = catalog:ozCatalogId(),
					}
					
					local hasSomeFavorites = false
					
					if next( styleFavorites.presetFavorites ) then
						hasSomeFavorites = true
						docOverlay.presets = {
							favorites = createFavoritesTableForOz( styleFavorites.presetFavorites )
						}
					end
					
					if 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