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

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 ],
				needsUpdateAISettings = originalPhotoRow[ 13 ] == 1,
				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
				contentType = WFOzContentTypes.contentTypeForContentTypeNumber( originalPhotoRow[ 3 ], true ),
				duration = originalPhotoRow[ 4 ],
				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, fileName ASC, coreInfo.docId 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
