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

NmbAlbumsCache.lua

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

ADOBE CONFIDENTIAL
------------------
Copyright 2017 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.

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

	-- Rx
local Rx = require 'Rx'

	-- WF.core
local KSTableUtils = require 'KSTableUtils'
local WFCloseSupport = require 'WFCloseSupport'

	-- WF.oz_client.managed_catalog
local WFCatAlbums = require 'WFCatAlbums'
local WFCatSyncStatus = require 'WFCatSyncStatus'
local WFCatCollaboration = require 'WFCatCollaboration'

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

local derefOpt = assert( KSTableUtils.derefOpt )

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

local function addAdditionalInfoToAlbum( album, counts, covers, statuses, markedForOfflineStatus, maybeShares )

	local shareInfoForAlbum = maybeShares[ album.fullDocId ]
	local isShared = shareInfoForAlbum and shareInfoForAlbum.isShared
	local isPrivate = shareInfoForAlbum and shareInfoForAlbum.isPrivate
	local nCollaborators = shareInfoForAlbum and shareInfoForAlbum.nCollaborators
	
	album.syncStatus = statuses[ album.fullDocId ]
	album.count = counts[ album.fullDocId ]
	album.cover = covers[ album.id ]
	album.isShared = isShared
	album.isPrivate = isPrivate
	album.nCollaborators = nCollaborators
	album.isMarkedForOffline = markedForOfflineStatus[ album.fullDocId ]
	
end

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

local function createBaseAlbumsHierarchy( wfCatalog )
	
	return Rx.Observable.combineLatest( function( albums, projectAlbums )
		
			local hierarchy = WFCatAlbums._initAlbumHierarchy()
			
			for _, album in ipairs( albums ) do
				WFCatAlbums._addAlbumIntoHierarchy( album, hierarchy )
			end
			
			for _, projectAlbum in ipairs( projectAlbums ) do
				WFCatAlbums._addAlbumIntoHierarchy( projectAlbum, hierarchy )
			end
			
			return hierarchy
		
		end,
		WFCatAlbums._basicAlbumsList( wfCatalog, false ),
		WFCatAlbums._basicProjectAlbumsList( wfCatalog ) )

end

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

local function getAdditionalAlbumInfo( wfCatalog )

	return Rx.Observable.combineLatest( 
				WFCatAlbums.getAlbumCounts( wfCatalog ):startWith( {} ),
				WFCatAlbums.getAlbumCovers( wfCatalog ):startWith( {} ),
				WFCatAlbums.getProjectAlbumCovers( wfCatalog ):startWith( {} ),
				WFCatSyncStatus.getSyncStatusForAlbums( wfCatalog, true --[[ignore album status]] ):startWith( {} ),
				WFCatCollaboration.getSharingStatusByAlbumId( wfCatalog ):startWith( {} ),
				WFCatAlbums.getAlbumMarkedForOfflineStatus( wfCatalog ):startWith( {} ) )

		:map( function( counts, covers, projectCovers, status, shares, markedForOfflineStatus )
			-- add project covers to covers since BaseHeirarchy (see above) includes projectAlbums.
			for albumId, coverId in pairs(projectCovers or {}) do
				covers[ albumId ] = coverId
			end
			return {
				counts = counts,
				covers = covers,
				status = status,
				shares = shares,
				markedForOfflineStatus = markedForOfflineStatus,
			} 
		end )

end 

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

local function combineAdditionalAlbumInfoWithBaseHierarchy( baseHierarchy, additionalAlbumInfo )
	
	local counts = additionalAlbumInfo.counts or {}
	local covers = additionalAlbumInfo.covers or {}
	local status = additionalAlbumInfo.status or {}
	local shares = additionalAlbumInfo.shares or {}
	local markedForOfflineStatus = additionalAlbumInfo.markedForOfflineStatus or {}
	
	local fullHierarchy = WFCatAlbums._initAlbumHierarchy()
			
	local hasAdditionalAlbumInfo = next( counts ) or next( covers ) or next( status ) or next( shares ) or next( markedForOfflineStatus )
			
	for _, album in pairs( baseHierarchy.localIdToAlbumData ) do
		if hasAdditionalAlbumInfo then
			addAdditionalInfoToAlbum( album, counts, covers, status, markedForOfflineStatus, shares )
		end
		
		WFCatAlbums._addAlbumIntoHierarchy( album, fullHierarchy )
	end
			
	return fullHierarchy

end


