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

ADOBE CONFIDENTIAL
------------------
Copyright 2013 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 KSTableUtils = require 'KSTableUtils'
local WFArguments = require 'WFArguments'

	-- WF.oz_client.catalog
local WFOzContentTypes = require 'WFOzContentTypes'

local WFPlatformInfo = require "WFPlatformInfo"

	-- Lrmobile_models
local LrmAlbumUtils = require 'LrmAlbumUtils'
local LrmAlbumQueryBuilder = require "LrmAlbumQueryBuilder"

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

local LrmAlbumModel = {}
local Model = {}

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

local derefOpt = KSTableUtils.derefOpt

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

local function makeCollectionInfo( name, sql, originalPhotoRows, eventName, startDate, endDate, eventNameFirst )

	if not originalPhotoRows or #originalPhotoRows == 0 then
		return { name = name, photos = { }, eventName = eventName, startDate = startDate, endDate = endDate, assetCount = 0 }
	end
	
	local collectionInfo = {
		name = name,
		photos = {},
		eventName = eventName,
		startDate = startDate,
		endDate = endDate,
		eventNameFirst = eventNameFirst,
		assetCount = #originalPhotoRows,
	}
	
	local photos = collectionInfo.photos
	
	local countCollectionInfoFields = LrmAlbumQueryBuilder.kCountCollectionInfoFields
	
	for i, originalPhotoRow in ipairs( originalPhotoRows ) do
		
		if WFPlatformInfo.isAndroid then
		
			-- Android needs only a limited set to save memory and improve performance
			photos[ i ] = {
				id = originalPhotoRow[ 1 ], -- coreInfo.docId
				croppedWidth = originalPhotoRow[ 2 ],
				croppedHeight = originalPhotoRow[ 3 ],
				captureDate = originalPhotoRow[ 4 ], --captureDate
				fileName = originalPhotoRow[ 5 ],
				modifiedDate = originalPhotoRow[ 6 ], -- modifiedDate
				thumbnailMD5 = originalPhotoRow[ 7 ], -- thumbnailMD5
				importDate = originalPhotoRow[ 8 ], -- importDate
				hasThumbnail = originalPhotoRow[ 9 ],
				importedByUser = originalPhotoRow[10] or "",
				isVideo = originalPhotoRow[ 11 ] == 1,
				duration = originalPhotoRow[ 12 ],
				isPopulated = true,
			}
			
			if string.find( sql, 'sortOrder' ) then
				photos[ i ].sortOrder = originalPhotoRow[ countCollectionInfoFields + 1 ]
			end

			if string.find( sql, 'topicId' ) then
				photos[ i ].topicId = originalPhotoRow[ countCollectionInfoFields + 2 ]
			end

		else
			photos[ i ] = {
				id = originalPhotoRow[ 1 ], -- coreInfo.docId
				captureDate = originalPhotoRow[ 2 ], --captureDate
				duration = originalPhotoRow[ 4 ],
				contentType = WFOzContentTypes.contentTypeForContentTypeNumber( originalPhotoRow[ 3 ], true ),
				flag = originalPhotoRow[ 6 ],
				croppedWidth = originalPhotoRow[ 7 ],
				croppedHeight = originalPhotoRow[ 8 ],
				fileName = originalPhotoRow[ 9 ],
				width = originalPhotoRow[ 10 ],
				height = originalPhotoRow[ 11 ],
				apertureValue = originalPhotoRow[ 12 ],
				isoSpeedRatings = originalPhotoRow[ 13 ],
				exposureTime = originalPhotoRow[ 14 ],
				cameraMaker = originalPhotoRow[ 15 ],
				cameraModel = originalPhotoRow[ 16 ],
				rating = originalPhotoRow[ 17 ],
				modifiedDate = originalPhotoRow[ 18 ], -- modifiedDate
				isVideo = originalPhotoRow[ 19 ],
				localUrl = originalPhotoRow[ 20 ],
				fnumber = originalPhotoRow[ 21 ],
				isMergedHDR = originalPhotoRow[ 22 ],
				hasThumbnail = originalPhotoRow[ 23 ],
				importedByUser = originalPhotoRow[ 24 ] or "",

			}

			-- extra fields 
			if string.find( sql, 'commentCount' ) then
				photos[ i ].commentCount = originalPhotoRow[ countCollectionInfoFields + 1 ]
			end
				
			if string.find( sql, 'favoriteCount' ) then
				photos[ i ].favoriteCount = originalPhotoRow[ countCollectionInfoFields + 2 ]
			end

			if string.find( sql, 'sortOrder' ) then
				photos[ i ].sortOrder = originalPhotoRow[ countCollectionInfoFields + 1 ]
			end

			if string.find( sql, 'topicId' ) then
				photos[ i ].topicId = originalPhotoRow[ countCollectionInfoFields + 2 ]
			end
		end
	
	end
	
	return collectionInfo

