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

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 Rx = require 'Rx'
local Observable = Rx.Observable

	-- WF.core.utils
local WFArguments = require 'WFArguments'
local WFCatUtils = require 'WFCatUtils'
local WFTableMerge = require 'WFTableMerge'
local WFRxRunUntilDone = require "WFRxRunUntilDone"

	-- WF.core.os
local KSDate = require 'KSDate'

	--WF.managed_catalog
local WFCatSharedWithYouAlbums = require 'WFCatSharedWithYouAlbums'
local WFCatSharedWithYouUtils = require "WFCatSharedWithYouUtils"
local WFCatCollaboration = require 'WFCatCollaboration'
local WFCatAlbums = require 'WFCatAlbums'
local WFCatCollaborationConstants = require 'WFCatCollaborationConstants'

--==============================================================================

local runUntilDone = WFRxRunUntilDone

--==============================================================================

local LrmCatAlbum = {}

--------------------------------------------------------------------------------
--[[!
	\brief Creates an album in this catalog.
	
	\param name (string) The name of the new album.
	\param parentId (string) The parentId of the new album
				(could be 'root' or 'empty' which is synonym for top level album)
	\param isFolder (boolean) The new album is of subtype collection_set
	\param hideInOrganize (boolean) The new album is an adhoc share
	\return <code>Observable< string ></code> An Observable that resolves to the ID of the
		newly-created album when the operation is complete.
]]--

