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

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.

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

	-- Rx library
local Rx = require 'Rx'

local WFRxRunUntilDone = require "WFRxRunUntilDone"

	-- WF.core.utils
local WFArguments = require 'WFArguments'
local KSDate = require 'KSDate'
local WFPlatformInfo = require 'WFPlatformInfo'

	--WF.managed_catalog
local WFCatSharedWithYouAlbums = require 'WFCatSharedWithYouAlbums'
local LrmAlbumAssetUtils = require 'LrmAlbumAssetUtils'

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

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

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

local kSharedWithYou = 'sharedWithYou'
local kOwnedByYou = 'ownedByYou'

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

local LrmCatAlbumAsset = {}

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

--------------------------------------------------------------------------------
-- @mark Remove assets from album
--------------------------------------------------------------------------------

--[[!
	\brief Remove assets from a space album or album in this catalog. 
	
	\param albumId (string) The unique ID of the album to modify.
	\param assetIds (array of strings) An array of unique IDs for the assets to delete.
	
	\return Observable ( array )
		An observable of the list of asset IDs that were actually removed from the album.
]]--

function LrmCatAlbumAsset.removeAssetsFromAlbum( catalog, albumId, assetIds )

	check( 'WFCatalog', 'string', 'table' )
	
	return WFCatSharedWithYouAlbums.getSpaceIdOfAlbumId( catalog, albumId )
			
		:flatMap( function( spaceId )
		
			if spaceId then
				return WFCatSharedWithYouAlbums.deleteAssets( catalog, spaceId, assetIds )
			end
			
			return LrmAlbumAssetUtils.removeAssetsFromOwnAlbum( catalog, albumId, assetIds )
				
		end )
	
end

--------------------------------------------------------------------------------
-- @mark Add assets to album
--------------------------------------------------------------------------------

--[[!
	\brief Adds assets to an album in this catalog.
	
	\param albumId (string) The unique ID of the album to modify.
	\param assetIds (array of strings) An array of unique IDs for the assets to add.
	\param orders (map of order per assetid) Map containing order for each asset id.
	\param maybeRedactionOptions (map) Map containing the values of redaction options.
			Valid only if the albumId belongs to another owner's space.
	
	\return Observable ( array, array, string )
		An observable that fires with 3 values.
		The first value is the list of asset IDs that were actually added to the album.
		The second value is an array of duplicate IDs that were already in the album
			(this is only set for shared-with-you albums).
		The last value is a string indicating if the album was owned by you, or shared
			with you.
		
]]--

function LrmCatAlbumAsset.addAssetsToAlbum( catalogService, albumId, assetIds, orders, maybeRedactionOptions )

	check( 'OzCatalogService', 'string', 'table', '?table', '?table' )

	logger:debugf( "LrmCatAlbumAsset.addAssetsToAlbum: maybeRedactionOptions=%s", maybeRedactionOptions )
	
	local copyRenditionsFn = nil

	if WFPlatformInfo.isAndroid then

		copyRenditionsFn  = function( renditionInfo, docId )

			local md5 = renditionInfo and renditionInfo.renditionInfo and renditionInfo.renditionInfo.md5

			if not md5 or not docId then

				return Observable.just( nil )

			end

			return catalogService:getDataCache()
			
			:flatMap( function( dataCache )
			
				assert( dataCache )
				
				return dataCache:get( md5 )
				
				:flatMap( function( data )
					local original_md5 = data:digest( 'MD5' )
					local new_md5 = original_md5 .. "_" .. docId
					
					return dataCache:put( new_md5, data )
					
					:map( function ()
						renditionInfo.renditionInfo.md5 = new_md5
						return renditionInfo
					end)
					
				end )
				
			end)
			
			:catch( function()
				return Observable.just( renditionInfo )
			end )

		end

	end

	return catalogService:getWfCatalog()
	
		:flatMap( function( wfCatalog )
		
			return WFCatSharedWithYouAlbums.getSpaceIdOfAlbumId( wfCatalog, albumId )
	
				:flatMap( function( spaceId )
				
					logger:debugf( "LrmCatAlbumAsset.addAssetsToAlbum: spaceId=%s", spaceId )
				
					if spaceId then
					
						return WFCatSharedWithYouAlbums.addAssetsToAlbum( wfCatalog, spaceId, albumId, assetIds, maybeRedactionOptions, copyRenditionsFn )
							:map( function( assetIdsAddedToAlbum, duplicateAssetIds )
							
								return assetIdsAddedToAlbum, duplicateAssetIds, kSharedWithYou
							
							end )
					end
					
					return LrmAlbumAssetUtils.addAssetsToOwnAlbum( wfCatalog, albumId, assetIds, orders )
					
						:map( function( assetIdsAddedToAlbum )
							
							return assetIdsAddedToAlbum, nil, kOwnedByYou
						
						end )
					
				
				end )
		
		end )

	
end

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

function LrmCatAlbumAsset.addAssetsToAlbumWithOrder( catalogService, albumId, assetIds, maybeRedactionOptions )

	check( 'OzCatalogService', 'string', 'table', '?table' )

	return LrmAlbumAssetUtils.getHighestOrderForAlbum( catalogService:indexStore(), albumId )
	
	:flatMap( function( currentHighOrder )
	
		local orders = LrmAlbumAssetUtils.calculateNewHighOrderForAssets( assetIds, currentHighOrder )
		
		return LrmCatAlbumAsset.addAssetsToAlbum( catalogService, albumId, assetIds, orders, maybeRedactionOptions )

	end )
	
end

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

