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

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 KSIdGenerator = require 'KSIdGenerator'
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 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
	
	local albumId = KSIdGenerator.uuidInLowercaseHex()
	local updateTimeStamp = KSDate.timeInIso8601Format( nil, false )
	local subtype = isFolder and 'collection_set' or 'collection'

	local baseDocValue = WFCatUtils.createBaseOverlayWithUserCreatedFields( albumId )

	local albumDocValue = {
		type = 'album',
		subtype = subtype,
		name = name,
		_localOnly = {
			updateTimeStamp = updateTimeStamp,
		}
	}

	albumDocValue.hideInOrganize = hideInOrganize

	if #parentId > 0 and parentId ~= 'root' then
		albumDocValue.parent = {
			id = parentId
		}
	end
	
	WFTableMerge.applyOverlay( albumDocValue, baseDocValue )

	return wfCatalog:docStore():applyOverlay( albumDocValue )
		:map( function()
			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.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 ]]
		
	if includeAssetCount then
		query = query .. [[ , albumAssetCount ]]
	end

	query = query .. [[ FROM albums a 
							LEFT JOIN ( SELECT * FROM space_album 
										WHERE spaceId IN ( SELECT docId FROM space ) ) 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
			
				local albumId = row[ 1 ]
				local spaceId = row[ 12 ]
				
				local isShared = sharingStatusByAlbumId[ albumId ]
					and sharingStatusByAlbumId[ albumId ].isShared or false
				
				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,
					spaceId = spaceId,
					shareCreated = row[ 13 ],
					title = row[ 22 ],
					assetCount = row[ 23 ] or 0,
				}
				
				if spaceId 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 ]
					
					role = WFCatCollaboration.downgradeRoleIfNotSupported( role )
					
					album.permissions = {
						role = role
					}
				
					album.ownerInfo = ownerInfoByAlbumId[ albumId ]
					
					album.spaceFeedbackUserId = row[ 19 ]
					
					album.dateJoined = row[ 21 ]
				
				end
				
				result[ #result + 1 ] = album
				
			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
