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

LrClassicAlbums.lua

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

ADOBE CONFIDENTIAL
------------------
Copyright 2023 Adobe
All Rights Reserved.

NOTICE: All information contained herein is, and remains
the property of Adobe and its suppliers, if any. The intellectual
and technical concepts contained herein are proprietary to Adobe
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.

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


local Rx = require 'Rx'
local WFCatAlbums = require 'WFCatAlbums'
local WFCatCollaboration = require 'WFCatCollaboration'
local WFCatDevelopSettings = require 'WFCatDevelopSettings'
local WFCatIdUtils = require 'WFCatIdUtils'
local WFCatSharedWithYouAlbums = require 'WFCatSharedWithYouAlbums'
local WFCatSharedWithYouUtils = require 'WFCatSharedWithYouUtils'

local WFRxRunUntilDone = require 'WFRxRunUntilDone'

local runUntilDone = WFRxRunUntilDone

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

local LrClassicAlbums  = {}

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

local function createAlbum( wfCatalog, albumName, optParentId, optHidden )

	return WFCatAlbums.createAlbum( wfCatalog, albumName, optParentId, optHidden )
			:flatMap( function( localDocId, fullDocId, docValue )

			return Rx.Observable.just( docValue )

		end )
end

--------------------------------------------------------------------------------
local function makeCatalogAndCacheBasedObservableIntoSubModel( fn, optObservableType, maybeRunUntilDone )
	
	local deliverOneResult = optObservableType == 'deliverOneResult'
	
	return function( model, ... )
		local params = { ... }
		local nParams = select( '#', ... )
		local value

		local catalogStream = deliverOneResult and model._wfCatalog or model._wfCatalog:take( 1 )
		local subModelStream = catalogStream
                             	:mapSwitch( function( wfCatalog )
									
									if maybeRunUntilDone then
                             			return fn( wfCatalog, unpack( params, 1, nParams ) )
											:call( runUntilDone )
									else
										return fn( wfCatalog, unpack( params, 1, nParams ) )
									end
									
                               	end )
								
								
		value = deliverOneResult and subModelStream:take( 1 ) or subModelStream

		return {
			_wfCatalog = model._wfCatalog,

			value = value,
		}
		
	end
	
end

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

local function makeCatalogAndCacheBasedObservableIntoRunUntilDoneSubModel( fn, optObservableType )

	return makeCatalogAndCacheBasedObservableIntoSubModel( fn, optObservableType, true )

end

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

local function makeCatalogBasedObservableIntoSubModel( fn, optObservableType, maybeRunUntilDone )
	
	local deliverOneResult = optObservableType == 'deliverOneResult'
	
	return function( model, ... )
		local params = { ... }
		local nParams = select( '#', ... )
		local value
		
		local catalogStream = deliverOneResult and model._wfCatalog or model._wfCatalog:take( 1 )
		local subModelStream = catalogStream
                             	:mapSwitch( function( wfCatalog )
								
									if maybeRunUntilDone then
                             			return fn( wfCatalog, unpack( params, 1, nParams ) )
											:call( runUntilDone )
									else
										return fn( wfCatalog, unpack( params, 1, nParams ) )
									end
									
                               	end )
								
		value = deliverOneResult and subModelStream:take( 1 ) or subModelStream

		return {
			_wfCatalog = model._wfCatalog,

			value = value,
		}
	end
end

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

local function makeCatalogBasedObservableIntoRunUntilDoneSubModel( fn, optObservableType )

	return makeCatalogBasedObservableIntoSubModel( fn, optObservableType, true )
	
end

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

local function addAssetsToAlbum( wfCatalog, albumId, assetIds, maybeRedactionOptions )

	return WFCatSharedWithYouAlbums.getSpaceIdOfAlbumId( wfCatalog, albumId )
	
		:flatMap( function( spaceId )
		
			if spaceId then
			
				return WFCatDevelopSettings.waitForPendingDevelopSettingsSaveForAssetIds( assetIds )
				
					:flatMap( function()
					
						return WFCatSharedWithYouAlbums.addAssetsToAlbum( wfCatalog, spaceId, albumId, assetIds, maybeRedactionOptions )
					
					end )
					
					:map( function( added, duplicates, invalidAssets )
						return {
							added = added,
							duplicates = duplicates,
							invalidAssets = invalidAssets,
						}
					end )
			
			else
			
				return WFCatAlbums.addAssetsToAlbum( wfCatalog, albumId, assetIds )
			
			end
		
		end )

end

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

local function removeAssetsFromAlbum( wfCatalog, albumId, assetIds )

	return WFCatSharedWithYouAlbums.getSpaceIdOfAlbumId( wfCatalog, albumId )
	
		:flatMap( function( spaceId )
		
			if spaceId then
			
				return WFCatSharedWithYouAlbums.deleteAssets( wfCatalog, spaceId, assetIds )
			
			else
			
				return WFCatAlbums.removeAssetsFromAlbum( wfCatalog, albumId, assetIds )
			
			end
		
		end )

