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

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.

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

local Rx = require "Rx"

	-- WF.core.utils
local WFRxRunUntilDone = require 'WFRxRunUntilDone'

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

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

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

local logger = WFLogger 'LrmLibraryAlbumAssetModelImp'
	-- :setLevel 'DEBUG'

local kSharedWithYou = 'sharedWithYou'

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

local LrmLibraryAlbumAssetModelImp = {}

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

local function returnKSReactiveErrorEvent( data )

	return Rx.Observable.just( {
		state = false,
		error = data
	} )

end

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

function LrmLibraryAlbumAssetModelImp:removeAssetsFromAlbum( albumId, assetIds )

	local result = self._wfCatalogStream
		
		:flatMap( function( wfCatalog )
		
			return LrmCatAlbumAsset.removeAssetsFromAlbum( wfCatalog, albumId, assetIds )

		end )
		
		:map( function()
			return { state = true }
		end )
		
		:catch( returnKSReactiveErrorEvent )
		
		:call( WFRxRunUntilDone )
	
	return {
		value = result,
	}
	
end

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

function LrmLibraryAlbumAssetModelImp:addAssetsToAlbum( albumId, assetIds, maybeRedactionOptions )

	logger:debugf( "LrmLibraryAlbumAssetModelImp:addAssetsToAlbum: maybeRedactionOptions=%s", maybeRedactionOptions )

	local result = LrmCatAlbumAsset.addAssetsToAlbumWithOrder( self.catalog, albumId, assetIds, maybeRedactionOptions )
		
		:map( function( _, maybeDuplicateAssetIds, maybeAlbumType )
		
			if maybeAlbumType == kSharedWithYou
				and maybeDuplicateAssetIds 
				and next( maybeDuplicateAssetIds ) then
				
				return { state = false, assetIds = maybeDuplicateAssetIds }
				
			else
			
				return { state = true }
				
			end
			
		end )
		
		:catch( returnKSReactiveErrorEvent )
		
		:call( WFRxRunUntilDone )

	return {
		value = result,
	}
	
end

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

function LrmLibraryAlbumAssetModelImp:moveAssetsToAlbum( fromAlbumId, toAlbumId, assetIds )

	local result = LrmCatAlbumAsset.moveAssetsToAlbum( self.catalog, fromAlbumId, toAlbumId, assetIds )
		
		:map( function()
			return { state = true }
		end )
		
		:catch( returnKSReactiveErrorEvent )
		
		:call( WFRxRunUntilDone )

	return {
		value = result
	}

end

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

function LrmLibraryAlbumAssetModelImp:makeAlbumFilterCountModel( albumId, userId, isSearching )

	local docType = LrmAlbumUtils.getDocTypeForAlbumId(albumId)
	local result

	if isSearching then
		
		local sqlQuery = [[SELECT filterType, filterCount FROM TEMP_searchFilterCount]]
		result = self.catalog:indexStore():query{ sql = sqlQuery }
		
	else
		
		local sqlQuery = [[SELECT flag, COUNT(coreInfo.docId) FROM coreInfo ]]
		local params = {}
		
		if docType ~= 'gen-album' then
			sqlQuery = sqlQuery .. [[INNER JOIN album_asset ON album_asset.assetId == coreInfo.docId
									WHERE album_asset.albumId == ?]]
			table.insert( params, albumId )
		end

		sqlQuery = sqlQuery .. [[ GROUP BY coreInfo.flag ]]
		result = self.catalog:indexStore():query
		{ 
			sql 	= sqlQuery, 
			calm 	= .125,
			nArgs 	= #params,
			unpack( params )
		}
		
	end
	
	
	local mappedResult = result

		:map( function( rows )
			
			local unflaggedCount = 0
			local pickCount = 0
			local rejectCount = 0
			
			for i=1, #rows do
			
				local row = rows[i]
			
				if row[1] == 'unflagged' then	
					unflaggedCount = row[2]
			  	elseif row[1] == 'pick' then
					pickCount = row[2]
			  	elseif row[1] == 'reject' then	
					rejectCount = row[2]
			  	end
				
			end
			
			return {
				unflaggedCount = unflaggedCount,
				pickCount = pickCount,
				rejectCount = rejectCount
			}
			
		end )
		
	return {
		value = mappedResult
	}
	
end

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

