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

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.

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

	-- RxLua
local Rx = require 'Rx'
local Observable = Rx.Observable


local KSTableUtils = require 'KSTableUtils'
local WFPlatformInfo = require 'WFPlatformInfo'

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

local derefOpt = assert( KSTableUtils.derefOpt )
local setNested = assert( KSTableUtils.setNested )

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

local LrmLibraryAssetOrganizationModelImp = {}

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

function LrmLibraryAssetOrganizationModelImp:hasAssetsInAlbum( albumId, assetIds )

	return {
		value = self.catalog:indexStore():queryOnce {
			sql = [[ SELECT assetId FROM album_asset WHERE albumId == ? AND assetId IN CARRAY(?,?,'char*') ]],
			singleColumnQuery = true,
			quiesceUntilUpToDate = true,
			albumId,
			assetIds,
			#assetIds }
		:map( function( rows )
			
			local assetIds = {}
			
			for _, assetId in ipairs( rows ) do
				assetIds[ assetId ] = true
			end
			
			return {
				assetIds = assetIds,
				state = true,
			}
		
		end )
	}
end

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

function LrmLibraryAssetOrganizationModelImp:hasAssetByLocalUrl( localUrls, userId )

	local query = self.catalog:indexStore():queryOnce {
			sql = [[SELECT docId FROM coreInfo WHERE importedBy = ? AND localUrl IN CARRAY(?,?,'char*') LIMIT 1 ]],
			singleColumnQuery = true,
			userId,
			localUrls,
			#localUrls }
		:map( function( rows )

			local assetId = derefOpt( rows, 1 )

			if assetId then
				return {
					state = true,
					assetIds = { assetId = assetId }
				}
			end

			return {
				state = false,
				error = 'No assets found'
			}

		end )

	return {
		value = query,
	}
end


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

function LrmLibraryAssetOrganizationModelImp:hasAssetsByLocalUrl( localUrls )

	-- WF-861: on mass import this method might be called with 1000s of localUrls at once.

	localUrls = localUrls or {}

	local sql = [[SELECT coreInfo.docId, localUrl FROM coreInfo WHERE coreInfo.localUrl IN CARRAY(?,?,'char*')]]

	local hasAssetsByLocalUrl = self.catalog:indexStore():queryOnce {
			sql = sql,
			localUrls,
			#localUrls
		}
		:map( function( idRows )
			local assetIds = {}

			for i, v in ipairs( idRows ) do
				assetIds[ v[ 2 ] ] = v[ 1 ]
			end

			return assetIds
		end )

	return {
		value = hasAssetsByLocalUrl,
	}

end

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

--[[
	\brief Looks up assets by localUrl and classifies them as edited or unedited.

	\param localUrls (table) - An array table of local identifiers that a client is interested in. Expected to be size limited _by the client_

	\return table - The return is divided into two sections by asset edited state. Ex:
		{
			edited = {
				localUrl1a = assetId1a
				localUrl2a = assetId2a
				...
			},
			unedited = {
				localUrl1b = assetId1b
				localUrl2b = assetId2b
				...
			}
		}

]]
function LrmLibraryAssetOrganizationModelImp:editedUneditedAssetsByLocalUrl( localUrls )

	localUrls = localUrls or {}

	local sql = [[SELECT coreInfo.docId, localUrl, developUserUpdated FROM coreInfo WHERE coreInfo.localUrl IN CARRAY(?,?,'char*')]]

	local editedUneditedAssetsByLocalUrl = self.catalog:indexStore():queryOnce {
			sql = sql,
			localUrls,
			#localUrls,
			quiesceUntilUpToDate = true
		}
		:map( function( idRows )
			local editedAssetIds = {}
			local uneditedAssetIds = {}

			for i, v in ipairs( idRows ) do
				local assetId = v[ 1 ]
				local url =  v[ 2 ]
				local edited = v[ 3 ]

				if edited then
					editedAssetIds[ url ] = assetId
				else
					uneditedAssetIds[ url ] = assetId
				end
			end

			return {
				edited = editedAssetIds,
				unedited = uneditedAssetIds
			}
		end )

	return {
		value = editedUneditedAssetsByLocalUrl,
	}

end

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

function LrmLibraryAssetOrganizationModelImp:hasAssetsBySha256( shas )

	shas = shas or {}

	local sql = [[
		SELECT docId, sha256 FROM coreInfo WHERE sha256 IN CARRAY(?,?,'char*')
			
	]]

	local hasAssetsBySha256 = self.catalog:indexStore():queryOnce {
			sql = sql,
			shas,
			#shas,
			quiesceUntilUpToDate = true
		}
		:map( function( idRows )
			local assetIds = {}

			for _, v in ipairs( idRows ) do
				assetIds[ v[ 2 ] ] = v[ 1 ]
			end

			return assetIds
		end )

	return {
		value = hasAssetsBySha256,
	}

end

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

function LrmLibraryAssetOrganizationModelImp:hasVersionAssetsByLinkedLocationSha256( shas )

	shas = shas or {}

	local sql = [[
		SELECT
			version.assetId as assetId,
			version.docId as versionId,
			version.linkedLocationId,
			version.linkedLocationSha,
			version.linkedLocationSource
		FROM versions as version
		LEFT JOIN trashAssets ON version.assetId = trashAssets.docId
		WHERE version.linkedLocationSha IN CARRAY(?,?,'char*')
			AND trashAssets.docId IS NULL
		]]

	local hasVersionAssetsByLinkedLocationSha256 = self.catalog:indexStore():queryOnce {
			sql = sql,
			shas,
			#shas,
			quiesceUntilUpToDate = true
		}
		:map( function( rows )

			local details = {}

			for _, row in ipairs( rows ) do
				local assetId			= row[ 1 ]
				local versionId			= row[ 2 ]
				local linkedLocationId		= row[ 3 ]
				local linkedLocationSha		= row[ 4 ]
				local linkedLocationSource	= row[ 5 ]

				local detail = {
					assetId = assetId,
					versionId = versionId,
					source = linkedLocationSource,
					linkedLocationId = linkedLocationId
				}

				setNested( details, detail, linkedLocationSha )
			end

			return details
		end )

	return {
		value = hasVersionAssetsByLinkedLocationSha256,
	}

end

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

--[[
	\brief Looks up versions and assets by linkedLocation.id

	\param linkedLocationIds (table) - An array table of linkedLocationIds that a client is interested in. Expected to be size limited _by the client_

	\return table - The return is divided into two sections by document type. Ex:
		{
			assets = {
				linkedLocationId1 = {
					assetId = "..."
					source = "..."
					sha256 = "..." -- Android only
				},
				linkedLocationId2 = ...
			},
			versions = {
				linkedLocationId1 = {
					assetId = "..."
					versionId = "..."
					source = "..."
					sha256 = "..." -- Android only
				}
			}
		}

]]
function LrmLibraryAssetOrganizationModelImp:hasAssetsByLinkedLocationId( linkedLocationIds )

	-- Chunk size will be dictated by the clients, not by WF.

	linkedLocationIds = linkedLocationIds or {}

	local versionSQL = [[
		SELECT
			version.assetId as assetId,
			version.docId as versionId,
			version.linkedLocationId,
			version.linkedLocationSha,
			version.linkedLocationSource
		FROM versions as version
		LEFT JOIN trashAssets ON version.assetId = trashAssets.docId
		WHERE version.linkedLocationId IN CARRAY(?,?,'char*')
			AND trashAssets.docId IS NULL
		]]

	local assetSQL = [[
		SELECT
			asset.docId AS assetId,
			NULL AS versionId,
			asset.linkedLocationId,
			asset.sha256 as linkedLocationSha,
			asset.linkedLocationSource
		FROM coreInfo AS asset
		LEFT JOIN trashAssets ON asset.docId = trashAssets.docId
		WHERE asset.linkedLocationId IN CARRAY(?,?,'char*')
			AND trashAssets.docId IS NULL
	]]


	-- could use combineLatest for the asset version and the asset version
	local hasVersionAssetsByLinkedLocationId = self.catalog:indexStore():queryOnce {
			sql = versionSQL,
			linkedLocationIds,
			#linkedLocationIds,
			quiesceUntilUpToDate = true
		}

	local hasAssetsByLinkedLocationId = self.catalog:indexStore():queryOnce {
			sql = assetSQL,
			linkedLocationIds,
			#linkedLocationIds,
			quiesceUntilUpToDate = true
		}

	local results = Observable.combineLatest( hasVersionAssetsByLinkedLocationId, hasAssetsByLinkedLocationId )
		:map( function( versionRows, assetRows )
			local linkedLocationDetails = {}

			local function buildDetail( row, kind )

				local assetId			= row[ 1 ]
				local versionId			= row[ 2 ]
				local linkedLocationId		= row[ 3 ]
				local linkedLocationSha		= row[ 4 ]
				local linkedLocationSource	= row[ 5 ]

				local detail = {
					assetId = assetId,
					versionId = versionId,
					source = linkedLocationSource,
					sha256 = WFPlatformInfo.isAndroid and linkedLocationSha or nil
				}

				setNested( linkedLocationDetails, detail, kind, linkedLocationId )

			end

			for _, row in ipairs( versionRows ) do
				buildDetail( row, 'versions')
			end

			for _, row in ipairs( assetRows ) do
				buildDetail( row, 'assets')
			end

			return linkedLocationDetails
		end )

	return {
		value = results,
	}

end

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

return LrmLibraryAssetOrganizationModelImp