function LrmCatAlbum.createAlbum( wfCatalog, name, parentId, isFolder, hideInOrganize )

	WFArguments.check( 'WFCatalog', 'string', 'string', 'boolean', 'boolean' )
	
	if not ( #name > 0 ) then
		error( "LrmCatAlbum:createAlbum: The album name must be a non-empty string", 2 )
	end

	if parentId == 'root' or parentId == 'empty' or parentId == '' then
		parentId = nil
	end

	local result

	if not isFolder then
		result = WFCatAlbums.createAlbum( wfCatalog, name, parentId, hideInOrganize )
	else
		result = WFCatAlbums.createFolder( wfCatalog, name, parentId )
	end

	return result
	:map( function( _, albumId )
		return albumId
	end )
	:call( runUntilDone )
		
end

--------------------------------------------------------------------------------
--[[!
	\brief Reparent an album in this catalog.

	\param albumId (string) The albumId of the album to reparent.
	\param newParentId (string) The new parentId

	\return <code>Observable< string ></code> A n Observable that resolves to true when the operation is complete.
]]--

function LrmCatAlbum.reparentAlbum( wfCatalog, albumId, newParentId )

	WFArguments.check( 'WFCatalog', 'string', 'string' )

	if not ( #albumId > 0 ) then
		error( "LrmCatAlbum.reparentAlbum: The albumId must be a non-empty string", 2 )
	end

	if newParentId ~= albumId then
	
		local overlay = WFCatUtils.createBaseOverlayWithUserUpdatedField( albumId )
		
		overlay.type = 'album'
		overlay.parent = {
			id = newParentId
		}

		if not (#newParentId > 0) or newParentId == 'root' then
			overlay.parent = {
				_delete = true
			}
		end

		return wfCatalog:docStore():applyOverlay( overlay )
			:map( function()
				return true
			end )
			:call( runUntilDone )

	else

		return Rx.Observable.throw( "Album can not be its own parent" )

	end

end

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

--[[!
	\brief Is an albumId valid for this catalog
	
	\param albumId (string) The unique ID of the album to modify.
	
	\return <code>Observable< boolean ></code> An Observable object that resolves to true
		if the album id exists and has not been deleted for this catalog.
]]--

function LrmCatAlbum.isValidAlbum( wfCatalog, albumId )

	WFArguments.check( 'WFCatalog', 'string' )
	
	return wfCatalog:indexStore():queryOnce{ 
			sql = [[ SELECT 1 FROM albums WHERE docId = ? LIMIT 1 ]], 
			albumId }
		:map( function( albumList )
			return not not albumList[ 1 ]
		end )
	
end

--------------------------------------------------------------------------------
--[[!
	\brief Renames an album in this catalog.
	
	It is an error to rename an album to match the name of an existing album,
	regardless of case.
	
	\param albumId (string) The unique ID of the album to rename.
	\param name (string) The new name for the album.
	
	\return <code>Observable< true ></code> An Observable that resolves to \c true
		if the request succeeded.
]]--

function LrmCatAlbum.renameAlbum( wfCatalog, albumId, name )

	WFArguments.check( 'WFCatalog', 'string', 'string' )
	
	if not ( #albumId > 0 ) then
		error( "LrmCatAlbum:renameAlbum: The albumId must be a non-empty string", 2 )
	end
	
	if not ( #name > 0 ) then
		error( "LrmCatAlbum:renameAlbum: The album name must be a non-empty string", 2 )
	end
	
	local baseDocValue = WFCatUtils.createBaseOverlayWithUserUpdatedField( albumId )

	local albumDocValue = {
		type = 'album',
		name = name,
		_localOnly = {
			updateTimeStamp = KSDate.timeInIso8601Format( nil, false ),
		}
	}

	WFTableMerge.applyOverlay( albumDocValue, baseDocValue )

	return wfCatalog:docStore():applyOverlay( albumDocValue )
		:map( function()
			return true
		end )
		:call( runUntilDone )

end

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

--[[!
	\brief Unshares an album in this catalog, deleting the associated space
		and space_album documents.

	\param albumId (string) The unique ID of the album.

	\return <code>Observable</code> An Observable that completes
		if the request succeeded.
]]--

function LrmCatAlbum.unshareAlbum( wfCatalog, albumId )

	WFArguments.check( 'WFCatalog', 'string' )

	return WFCatCollaboration.deleteSharedAlbum( wfCatalog, albumId )

end

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

function LrmCatAlbum.shareGroupAlbum( wfCatalog, albumId, attributes, optForceNoOz )

	WFArguments.check( 'WFCatalog' )

	return WFCatCollaboration.shareAlbum ( wfCatalog, albumId, attributes, false, optForceNoOz)
		:call( runUntilDone )

end

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

function LrmCatAlbum.getSharedAlbumAttributes( wfCatalog, albumId)


    return WFCatCollaboration.getSharedAlbumAttributes( wfCatalog, albumId )

end

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

function LrmCatAlbum.stopSharing( wfCatalog, albumId)

	return WFCatCollaboration.unshareAlbum( wfCatalog, albumId )

end

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

function LrmCatAlbum.setSharedAlbumAttributes( wfCatalog, albumId, attributes )

	return WFCatCollaboration.setSharedAlbumAttributes( wfCatalog, albumId, attributes )

end

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

function LrmCatAlbum.getCollaborators( wfCatalog, albumId )

	return WFCatCollaboration.getCollaborators( wfCatalog, albumId )

end

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

function LrmCatAlbum.getInvites( wfCatalog, albumId )

	return WFCatCollaboration.getInvitations( wfCatalog, albumId )

end

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

function LrmCatAlbum.getAccessRequests( wfCatalog, albumId )

	return WFCatCollaboration.getPendingRequests( wfCatalog, albumId )

end

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

function LrmCatAlbum.updateCollaborator( wfCatalog, update  )

    local updates = { update }
	return WFCatCollaboration.updateCollaborators( wfCatalog, updates )

end

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

function LrmCatAlbum.hasSharedAlbumsWithDisplayFilters( wfCatalog )

	return WFCatCollaboration.hasSharedAlbumsWithDisplayFilters( wfCatalog )

end

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

function LrmCatAlbum.getMigratedToUnfilteredAlbumId( wfCatalog, albumId )

	return WFCatCollaboration.getMigratedToUnfilteredAlbumId( wfCatalog, albumId )

end

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

function LrmCatAlbum.leaveSpace( wfCatalog, spaceId )

	return WFCatSharedWithYouAlbums.leaveSpace( wfCatalog, spaceId )

end

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

function LrmCatAlbum.reportAbuse( wfCatalog, spaceId, albumId )

	return WFCatSharedWithYouAlbums.reportAbuse( wfCatalog, spaceId, albumId )

end

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

function LrmCatAlbum.canEditAsset( docValue )

	return WFCatSharedWithYouUtils.canEditAsset( docValue )

end

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

function LrmCatAlbum.canDeleteAsset( docValue )

	return WFCatSharedWithYouUtils.canDeleteAsset( docValue )

end

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

function LrmCatAlbum.deleteAssets( wfCatalog, spaceId, assetIds )

	return WFCatSharedWithYouAlbums.deleteAssets( wfCatalog, spaceId, assetIds )

end

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

function LrmCatAlbum.canDeleteAssetIds( wfCatalog, arrayOfDocIds )

	return WFCatSharedWithYouUtils.canDeleteAssetIds( wfCatalog, arrayOfDocIds )

end

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

function LrmCatAlbum.inviteEmailAllowed( wfCatalog, email )

	return WFCatCollaboration.inviteEmailAllowed( wfCatalog, email )

end

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

function LrmCatAlbum.inviteByEmail( wfCatalog, albumId, email ,optAccessLevel)

	return WFCatCollaboration.inviteByEmail( wfCatalog, albumId, email,optAccessLevel )

end

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

function LrmCatAlbum.getAllCollaborators( wfCatalog  )

	return require 'WFCatCollaborator'.getAllCollaborators( wfCatalog )

end

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

function LrmCatAlbum.deleteDisplayFilters( wfCatalog , albumId )

	return WFCatCollaboration.deleteDisplayFiltersFromAlbum( wfCatalog , albumId )

end

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

local function intToBool( zeroOrOne )
	return zeroOrOne ~= 0
end

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

function LrmCatAlbum.getAlbumsList( wfCatalog, includeAssetCount )

	local query = [[SELECT a.docId, a.name, a.nameLC, a.useOffline, a.updateTimeStamp,
		a.localTimeStamp, a.coverId, a.parentId, a.subtype, a.hideInOrganize,
		MAX( ifnull(lastFavorite, ""), ifnull(lastComment, "") ), 
		a.spaceId, sa.created, s.download, s.showMetadata, s.showLocation, s.allowComments, s.allowFavorites,
		su.userId, su.role, su.created, title, s.private, sa.spaceId ]]
		
	if includeAssetCount then
		query = query .. [[ , albumAssetCount ]]
	end

	query = query .. [[ FROM albums a
							
							LEFT JOIN (
								SELECT sa1.*
								FROM space_album sa1
								WHERE sa1.spaceId IN ( SELECT docId FROM space )
								AND COALESCE( sa1.created, '0000-00-00T00:00:00' ) = (
									SELECT COALESCE( MAX( sa2.created ), '0000-00-00T00:00:00')
									FROM space_album sa2
									WHERE sa2.albumId = sa1.albumId
								)
							) AS sa ON sa.albumId = a.docId
							
							LEFT JOIN space s ON s.docId = a.spaceId
							LEFT JOIN space_user su ON s.spaceUserId = su.fullDocId ]]

	if includeAssetCount then
		query = query .. [[ LEFT OUTER JOIN ( SELECT albumId as aId, count(albumId) AS albumAssetCount 
											FROM album_asset WHERE album_asset.assetId IN (SELECT coreInfo.docId FROM coreInfo) 
											GROUP BY albumId ) 
							ON a.docId = aId ]]
	end
	
	query = query .. [[ LEFT JOIN (SELECT spaceId, MAX(created) AS lastFavorite FROM spacefavorite GROUP BY spaceId) sf ON sa.spaceId == sf.spaceId ]]
	query = query .. [[ LEFT JOIN (SELECT spaceId, MAX(created) AS lastComment FROM comment AS c GROUP BY spaceId) c ON sa.spaceId == c.spaceId ]]

	local sharingStatusObservable = WFCatCollaboration.getSharingStatusByAlbumId( wfCatalog )
	
	local ownerInfoByAlbumIdObservable = WFCatCollaboration.getInfoOfAllAlbumOwnersByAlbumId( wfCatalog )
	
	local albumInfoObservable = wfCatalog:indexStore():query(
		{
			sql = query,
			calm = 2,
		})

	return Observable.combineLatest( 
			albumInfoObservable, 
			sharingStatusObservable,
			ownerInfoByAlbumIdObservable )
			
		:map(function( rows, sharingStatusByAlbumId, ownerInfoByAlbumId )
		
			local result = {}
			
			for _, row in ipairs( rows ) do
	
				-- Note on spaceId: row[ 12 ] contains the spaceId, but only for SWY Albums.
				-- For non SWY albums, the spaceId is stored within row[ 24 ].

				local albumId = row[ 1 ]
				local spaceIdForSWYAlbum = row[ 12 ]

				local isShared = sharingStatusByAlbumId[ albumId ]
					and sharingStatusByAlbumId[ albumId ].isShared or false

				local isPrivate = sharingStatusByAlbumId[ albumId ]
					and sharingStatusByAlbumId[ albumId ].isPrivate or false

				if spaceIdForSWYAlbum then

					-- sharingStatusByAlbumId does not consider SWY albums,
					-- so we must add additional logic.

					isPrivate = intToBool( row[ 23 ] )

				end

				local album = {
					albumId = albumId,
					name = row[ 2 ],
					nameLC = row[ 3 ],
					useOffline = row[ 4 ],
					updateTimestamp = row[ 5 ] or "",
					localTimestamp = row[ 6 ] or "",
					coverId = row[ 7 ] or "",
					parentId = row[ 8 ],
					subType = row[ 9 ],
					hideInOrganize = row[ 10 ] or 0.0,
					lastActivityTimestamp = row[ 11 ] or '',
					isShared = isShared,
					isPrivate = isPrivate,
					spaceId = spaceIdForSWYAlbum, -- The spaceId field is only set for SWY albums for historical reasons.
					shareCreated = row[ 13 ],
					title = row[ 22 ],
					assetCount = row[ 25 ] or 0,
					globalSpaceId = spaceIdForSWYAlbum or row[ 24 ] -- This holds the spaceId for SWY and non-SWY shared albums.
				}

				if spaceIdForSWYAlbum then

					album.shareSettings = {
						allowDownloads = intToBool( row[ 14 ] ) or false,
						showMetadata = intToBool( row[ 15 ] ) or false,
						showLocation = intToBool( row[ 16 ] ) or false,
						allowComments = intToBool( row[ 17 ] ) or false,
						allowFavorites = intToBool( row[ 18 ] ) or false
					}
					local role = row[ 20 ]
					album.permissions = {
						role = role
					}
					album.ownerInfo = ownerInfoByAlbumId[ albumId ]
					
					album.spaceFeedbackUserId = row[ 19 ]
					
					album.dateJoined = row[ 21 ]
				
				end
				
				local role = row[ 20 ]
				-- Hide spaces from SWY list if user role is unknown
				if not spaceIdForSWYAlbum or ( role and WFCatCollaborationConstants.kRoles[ role ] ) then
					result[ #result + 1 ] = album
				end	

			end
	
			return result
			
		  end)

end

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

function LrmCatAlbum.deleteAlbum( wfCatalog, albumId )

	WFArguments.check( 'WFCatalog', 'string' )

	if not ( #albumId > 0 ) then
		error( "LrmCatAlbum.deleteAlbum: The albumId must be a non-empty string", 2 )
	end

	-- Remove any album share first, then delete the album, too.
	-- Regardless of success/failure of unsharing the album, we're still going
	-- to delete it, hence the catch()

	return LrmCatAlbum.unshareAlbum( wfCatalog, albumId )
		:catch( Rx.Observable.empty() )
		:concat( Rx.Observable.just( true ) )
		:takeLast( 1 )
		:flatMapLatest( function()

			return wfCatalog:docStore():deleteDoc( albumId )

		end )
		:call( runUntilDone )

end

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

function LrmCatAlbum.touchAlbum( wfCatalog, albumId )

	WFArguments.check( 'WFCatalog', 'string' )
	
	if not ( #albumId > 0 ) then
		error( "LrmCatAlbum.touchAlbum: The albumId must be a non-empty string", 2 )
	end
	
	local baseDocValue = WFCatUtils.createBaseOverlayWithUserUpdatedField( albumId )

	local albumDocValue = {
		type = 'album',
		_localOnly = {
			updateTimeStamp = KSDate.timeInIso8601Format( nil, false ),
		}
	}

	WFTableMerge.applyOverlay( albumDocValue, baseDocValue )

	return wfCatalog:docStore():applyOverlay( albumDocValue )
		:call( runUntilDone )
end

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

function LrmCatAlbum.convertAlbumToAdhoc( wfCatalog, albumId )
	WFArguments.check( 'WFCatalog', 'string' )

	if not ( #albumId > 0 ) then
		error( "LrmCatAlbum:convertAlbumToAdhoc: The albumId must be a non-empty string", 2 )
	end

	local result = {}
	local overlay = {
		_id = albumId,
		hideInOrganize = true,
		parent = {
			_delete = true
		},
		_localOnly = {
			updateTimeStamp = KSDate.timeInIso8601Format( nil, false ),
		}
	}

	result = wfCatalog:docStore():applyOverlay( overlay )

	return {
		value = result
	}

end

function LrmCatAlbum.convertAdhocToAlbum( wfCatalog, albumId )
	WFArguments.check( 'WFCatalog', 'string' )

	if not ( #albumId > 0 ) then
		error( "LrmCatAlbum:convertAdhocToAlbum: The albumId must be a non-empty string", 2 )
	end

	local result = {}
	local overlay = {
		_id = albumId,
		hideInOrganize = false,
		_localOnly = {
			updateTimeStamp = KSDate.timeInIso8601Format( nil, false ),
		}
	}

	result = wfCatalog:docStore():applyOverlay( overlay )

	return {
		value = result
	}

end

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

return LrmCatAlbum