function LrmCatAlbumAsset.addAssetsToAlbumWithCustomOrder( catalogService, albumId, assetIds, insertOrder, location, maybeRedactionOptions )
	
	check( 'OzCatalogService', 'string', 'table', 'string', 'string', '?table' )
	
	local orders = LrmAlbumAssetUtils.calculateNewInsertOrderForAssets( assetIds, insertOrder, location )
	
	return LrmCatAlbumAsset.addAssetsToAlbum( catalogService, albumId, assetIds, orders, maybeRedactionOptions )
	
end

--------------------------------------------------------------------------------
-- @mark Move assets from album to another album
--------------------------------------------------------------------------------

--[[!
	\brief Move assets from one album to another album in this catalog.
	
	\param fromAlbumId (string) The unique ID of the album to move assets from.
	\param toAlbumId (string) The unique ID of the album to move assets into.
	\param assetIds (array of strings) An array of unique IDs for the assets to add.
	
	\return An observable of the list of asset IDs that were actually moved to the album.
		
]]--

function LrmCatAlbumAsset.moveAssetsToAlbum( catalogService, fromAlbumId, toAlbumId, assetIds )
	
	check( 'OzCatalogService', 'string', 'string', 'table' )
	
	return catalogService:getWfCatalog()
	
	:flatMap( function( wfCatalog )
		return LrmAlbumAssetUtils.removeAssetsFromOwnAlbum( wfCatalog, fromAlbumId, assetIds )
	end )
	
	:flatMap( function()
		return LrmCatAlbumAsset.addAssetsToAlbumWithOrder( catalogService, toAlbumId, assetIds )
	end )
	
end

--------------------------------------------------------------------------------
-- @mark Update assets ordering
--------------------------------------------------------------------------------

local function createIndex( array )
	local index = { }
	for i, asset in ipairs( array ) do
		index[ asset ] = i
	end
	
	return index
end

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

local function createMarkedAssetArray( assets, touched )
	local markedAssets = { }
	
	for i, assetId in ipairs( assets ) do
		markedAssets[ i ] = {
			assetId = assetId,
			touched = not not touched[ assetId ] -- toboolean
		}
	end
	
	return markedAssets
end

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

--[[!
	\brief Apply an ordering to a set of assets in the album.
	
	All assets whose ids are in the list will have a persisted ordering when this
	is complete. Any members of the album not in the list will be untouched.  The
	optional set of "touched" ids will be used as a hint to avoid unnecessary
	changes to the order of other items.
	
	Assets in the list that turn out to not actually be in the album for any
	reason (either they aren't really in there or have been deleted since the list
	was generated) will be ignored 	rather than interfere with the persistence of
	other ordering adjustments.
	
	If the assets are already in the specified order, no changes be made even if
	they appear in the touched id set (i.e., superfluous touched id entries should
	not trigger unnecessary changes).
	
	If changes are required, this routine will prefer to modify the order attribute
	of items that appear in the touched id set.
	
	\param albumId (string) The unique ID of the album.
	\param assetArray (table) An array of asset id strings in the desired order.
	\param optTouchedIds (table) a set of "touched" assets (asset id string keys)
	
	\return <code>Observable< true ></code> An indication that the order was saved.
 ]]--

function LrmCatAlbumAsset.updateAssetOrdering( catalog, albumId, assetArray, optTouchedIds )

	return catalog:indexStore():queryOnce{ 
			sql = [[SELECT assetId, docId, sortOrder
					FROM album_asset WHERE albumId = ?]], 
			albumId }
		:flatMap( function( results )
		
			local assetIdToIndex = createIndex( assetArray )
			local markedAssetArray = createMarkedAssetArray( assetArray, optTouchedIds or { } )
		
			for i, result in ipairs( results ) do
				local index = assetIdToIndex[ result[ 1 ] ]
				if index then
					markedAssetArray[ index ].albumAssetId = result[ 2 ]
					markedAssetArray[ index ].order = result[ 3 ]
				end
			end
			
			local WFAlbumOrdering = require 'WFAlbumOrdering'

			local updates = WFAlbumOrdering.adjustOrderFields( markedAssetArray )
			
			local overlays = { }
			
			for i, update in pairs( updates ) do
				local docId = markedAssetArray[ i ].albumAssetId
				
				if docId then
					overlays[ #overlays + 1 ] = {
						_id = docId,
						type = 'album_asset',
						order = update,
						userUpdated = KSDate.timeInIso8601Format()
					}
				end
			end

			if #overlays > 0 then
				return catalog:docStore():bulkApplyOverlay( overlays )
					:map( function()
						return true
					end )
			else
				return Rx.Observable.just( true )
			end
		end )
		:call( WFRxRunUntilDone )

end

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

--[[!
	\brief Given an albumName, creates a adhoc album with the assetIds, then shares
		the album.
	
	Would try to create adhoc album synchronously with Oz. If fail, would fallback
	to create album locally.

	If the album is created locally, we delay 2.5 seconds to allow Oz to create 
	album before it's ready to share.
	
	\param albumName (string) The album name for the adhoc album.
	\param assetIds (table) An array of asset id to be added to adhoc album and share.
	\param optShareAttributes (optional table) Share attributes.
	
	\return <code>Observable< albumId, didCreateWithOz ></code> AlbumId of the album created, and if the album is successfully created on Oz.
 ]]--

function LrmCatAlbumAsset.addAssetsToNewAdhocAlbumAndShare( catalogService, albumName, assetIds, optShareAttributes )

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

	return catalogService:getWfCatalog()
	
		:flatMap( function( wfCatalog )

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

		end )

end
--------------------------------------------------------------------------------

return LrmCatAlbumAsset
