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

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

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

	-- Rx library
local Rx = require 'Rx'

	-- WF.core.rxlua
local WFRxScheduler = require 'WFRxScheduler'

	-- WF.core.utils
local KSIdGenerator = require 'KSIdGenerator'
local WFCatUtils = require 'WFCatUtils'
local WFTableMerge = require 'WFTableMerge'
local KSTableUtils = require 'KSTableUtils'
local WFArguments = require 'WFArguments'
local WFRxThrottledBatchedBulkApplyOverlay = require 'WFRxThrottledBatchedBulkApplyOverlay'

	-- WF.core.string
local WFSortOrderStrings = require 'WFSortOrderStrings'

	-- WF.lrmobile
local LrmCatAlbum = require 'LrmCatAlbum'

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

	-- WF.core.logging
local WFLogger = require 'WFLogger'

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

local Observable = Rx.Observable
local check = assert( WFArguments.check )
local logger = WFLogger 'LrmAlbumAssetUtils'
	-- :setLevel 'DEBUG'

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

local LrmAlbumAssetUtils = {}

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

function LrmAlbumAssetUtils.removeAssetsFromOwnAlbum( catalog, albumId, assetIds )

	check( 'WFCatalog', 'string', 'table' )
	
	local albumAssetsPresent = {}
	
	local function getAlbumAssetIds()
		
		local sql = [[SELECT docId, assetId FROM album_asset 
					WHERE albumId == ? AND assetId IN CARRAY( ?, ?, 'char*' )]]
		
		return catalog:indexStore():queryOnce( { 
			sql = sql,
			albumId,
			assetIds,
			#assetIds
		} )
		:map( function( rows )
		
			for i, row in pairs( rows ) do
				albumAssetsPresent[ row[ 2 ] ] = row[ 1 ]
			end
			
			return albumAssetsPresent
			
		end )

	end
	
	local function deleteAlbumAssetOverlays( assetIds )
	
		local overlays = {}
		local assetIdsToDeleteFromAlbum = {}
		
		for i, assetId in pairs( assetIds ) do
		
			local albumAssetId = albumAssetsPresent[ assetId ]
			
			if albumAssetId then
			
				local albumAssetOverlay = {
					_id = albumAssetId,
					type = 'album_asset',
					_deleted = true,
					_localOnly = {
						album = {
							id = albumId
						},
						asset = {
							id = assetId
						}
					}
				}
	
				overlays[ #overlays + 1 ] = albumAssetOverlay
				assetIdsToDeleteFromAlbum[ #assetIdsToDeleteFromAlbum + 1 ] = assetId
				
			end
			
		end

		return overlays, assetIdsToDeleteFromAlbum
		
	end
	
	return catalog:indexStore():queryOnce{
		sql = "SELECT docId FROM albums WHERE docId == ?", 
		singleColumnQuery = true,
		albumId
	}
	:flatMap( function( docIds )
	
		if docIds and docIds[ 1 ] then
		
			return getAlbumAssetIds()
			:flatMap( function()
				local albumAssetOverlays, assetIdsToDeleteFromAlbum = deleteAlbumAssetOverlays( assetIds )
				
				if #assetIdsToDeleteFromAlbum > 0 then
				
					return WFRxThrottledBatchedBulkApplyOverlay( "removeAssetsFromAlbum", catalog:docStore(), albumAssetOverlays, 100, 10 )
					:concat( catalog:docStore():docValue( albumId ) )
					:takeLast( 1 )
					
					:flatMap( function( albumDocValue )
					
						local coverId = KSTableUtils.derefOpt( albumDocValue, 'cover', 'id' )
			
						if coverId and albumAssetsPresent[ coverId ] then
						
							return catalog:docStore():applyOverlay{
								_id = albumId,
								type = 'album',
								cover = {
									_delete = true,
								}
							}
							
						else
							return Observable.just( true )
						end
						
					end )
					
					:flatMap( function()
					
						return LrmCatAlbum.touchAlbum( catalog, albumId )
						:map( function()
							return assetIdsToDeleteFromAlbum
						end )
						
					end )
				
				else
					logger:warnf( "LrmCatAlbumAsset.removeAssetsFromAlbum: None of the assets exists in album %s", albumId )
					return Observable.just( {} )
				end
				
			end )
		
		else
			logger:warnf( "LrmCatAlbumAsset.removeAssetsFromAlbum: albumId %s doesn't exist", albumId )
			return Rx.Observable.throw( 'invalidAlbum' )
		end
	
	end )
	
end

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

function LrmAlbumAssetUtils.getHighestOrderForAlbum( indexStore, albumId )

	return indexStore:queryOnce( {
		sql = [[SELECT assetId, sortOrder FROM album_asset WHERE albumId == ? ORDER BY sortOrder DESC LIMIT 1]],
		albumId
	} )
	:map( function( rows )
	
		local highestUserOrder = nil
		
		if rows and type( rows ) == 'table' and rows[ 1 ] then
			highestUserOrder = rows[ 1 ][ 2 ] or nil
		end
		
		return highestUserOrder
		
	end )
end

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

function LrmAlbumAssetUtils.calculateNewHighOrderForAssets( assetIds, currentHighOrder )

	local orders = {}
	
	if currentHighOrder then

		local formerOrder = currentHighOrder

		for i = 1, #assetIds do
			
			local assetId = assetIds[ i ]
			local newOrder = WFSortOrderStrings.insertAfter_lex64( formerOrder, 'low' )
			orders[ assetId ] = newOrder
			formerOrder = newOrder
			
		end
		
		return orders
		
	end
	
end

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

function LrmAlbumAssetUtils.calculateNewInsertOrderForAssets( assetIds, insertOrder, location )

	local orders = {}
	
	local formerOrder

	for i, assetId in pairs( assetIds ) do
	
		local newOrder 
		
		if formerOrder == nil then
		
			if location == "before" then
				
				newOrder = WFSortOrderStrings.insertBefore_lex64( insertOrder, 'high' )
				
			else
			
				newOrder = WFSortOrderStrings.insertAfter_lex64( insertOrder, 'low' )
			
			end
		
		else
		
			if location == 'before' then
			
				newOrder = WFSortOrderStrings.insertBetween_lex64( formerOrder, insertOrder )
				
			else
			
				newOrder = WFSortOrderStrings.insertBetween_lex64( insertOrder, formerOrder )
				
			end
		
		end
		
		orders[ assetId ] = newOrder
		formerOrder = newOrder
	end
		
	return orders
		
end

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

function LrmAlbumAssetUtils.addAssetsToOwnAlbum( catalog, albumId, assetIds, orders )

	check( 'WFCatalog', 'string', 'table', '?table' )
	
	local assetsPresent = {}
	
	local function checkForExistingAssets()
		
		local sql = [[SELECT assetId FROM album_asset 
					WHERE albumId == ? AND assetId IN CARRAY( ?, ?, 'char*' )]] 
		
		return catalog:indexStore():queryOnce( {
			sql = sql,
			singleColumnQuery = true,
			albumId,
			assetIds,
			#assetIds
		} )
		:map( function( assetIds )
			for i, assetId in pairs( assetIds ) do
				assetsPresent[ assetId ] = true
			end
			return assetsPresent
		end )

	end
	
	local function createAlbumAssetOverlays( assetIds )
		
		local overlays = {}
		local assetIdsToAddToAlbum = {}
		
		for i, assetId in pairs( assetIds ) do
		
			if not assetsPresent[ assetId ] then
			
				local baseDocValue = WFCatUtils.createBaseOverlayWithUserCreatedFields( KSIdGenerator.uuidInLowercaseHex() )
				
				local albumAssetOverlay = {
					type = 'album_asset',
					album = {
						id = albumId,
					},
					asset = {
						id = assetId,
					},
				}
				
				if orders then
					local order = orders[ assetId ]
					albumAssetOverlay[ "order" ] = order
				end
				
				WFTableMerge.applyOverlay( albumAssetOverlay, baseDocValue )
	
				overlays[ #overlays + 1 ] = albumAssetOverlay
				assetIdsToAddToAlbum[ #assetIdsToAddToAlbum + 1 ] = assetId
				
			end
		end
		
		return overlays, assetIdsToAddToAlbum
					
	end
	
	return catalog:indexStore():queryOnce{
		sql = [[SELECT docId FROM albums WHERE docId == ? ]],
		singleColumnQuery = true,
		albumId
	}
	
	:flatMap( function( docIds )

		if docIds and docIds[ 1 ] then
			
			return checkForExistingAssets()
			
			:flatMap( function()
			
				local albumAssetOverlays, assetIdsToAddToAlbum = createAlbumAssetOverlays( assetIds )
				
				if #assetIdsToAddToAlbum > 0 then
				
					return WFRxThrottledBatchedBulkApplyOverlay( "addAssetsToOwnAlbum", catalog:docStore(), albumAssetOverlays, 100, 10 )
					:concat( Rx.Observable.just( true ) )
					:takeLast( 1 )
					:flatMap( function()
						return LrmCatAlbum.touchAlbum( catalog, albumId )
						:map( function()
							return assetIdsToAddToAlbum
						end )
					end )
				
				else
					logger:warnf( "LrmAlbumAssetUtils.addAssetsToOwnAlbum: All assets already exist in album %s", albumId )
					return Rx.Observable.just( {} )
				end
			
			end )
			
		else
			logger:warnf( "LrmAlbumAssetUtils.addAssetsToOwnAlbum: albumId %s doesn't exist", albumId )
			return Rx.Observable.throw( 'invalidAlbum' )
		end
		
	end )
	
end

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

function LrmAlbumAssetUtils.addAssetsToNewAdhocAlbumAndShare( wfCatalog, albumName, assetIds, optShareAttributes )

	check( 'WFCatalog', '+string', 'table', '?table' )

	local parentId = nil
	local hidden = true

	return WFCatAlbums.createAlbumSynchronously( wfCatalog, albumName, parentId, hidden )
	
		:flatMap( function( _, albumId, _, didCreateWithOz )

			local maybeDelay = Observable.just( true )

			if not didCreateWithOz then
				-- Delay 2.5 seconds to wait for the album doc being generated on Oz
				maybeDelay = Observable.timer( 2.5, WFRxScheduler.sharedScheduler() ):take( 1 )
			end

			local addAssetsToOwnAlbumObservable = LrmAlbumAssetUtils.getHighestOrderForAlbum( wfCatalog:indexStore(), albumId )
				:flatMap( function( currentHighOrder )
					local orders = LrmAlbumAssetUtils.calculateNewHighOrderForAssets( assetIds, currentHighOrder )
					return LrmAlbumAssetUtils.addAssetsToOwnAlbum( wfCatalog, albumId, assetIds, orders )
				end )

			return Observable.combineLatest( maybeDelay, addAssetsToOwnAlbumObservable )
				
				:flatMap( function()
					return WFCatCollaboration.shareAlbum ( wfCatalog, albumId, optShareAttributes )
				end )

				:map( function()
					return albumId, didCreateWithOz
				end  )

		end )

end

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

return LrmAlbumAssetUtils