end

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

local function albumAssetsAndSubtype( wfCatalog, albumId, albumType )
	
	local albumAssetIdsObservable
	if albumType == 'project' then
		albumAssetIdsObservable = WFCatAlbums.getProjectAlbumAssetIds( wfCatalog, albumId )
	else
		albumAssetIdsObservable = WFCatAlbums.getAlbumAssetIds( wfCatalog, albumId )
	end
	   
	return albumAssetIdsObservable
		:take( 1 )
        :flatMap( function( assetIdsMap )
		
            local assetIds = {}
			
            for assetId, _ in pairs( assetIdsMap ) do
                assetIds[ #assetIds + 1 ] = assetId
            end
			
            return WFCatIdUtils.getOzAssetIdsAndTypeForIds( wfCatalog, assetIds )
					:map( function( assetIdsAndSubtypes ) 
						
						local assetIdsToSubtypes = {}
						for _, row in ipairs( assetIdsAndSubtypes ) do
							assetIdsToSubtypes[ row[ 1 ] ] = row[ 2 ]
						end
						
						local result = {}
						for _, assetId in ipairs( assetIds ) do
							result[ #result + 1 ] = { 
								id = assetId, 
								subtype = assetIdsToSubtypes[ assetId ] 
							}
						end
						return result
						
					end )
        end )
		
end

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

--[[
	\brief Returns an array of tables that contain the asset ids in an album along with the subtype of the asset
	(image, video, stack, etc.) if we have it. i.e. <code>{ { id="abc123", subtype="image" }</code>. 
	
	There may be some asset subtypes that we don't have a readily accessible subtype for and for these the subtype may 
	be <code>nil</code> or additional assets and subtypes may be added to the album later. For these reasons it 
	is best to check the subtypes affirmatively when using the results of this function. For example,
	
	<code>if row.subtype == 'image' or row.subtype =='video' then ...</code>
	
	\param wfCatalog WFCatalog
	\param albumId the local or full doc id of an album to get the assets
	
	\return <code>Observable<table></code> list of tables containing the <code>id</code> and <code>subtype</code> 
		of each asset in the provided album
--]]
local function getAlbumAssetsAndSubtype( wfCatalog, albumId )

    return albumAssetsAndSubtype( wfCatalog, albumId )
        
end 

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

--[[
	\brief Returns an array of tables that contain the asset ids in a project album along with the subtype of the asset
	(image, video, stack, etc.) if we have it. i.e. <code>{ { id="abc123", subtype="image" }</code>. 
	
	There may be some asset subtypes that we don't have a readily accessible subtype for and for these the subtype may 
	be <code>nil</code> or additional assets and subtypes may be added to the album later. For these reasons it 
	is best to check the subtypes affirmatively when using the results of this function. For example,
	
	<code>if row.subtype == 'image' or row.subtype =='video' then ...</code>
	
	\param wfCatalog WFCatalog
	\param projectAlbumId the local or full doc id of a project album to get the assets
	
	\return <code>Observable<table></code> list of tables containing the <code>id</code> and <code>subtype</code> 
		of each asset in the project album
--]]
local function getProjectAlbumAssetsAndSubtype( wfCatalog, projectAlbumId )
	 
	 return albumAssetsAndSubtype( wfCatalog, projectAlbumId, 'project' )

end

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

--[[
	\param wfCatalog an Observable( WFCatalog )
	Returns a model whose value is an Observable array of top-level albums, each containing fields:
		id : (int) the uuid of the album
		name : (user string) the name of the album
		path : (user string) The name of the parents and album separated by /
		parentId : (uuid, or nil) the id of the albums parent
		children : (array) array of child albums
--]]

function LrClassicAlbums.makeAlbumsModel( wfCatalog )
	
	return {
	
		_wfCatalog = wfCatalog,
		
		priority = 'high',
		
		value = {},

		models = {

			-- album ops
			createAlbum = makeCatalogAndCacheBasedObservableIntoRunUntilDoneSubModel( createAlbum, 'deliverOneResult' ),
			deleteAlbum = makeCatalogAndCacheBasedObservableIntoRunUntilDoneSubModel( WFCatAlbums.deleteIndividualAlbums, 'deliverOneResult' ),
			renameAlbum = makeCatalogAndCacheBasedObservableIntoRunUntilDoneSubModel( WFCatAlbums.renameAlbumOrFolder, 'deliverOneResult' ),
			
			shareAlbum = makeCatalogAndCacheBasedObservableIntoRunUntilDoneSubModel( WFCatCollaboration.shareAlbum, 'deliverOneResult' ),
			unshareAlbum = makeCatalogAndCacheBasedObservableIntoRunUntilDoneSubModel( WFCatCollaboration.unshareAlbum, 'deliverOneResult' ),
			setAssetSortOrder = makeCatalogAndCacheBasedObservableIntoRunUntilDoneSubModel( WFCatAlbums.setAssetSortOrder, 'deliverOneResult' ),
			updateAlbumAssetOrdering = makeCatalogBasedObservableIntoRunUntilDoneSubModel( WFCatAlbums.updateAlbumAssetOrdering , 'deliverOneResult' ),
			
			-- do not use cache
			addAssetsToAlbum = makeCatalogBasedObservableIntoRunUntilDoneSubModel( addAssetsToAlbum, 'deliverOneResult' ),
			addAssetsToAlbumWithOrder = makeCatalogBasedObservableIntoRunUntilDoneSubModel( WFCatAlbums.addAssetsToAlbumWithOrder, 'deliverOneResult' ),
			removeAssetsFromAlbum = makeCatalogBasedObservableIntoRunUntilDoneSubModel( removeAssetsFromAlbum ),
			setAlbumCover = makeCatalogBasedObservableIntoRunUntilDoneSubModel( WFCatAlbums.setAlbumCover, 'deliverOneResult' ),
			unhideAlbum = makeCatalogBasedObservableIntoRunUntilDoneSubModel( WFCatAlbums.unhideAlbum, 'deliverOneResult' ),
			getAlbumAssets = makeCatalogBasedObservableIntoRunUntilDoneSubModel( WFCatAlbums.getAlbumAssetIds, 'deliverOneResult' ),
			getAlbumAssetsAndSubtype = makeCatalogBasedObservableIntoRunUntilDoneSubModel( getAlbumAssetsAndSubtype, 'deliverOneResult' ),
			getProjectAlbumAssets = makeCatalogBasedObservableIntoRunUntilDoneSubModel( WFCatAlbums.getProjectAlbumAssetIds, 'deliverOneResult' ),
			getProjectAlbumAssetsAndSubtype = makeCatalogBasedObservableIntoRunUntilDoneSubModel( getProjectAlbumAssetsAndSubtype, 'deliverOneResult' ),
			getAlbumAssetIdFromAlbumIdAssetId = makeCatalogBasedObservableIntoRunUntilDoneSubModel( WFCatAlbums.getAlbumAssetIdFromAlbumIdAssetId , 'deliverOneResult' ),
			getAlbumIdAssetIdFromAlbumAssetId = makeCatalogBasedObservableIntoRunUntilDoneSubModel( WFCatAlbums.getAlbumIdAssetIdFromAlbumAssetId , 'deliverOneResult' ),
			getAlbumPrivateUrl = makeCatalogBasedObservableIntoRunUntilDoneSubModel( WFCatAlbums.getAlbumPrivateUrl , 'deliverOneResult' ),
			
			sharedAlbumUrl = makeCatalogBasedObservableIntoSubModel( WFCatCollaboration.sharedAlbumUrl ),
			getLrWebShareUrl = makeCatalogBasedObservableIntoSubModel( WFCatCollaboration.getLrWebShareUrl ),
			albumCover = makeCatalogBasedObservableIntoSubModel( WFCatAlbums.albumCover ),
			getSharedAlbumAttributes = makeCatalogBasedObservableIntoSubModel( WFCatCollaboration.getSharedAlbumAttributes ),
			setSharedAlbumAttributes = makeCatalogBasedObservableIntoSubModel( WFCatCollaboration.setSharedAlbumAttributes ),
	
			hasSharedAlbumsWithDisplayFilters = makeCatalogBasedObservableIntoSubModel( WFCatCollaboration.hasSharedAlbumsWithDisplayFilters ),
			deleteDisplayFiltersFromAlbum = makeCatalogBasedObservableIntoSubModel( WFCatCollaboration.deleteDisplayFiltersFromAlbum ),
			getCollaborators = makeCatalogBasedObservableIntoSubModel( WFCatCollaboration.getCollaborators ),
			inviteEmailAllowed = makeCatalogBasedObservableIntoSubModel( WFCatCollaboration.inviteEmailAllowed, 'deliverOneResult'  ),
			inviteByEmail = makeCatalogBasedObservableIntoSubModel( WFCatCollaboration.inviteByEmail ),
			getPendingRequests = makeCatalogBasedObservableIntoSubModel( WFCatCollaboration.getPendingRequests ),
			getInvitations = makeCatalogBasedObservableIntoSubModel( WFCatCollaboration.getInvitations ),
			updateCollaborators = makeCatalogBasedObservableIntoSubModel( WFCatCollaboration.updateCollaborators ),
			spaceNeededToContributeAssets = makeCatalogBasedObservableIntoSubModel( WFCatSharedWithYouUtils.spaceNeededToContributeAssets ),
	
			waitForAlbumSync = makeCatalogBasedObservableIntoSubModel( WFCatCollaboration.waitForAlbumSync ),
			
		},

	}

end

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

return LrClassicAlbums