local SimpleAlbumsCache = Rx.Class 'WF.internals.SimpleAlbumsCache' {
	
	_baseHierarchyObservable = false,
	
	_baseHierarchySubscription = false,
	
	_albumsCacheObservable = false,
	
	_albumsSubscription = false,
	
	_additionalAlbumInfoObservable = false,
	
	_additionalAlbumInfoSubscription = false,
	
	WFCloseSupport.addCloseSupportMethods,
	
	__init = function( self, catalog )
			
		self._baseHierarchyObservable = Rx.ReplaySubject( 1 )
		
		self._baseHierarchySubscription = createBaseAlbumsHierarchy( catalog )
			:subscribe( function( baseHierarchy )
				self:maybeUpdateBaseHierarchy( baseHierarchy )
			end )
			
		self._additionalAlbumInfoObservable = Rx.ReplaySubject( 1 )
		
		self._additionalAlbumInfoSubscription = getAdditionalAlbumInfo( catalog )
			:subscribe( function( additionalAlbumInfo )
				self._additionalAlbumInfoObservable:onNext( additionalAlbumInfo )
			end )
			
		self._albumsCacheObservable = Rx.ReplaySubject( 1 )
			
		self._albumsSubscription = Rx.Observable.combineLatest( self._baseHierarchyObservable, self._additionalAlbumInfoObservable )
			:map( function( baseHierarchy, additionalAlbumInfo )
				return combineAdditionalAlbumInfoWithBaseHierarchy( baseHierarchy, additionalAlbumInfo )
			end )
			:subscribe( function( fullAlbumInfo )
				
				self:maybeUpdateCacheWithHierarchy( fullAlbumInfo )
				
			end )
	end,
	
	close = function( self )
		
		if self._baseHierarchySubscription then
			self._baseHierarchySubscription:dispose()
			self._baseHierarchySubscription = nil
		end
		
		if self._albumsSubscription then
			self._albumsSubscription:dispose()
			self._albumsSubscription = nil
		end
		if self._additionalAlbumInfoSubscription then
			self._additionalAlbumInfoSubscription:dispose()
			self._additionalAlbumInfoSubscription = nil
		end
		
	end,
	
	maybeUpdateBaseHierarchy = function( self, baseHierarchy )
		self._baseHierarchyObservable:onNext( baseHierarchy )
	end,
	
	maybeUpdateCacheWithHierarchy = function( self, hierarchy )
		self._albumsCacheObservable:onNext( hierarchy )
	end,
	
	getCurrentBaseCache = function( self )
		return self._baseHierarchyObservable:take( 1 )
	end,
	
	getCache = function( self )
		return self._albumsCacheObservable
	end,
	
	addAlbumOrFolder = function( self, docValue, optParentId )
	
		-- SPAM [matthewj 2017-02-03]: Because the creation in the cache on adds happens *after*
		-- the creation in the doc store, we are not doing validation on whether it's ok to add 
		-- to the cache
		return self:getCurrentBaseCache()
			:map( function( cachedHierarchy )
			
				local localId = derefOpt( docValue, '_localId' )
				local albumName = derefOpt( docValue, 'name' )
				local subtype = derefOpt( docValue, 'subtype' )
				local optHidden = derefOpt( docValue, 'hideInOrganize' )
				local updateTimestamp = derefOpt( docValue, 'userUpdated' )
				local createdTimestamp = derefOpt( docValue, 'userCreated' )
			
				if not WFCatAlbums._getAlbumFromHierarchyById( cachedHierarchy, localId ) then
					
					local album = {
						id = localId,
						name = albumName,
						parentId = optParentId,
						subtype = subtype,
						hidden = optHidden,
						updateTimestamp = updateTimestamp,
						createdTimestamp = createdTimestamp,
					}
					
					WFCatAlbums._addAlbumIntoHierarchy( album, cachedHierarchy )
					
					self:maybeUpdateBaseHierarchy( cachedHierarchy )
				end
				
			end )
	end,
		
	renameAlbumOrFolder = function( self, albumId, albumName )
		return  WFCatAlbums._validateRenameAlbumInHierarchy( self:getCurrentBaseCache(), albumId, albumName )
			:map( function( cachedHierarchy )
				
				local album = WFCatAlbums._getAlbumFromHierarchyById( cachedHierarchy, albumId )
				
				if album then
					album.name = albumName
					self:maybeUpdateBaseHierarchy( cachedHierarchy )
				end
				
			end )
			:catch( Rx.Observable.empty() )
	end,
	
	setAssetSortOrder = function( self, albumId, assetSortOrder )
		return  WFCatAlbums._validateAssetSortOrder( self:getCurrentBaseCache(), albumId, assetSortOrder )
			:map( function( cachedHierarchy )
				
				local album = WFCatAlbums._getAlbumFromHierarchyById( cachedHierarchy, albumId )
				
				if album then
					album.assetSortOrder = assetSortOrder
					self:maybeUpdateBaseHierarchy( cachedHierarchy )
				end
				
			end )
			:catch( Rx.Observable.empty() )
	end,
	
	reparentAlbumOrFolder = function( self, albumId, optNewParentId )
		return WFCatAlbums._validateReparentAlbumInHierarchy( self:getCurrentBaseCache(), albumId, optNewParentId )
			:map( function( hierarchy )

				hierarchy = WFCatAlbums._reparentAlbumInHierarchy( hierarchy, albumId, optNewParentId )
				self:maybeUpdateBaseHierarchy( hierarchy )
				
			end )
	end,
	
	reparentMultipleAlbumsOrFolders = function( self, albumIds, optNewParents )
		return WFCatAlbums._validateReparentMultipleAlbumsInHierarchy( self:getCurrentBaseCache(), albumIds, optNewParents )
			:map( function( hierarchy, maybeErrors )
				-- Related to NMB-6314: if attempting to reparent multiple albums and there is an error, we 
				-- reject the full reparent even if some would succeed.
				if not maybeErrors then
					
					for _, albumId in ipairs( albumIds ) do
						local maybeParentId 
						if type( optNewParents ) == 'number' or type( optNewParents ) == 'string' then
							maybeParentId = optNewParents
						elseif type( optNewParents ) == 'table' then
							maybeParentId = optNewParents[ albumId ]
						end
				
						hierarchy = WFCatAlbums._reparentAlbumInHierarchy( hierarchy, albumId, maybeParentId )
					end
					
					self:maybeUpdateBaseHierarchy( hierarchy )
					
				end
				
			end )
	end,
	
	updateSmartAlbum = function( self, albumId, smartAlbumName, optNewParentId, predicates )
		return WFCatAlbums._validateReparentAlbumInHierarchy( self:getCurrentBaseCache(), albumId, optNewParentId, smartAlbumName )
			:tap( function( hierarchy )
				
				hierarchy = WFCatAlbums._reparentAlbumInHierarchy( hierarchy, albumId, optNewParentId )
				
				local album = WFCatAlbums._getAlbumFromHierarchyById( hierarchy, albumId )
				
				if album then
					album.name = smartAlbumName
					album.predicates = predicates
				end
				
				self:maybeUpdateBaseHierarchy( hierarchy )
				
			end )
	end,
	
	deleteAlbumOrFolder = function( self, albumId )
	
		return self:getCurrentBaseCache()
			:map( function( cachedHierarchy )
				
				local idsRemoved
				cachedHierarchy, idsRemoved = WFCatAlbums._deleteAlbumFromHierarchy( cachedHierarchy, albumId )
			
				self:maybeUpdateBaseHierarchy( cachedHierarchy )
				
				return idsRemoved
			end )
	end,
	
	deleteProjectAlbum = function( self, albumId )
	
		return self:getCurrentBaseCache()
			:map( function( cachedHierarchy )
				
				local idsRemoved
				cachedHierarchy, idsRemoved = WFCatAlbums._deleteProjectAlbumFromHierarchy( cachedHierarchy, albumId )
				
				self:maybeUpdateBaseHierarchy( cachedHierarchy )
				
				return idsRemoved
			end )
			
	end,
	
	shareAlbum = function( self, albumId )
		return self:getCache()
			:take( 1 )
			:map( function( cachedHierarchy )
				local album = WFCatAlbums._getAlbumFromHierarchyById( cachedHierarchy, albumId )
				if album then
					self:maybeUpdateCacheWithHierarchy( cachedHierarchy )
				end
			end )
	end,
	
	unshareAlbum = function( self, albumId )
		return self:getCache()
			:take( 1 )
			:map( function( cachedHierarchy )
				local album = WFCatAlbums._getAlbumFromHierarchyById( cachedHierarchy, albumId )
				if album then
					album.isShared = nil
					self:maybeUpdateCacheWithHierarchy( cachedHierarchy )
				end
			end )
	end,
	
	getAlbumsFromCache = function( self )
		return self._albumsCacheObservable
			:map( function( hierarchy ) 
				return hierarchy and hierarchy.albums or {}
			end )
	end,
	
	getAdhocAlbumsFromCache = function( self )
		return self._albumsCacheObservable
			:map( function( hierarchy ) 
				return hierarchy and hierarchy.hiddenAlbums or {}
			end )
	end,
	
	getSharedAlbumsFromCache = function( self )
		return self._albumsCacheObservable
			:map( function( hierarchy )
				return hierarchy and hierarchy.sharedAlbums or {}
			end )
			:map( function( results )
				-- SPAM [matthewj 2018-08-08]: This is *horribly* optimized, please do better
				local sharedAlbums = {}
				for _, album in ipairs( results ) do
					if album.isShared then
						sharedAlbums[ #sharedAlbums + 1 ] = album
					end
				end
				return sharedAlbums
			end ) 
	end,
	
	getSharedWithYouAlbumsFromCache = function( self )
		return self._albumsCacheObservable
			:map( function( hierarchy )
				return hierarchy and hierarchy.sharedWithYouAlbums or {}
			end )
	end,
	
	getProjectAlbumsFromCache = function( self )
		return self._albumsCacheObservable
			:map( function( hierarchy )
				return hierarchy and hierarchy.projectAlbums or {}
			end )
	end,

}


return SimpleAlbumsCache