function LrmLibraryAlbumAssetModelImp:makeAlbumRatingCountModel( albumId, isSearching, rating, compop )

	local docType = LrmAlbumUtils.getDocTypeForAlbumId(albumId)
	local result

	if isSearching then
	
		local sqlQuery = [[SELECT filterCount FROM TEMP_searchFilterCount WHERE filterType == ?]]
		result = self.catalog:indexStore():query{ 
				sql = sqlQuery, 
				calm = .125,
				singleColumnQuery = true,
				'rating' }

	else
	
		local sqlQuery = [[SELECT count(coreInfo.rating) FROM coreInfo ]]
		local params = {}
		
		if docType == 'gen-album' then
		    sqlQuery = sqlQuery .. [[WHERE rating ]] .. compop .. [[ ?]]
		else
		    sqlQuery = sqlQuery .. [[INNER JOIN album_asset ON album_asset.assetId == coreInfo.docId
									WHERE album_asset.albumId == ? AND rating ]] .. compop .. [[ ?]]
			table.insert( params, albumId )
		end
		
		table.insert( params, rating )
		
		result = self.catalog:indexStore():query
		{ 
			sql 	= sqlQuery, 
			calm 	= .125,
			nArgs 	= #params,
			singleColumnQuery = true,
			unpack( params )
		}
		
	end

		
	return {
		value = result
				:pluck( 1 )
	}
	
end

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

function LrmLibraryAlbumAssetModelImp:makeAlbumAssetTypeCountModel( albumId, isSearching )

	local docType = LrmAlbumUtils.getDocTypeForAlbumId(albumId)
	local result
	
	if isSearching then
	
		local sqlQuery = [[SELECT filterType, filterCount FROM TEMP_searchFilterCount]]
		result = self.catalog:indexStore():query{ sql = sqlQuery }
	
	else
	
		local sqlQuery = [[SELECT 
							(case 
								when coreInfo.isVideo = 0 then 'image'
	             				when coreInfo.isVideo = 1 then 'video'
	        				end), count(coreInfo.docId) FROM coreInfo ]]
		local params = {}
		
		if docType == 'gen-album' then
		    sqlQuery = sqlQuery .. [[GROUP BY coreInfo.isVideo]]
		else
		    sqlQuery = sqlQuery .. [[INNER JOIN album_asset ON album_asset.assetId == coreInfo.docId
									WHERE album_asset.albumId == ?
									GROUP BY coreInfo.isVideo]]
			table.insert( params, albumId )
		end
		
		result = self.catalog:indexStore():query
		{ 
			sql 	= sqlQuery,
			calm 	= .125,
			nArgs 	= #params,
			unpack( params )
		}
		
	end
	
	local mappedResult = result

		:map( function( rows )
					
			local imageCount = 0
			local videoCount = 0
	
			for i=1, #rows do
				local row = rows[i]
	
				if row[1] == 'image' then	
					imageCount = row[2]
	  			elseif row[1] == 'video' then
					videoCount = row[2]
	  			end
			end
	
			return {
				imageCount = imageCount,
				videoCount = videoCount
			}
			
		end )
		
	return {
		value = mappedResult
	}
	
end

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

function LrmLibraryAlbumAssetModelImp:updateAssetOrdering( albumId, assetIds )

	local result = self._wfCatalogStream
		:flatMap( function( wfCatalog )

			return LrmCatAlbumAsset.updateAssetOrdering( wfCatalog, albumId, assetIds, assetIds )
				:flatMap( function()

					return LrmCatAlbum.touchAlbum( wfCatalog, albumId )
						:map(function()
							return { state = true }
						end )
						:call( WFRxRunUntilDone )

				end )
	end )

	return {
		value = result
	}

end

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