end


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

local function globalGridCollectionInfo( catalog, activeAlbum, ascending, assetFilter, assetTypeFilter, keyword, edited, sortCriteria, ratingFilter, ratingComparisonOperator, isSearch )

	local sql, queryParams = LrmAlbumQueryBuilder.buildCoreInfoQuery( activeAlbum, ascending, 
												assetFilter, assetTypeFilter, keyword, edited, sortCriteria, 
												ratingFilter, ratingComparisonOperator,
												isSearch
											)
	
    local result = catalog:indexStore():query { 
		sql = sql, 
		calm = .125,
		nArgs = #queryParams, 
		unpack( queryParams )
	}

	return result
		:map( function( originalPhotoRows )
        
			return makeCollectionInfo(
					'grid', -- collection id for grid mode, not used by UI
					sql,
					originalPhotoRows,
					true )

	end )
							
end

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

local function bestPhotosCollectionInfo( catalog, albumId, bestPhotosClusterId )

	local sql, queryParams = LrmAlbumQueryBuilder.buildBestPhotosCoreInfoQuery( albumId, bestPhotosClusterId )

	local result = catalog:indexStore():query {
		sql = sql,
		calm = .125,
		nArgs = #queryParams,
		unpack( queryParams )
	}

	return result:map( function( originalPhotoRows )

		return makeCollectionInfo(
			'grid', -- collection is for grid mode, not used by UI
			sql,
			originalPhotoRows,
			true )

	end )

end

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

local function oldAssetListModel( collectionInfo )

	local prevCollectionInfo = { 
		assetCount = 0,
		closeEnough = false,
		photos = {},
	}
	prevCollectionInfo.photos = {}
	return collectionInfo:map( function( collectionInfo )
		
		if type( collectionInfo ) == 'userdata' then
			return collectionInfo, false
		end
		
		local newPhotos = collectionInfo and collectionInfo.photos

		if type( newPhotos ) == 'userdata' then
			return newPhotos, false
		end
		
		if collectionInfo ~= nil then
			newPhotos = collectionInfo and collectionInfo.photos
			
			local prevPhotos = nil
			if prevCollectionInfo and prevCollectionInfo.photos then
				prevPhotos = prevCollectionInfo.photos
			end
			
			local closeEnough = LrmAlbumUtils.photoListsCloseEnough( newPhotos, prevPhotos )
			
			if collectionInfo then
				collectionInfo.closeEnough = closeEnough
			end
			
			if closeEnough then
				return prevCollectionInfo
			else
				prevCollectionInfo = collectionInfo
				return prevCollectionInfo
			end
		end
	end )
	:map( function( collectionInfo )
	
		
		local albumInfo = nil
		local photos = nil
		if collectionInfo ~= nil then
			albumInfo = {}
			albumInfo.photos = collectionInfo.photos
			albumInfo.assetCount = collectionInfo.assetCount
			albumInfo.closeEnough = collectionInfo.closeEnough
			photos = collectionInfo.photos
		end
		return {
			albumInfo = albumInfo,
			assetListHash = LrmAlbumUtils.calculateAssetListHash( photos )
		}
	end )
end

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