function LrmLibraryAlbumAssetModelImp:changeOrderInsertAssets( albumId, assetIds, targetAssetId, location )

	if assetIds and assetIds[ 1 ] then

		local sqlQuery =
				[[SELECT assetId 
						FROM album_asset 
						WHERE albumId == ? AND assetId NOT IN CARRAY( ?, ?, 'char*' )
						ORDER BY sortOrder ]]

		local result = self.catalog:indexStore():queryOnce{ 
				sql = sqlQuery,
				singleColumnQuery = true,
				albumId,
				assetIds,
				#assetIds
			}
			:flatMap( function( assetIds )

				if assetIds and assetIds[ 1 ] then

					local elements = {}

					for i, assetId in ipairs( assetIds ) do

						if assetId == targetAssetId then

							if location == "after" then
								table.insert(elements, targetAssetId)
							end

							for j = 1, #assetIds do
								table.insert(elements, assetIds[j])
							end

							if location == "before" then
								table.insert(elements, targetAssetId)
							end

						else
							table.insert(elements, assetId)
						end
					end


					if #elements > 0 then

						local touchedIds = assetIds
						table.insert( touchedIds, targetAssetId )

						return LrmCatAlbumAsset.updateAssetOrdering( self.catalog, albumId, assetIds, touchedIds )
							:flatMap( function()
								return LrmCatAlbum.touchAlbum( self.catalog, albumId )
							end )
							:map( function()
								return { state = true }
							end )

					end

				end

				return Rx.Observable.just( { state = false } )

			end )

		return {
			value = result
		}

	end

end

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

function LrmLibraryAlbumAssetModelImp:addAssetsToNewAdhocAlbumAndShare( albumName, assetIds, shareAttributes )

	local result = LrmCatAlbumAsset.addAssetsToNewAdhocAlbumAndShare( self.catalog, albumName, assetIds, shareAttributes )

		:map( function( albumId )
			return { state = true, albumId = albumId }
		end )

		:catch( returnKSReactiveErrorEvent )

		:call( WFRxRunUntilDone )

	return {
		value = result
	}
end

--------------------------------------------------------------------------------
local function createAlbumClusters(self, albumId)

    local KSStringUtils = require 'KSStringUtils'

    return self.catalog._wfCatalogStream

    :flatMap( function( wfCatalog )

        local bulkPostUrl = wfCatalog:ozCatalogSpec():catalogHref() ..
                '/albums/' .. albumId .. '/clusters'
                .. '?similarity_distance=-0.70'

        bulkPostUrl = bulkPostUrl:gsub( '/v1%.0/', '/v2/' )
        local WFURI = require 'WFURI'
        bulkPostUrl = WFURI.stringByAppendingPathComponents( wfCatalog:ozCatalogSpec():serverBaseUrl(), bulkPostUrl )

        local options = {
            requestHeaders = {
                { header = 'Content-Type', value = 'application/json' },
                { header = 'Accept', value = '*/*' },
            },
            requestMethod = 'POST',
            bypassRateLimiter = true,
        }

        return wfCatalog:ozCatalogSpec():put( bulkPostUrl, '{}', options )

        :map( function( _, headers )

            local location = headers.Location

            assert( type( location ) == 'string' )

            local tokens = KSStringUtils.split(location, "/")

            return {
                clusterId = tokens[#tokens]
            }

        end )
    end)

end

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

function LrmLibraryAlbumAssetModelImp:createAlbumClusters( albumId)

	local result = createAlbumClusters (self, albumId)
    :tap(function()

		self._syncManager:getSyncActivityControl()
		:tap(function( shouldSync, hasOzCatalog, isResidualMode, isBinaryUploading, isAppInBackground )

			-- restart the sync to initiate a new cycle
			
			local syncClient = self.catalog:getSyncClient()
			require 'LrmSyncManager'.syncClientManager( syncClient, shouldSync, hasOzCatalog, isResidualMode, isBinaryUploading, isAppInBackground )

		end)
		:call( WFRxRunUntilDone )

    end)

	return {
		value = result
	}
end

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

function LrmLibraryAlbumAssetModelImp:deleteCluster(clusterId)
	
	local docStore = self.catalog:docStore()
	
	local result = self.catalog:indexStore():queryOnce( {
		sql = "SELECT docId FROM albums WHERE parentId == ?",
		singleColumnQuery = true,
		clusterId } )
		
		:flatMap( function( topicAlbumIds )
			return Rx.Observable.from( topicAlbumIds )
		end )
		
		:flatMap( function( topicAlbumId )
			return docStore:deleteDoc( topicAlbumId )
		end )
		
		:toArray()
		
		:flatMap ( function( topicAlbumIds )
			return docStore:deleteDoc(clusterId)
		end )
		
		:call( WFRxRunUntilDone )
	
	return {
		value = result
	}
end

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

return LrmLibraryAlbumAssetModelImp