local function newAssetListModel( encoder, catalog, activeAlbum, ascending, assetFilter, assetTypeFilter, keyword, edited, sortCriteria, ratingFilter, ratingComparisonOperator, isSearch )

	local sql, queryParams = LrmAlbumQueryBuilder.buildCoreInfoQuery( activeAlbum, ascending, 
												assetFilter, assetTypeFilter, keyword, edited, sortCriteria, 
												ratingFilter, ratingComparisonOperator,
												isSearch
											)
	
    local result = catalog:indexStore():query { 
		sql = sql, 
		encoder = encoder,
		calm = .125,
		unpack( queryParams )
	}
	
	return result
								
end

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

function Model:cacheSizeModel()

	local cacheSize = self.wfCatalog:indexStore()
		:query( {
			sql = [[SELECT SUM(cr.size) FROM cacheReferences AS cr INNER JOIN
					album_asset ON album_asset.assetId = cr.assetId 
					WHERE album_asset.albumId == ? AND cr.isTrash == 0]],
			calm = 3,
			nArgs = 1,
			self.albumId,
		} )
		:map( function( rows )
				
			local cacheSize = derefOpt( rows, 1, 1 ) or 0
			
			return cacheSize
		end )
		
	return {
		value = cacheSize,
	}
end

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

function Model:isSharedModel()

	return {
		value = LrmAlbumUtils.isAlbumShared( self.wfCatalog:indexStore(), self.albumId )
	}
	
end

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

function Model:unsynchronizedAssets()

	local trickerSql = [[SELECT count( distinct r.assetId ) FROM renditionRevisions as r 
			INNER JOIN album_asset ON album_asset.assetId = r.assetId WHERE album_asset.albumId == ? 
			LIMIT 1]]
	local tricker = self.wfCatalog:indexStore():query( {
		sql = trickerSql,
		self.albumId
	 } )
	:pluck( 1, 1 )

	return {
		value = tricker,
	}
end

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

function Model:bestPhotosListModel( bestPhotosClusterId )

	local bestPhotosList = oldAssetListModel( bestPhotosCollectionInfo( self.wfCatalog, self.albumId,  bestPhotosClusterId ) )
	return {
		value = bestPhotosList,
	}

end

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


function Model:assetListModel( sortCriteria, ascending, assetFilter, assetTypeFilter, keyword, edited, ratingFilter, ratingComparisonOperator, isSearch, useEncoder )

	useEncoder = useEncoder or false
	
	local activeFilter = {}
	
	for i = 1, #assetFilter do
		local filter = assetFilter[ i ]
		activeFilter[ filter ] = true
	end
	
	for i = 1, #assetTypeFilter do
		local assetType = assetTypeFilter[ i ]
		activeFilter[ assetType ] = true
	end
	
	if type( edited ) == 'table' then
		for i = 1, #edited do
			activeFilter.edited = edited[ i ]
		end
	end
	
	local applyFilter = self.wfCatalog:docStore():applyOverlay( {
		_id = self.albumId,
		_localOnly = {
			filter = {
				image = activeFilter.image,
				video = activeFilter.video,
				pick = activeFilter.pick,
				reject = activeFilter.reject,
				unflagged = activeFilter.unflagged,
				favorite = activeFilter.favorite,
				comment = activeFilter.comment,
				keyword = keyword,
				edited = activeFilter.edited,
				rating = ratingFilter,
				ratingComparisonOperator = ratingComparisonOperator,
				sortCriteria = sortCriteria,
				ascending = ascending
			},
		},
	} )

	local assetListModel
	
	if useEncoder then
	
		local encoder = 'LrmOzAssetCoreInfo.encodeSql'
		assetListModel = newAssetListModel( encoder, self.wfCatalog, 
						self.albumId, ascending, assetFilter, assetTypeFilter, keyword, activeFilter.edited, sortCriteria, 
						ratingFilter, ratingComparisonOperator, isSearch
					)
	else
	
		assetListModel = oldAssetListModel( globalGridCollectionInfo( self.wfCatalog, 
						self.albumId, ascending, assetFilter, assetTypeFilter, keyword, activeFilter.edited, sortCriteria, 
						ratingFilter, ratingComparisonOperator, isSearch ) 
					)
		
	end
	
	return {
		value = applyFilter
					:flatMap( function()
						return assetListModel
					end )
	}
	
end

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

local function makeFlagStatus( rows )

	local flags = {
		unflagged = 0,
		pick = 0,
		reject = 0,
	}
	
	for i=1, #rows do
	
		local row = rows[i]
	
		if row[1] == 'unflagged' then	
		
			flags.unflagged = row[2]
		
	  	elseif row[1] == 'pick' then
		
			flags.pick = row[2]
			
	  	elseif row[1] == 'reject' then	
			
			flags.reject = row[2]
			
	  	end
		
	end
	
	return flags
	
end

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

local function flags( self, wfCatalog, albumId )

	local flagStatusQuery = [[SELECT flag, COUNT( coreInfo.docId )
								FROM album_asset
							      INNER JOIN coreInfo ON album_asset.assetId == coreInfo.docId
								WHERE
								  album_asset.albumId == ?
								GROUP BY coreInfo.flag]]

	return wfCatalog:indexStore():query{
				sql = flagStatusQuery,
				calm = 1,
				albumId
			}
			:map( makeFlagStatus )

end

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

function Model:flagStatusCountModel()

	return {
		value = flags( self, self.wfCatalog, self.albumId )
	}
end

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

function Model:alternativeCoverAssetId()

	local coverImageAssetId = self.wfCatalog:indexStore():query{ 
			sql = [[SELECT coreInfo.docId FROM coreInfo
					INNER JOIN album_asset ON album_asset.assetId == coreInfo.docId
						WHERE albumId == ? 
					ORDER BY captureDate ASC LIMIT 1]], 
			singleColumnQuery = true,
			self.albumId }
		:pluck( 1 )

	return {
		value = coverImageAssetId,
	}
	
end

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

function Model:coverImageModel()

	local explicitCoverField = self.wfCatalog:docStore():ongoingFieldValue( self.albumId,
							'cover/id' )

	local result = explicitCoverField:flatMap( function( coverId )
	
		coverId = coverId or ""
		
		return self.wfCatalog:indexStore():query( { 
		
			sql = [[
				SELECT coreInfo.docId
				FROM coreInfo
				INNER JOIN album_asset ON album_asset.assetId == coreInfo.docId
				WHERE
					albumId == ?
				AND
					coreInfo.docId = ?
				UNION ALL
					SELECT ( SELECT coreInfo.docId
					FROM coreInfo
					INNER JOIN album_asset ON album_asset.assetId == coreInfo.docId
					WHERE
						albumId == ?
					ORDER BY
						CASE WHEN album_asset.sortOrder IS NULL THEN 1 ELSE 0 END,
						album_asset.sortOrder, captureDate ASC )
				LIMIT 1]],
				calm = 2,
				singleColumnQuery = true,
				self.albumId, coverId, self.albumId
			} )
			
			:pluck( 1 )

		end )

	return {
		value = result,
	}
	
end

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

function LrmAlbumModel.albumModel( wfCatalog, albumId )

	WFArguments.check( 'WFCatalog', 'string' )
	
	local result = wfCatalog:docStore():docValue( albumId )
		:map( function( status, docValue )
			
			local filter = derefOpt( docValue, "_localOnly", "filter" )
			
			if filter then
								
				local activeFilter = {
					image = derefOpt( filter, "image" ),
					video = derefOpt( filter, "video" ),
					pick = derefOpt( filter, "pick" ),
					reject = derefOpt( filter, "reject" ),
					unflagged = derefOpt( filter, "unflagged" ),
					favorite = derefOpt( filter, "favorite" ),
					comment = derefOpt( filter, "comment" ),
					rating = derefOpt( filter, "rating" ),
					ratingComparisonOperator = derefOpt( filter, "ratingComparisonOperator" ),
					sortCriteria = derefOpt( filter, "sortCriteria" ),
					ascending = derefOpt( filter, "ascending" ),
				}

				return activeFilter
				
			else
				return nil
			end
		
		end )
		:concat( Rx.Observable.never() )
		:replay( nil, 1 )
		:refCount()
	
	return {
		wfCatalog = wfCatalog,
		albumId = albumId,
		value = result,
		models = Model,
		
		events = {
		}
	}
end


return LrmAlbumModel
