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

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.

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

	-- WF.core.reactive
local KSPromises = require 'KSPromises'

	-- WF.core.utils
local KSIdGenerator = require 'KSIdGenerator'
local KSTableUtils = require 'KSTableUtils'
local KSPathUtils = require "KSPathUtils"

local WFTableMerge = require 'WFTableMerge'

	-- WF.core.lua
local KSData = require 'KSData'

	-- WF.core.os
local KSDate = require 'KSDate'
local WFDeviceInfo = require 'WFDeviceInfo'
local WFFileUtils = require 'WFFileUtils'

	-- WF.web.json
local KSJsonDecoder = require 'KSJsonDecoder'

local LriOzRenditionServerUtils = require "LriOzRenditionServerUtils"

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

local LriImport = {}

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

local derefOpt = assert( KSTableUtils.derefOpt )

local kImportStatusNewAsset = 'new'
local kImportStatusDuplicateAsset = 'duplicate'

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

local function applyFilesOverlay( asset, renditionName, catalogPath, filePath )

	if filePath then

		local masterFileAttributes = WFFileUtils.fileAttributes( KSPathUtils.child( catalogPath, filePath ) )
		local masterSize = masterFileAttributes.fileSize
	
		local contentId
		
		if renditionName == 'master' or renditionName == 'proxy' then
			contentId = derefOpt(asset.docValue, 'importSource', 'sha256' )
		elseif renditionName == 'thumbnail2x' or renditionName == 'preview' then
			contentId = LriOzRenditionServerUtils.renditionContentId( asset.docValue )
		end
	
		local fileInfo = {}
	
		fileInfo[ renditionName ] = {
			fileSize = masterSize,
			path = filePath,
			contentId = contentId
		}

		WFTableMerge.applyOverlay( asset.docValue, {
			_localOnly = {
				files = fileInfo
			}
		} )

	end

	return asset
	
end

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

local function createImportResult( assetId, localUrl, jobAssetId, importStatus )

	return {
		assetId = assetId,
		importStatus = importStatus,
		localUrl = localUrl,
		jobAssetId = jobAssetId,
	}
end

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

local function addAssetToCatalogP( catalog, asset, userId, options )

--	check( 'OzCatalogServiceImp', 'table' )
	
	-- check KSData
	-- check args
	
	-- check local url
	
	local deviceId = asset.deviceId
	
	local bypassSha256Check = options.bypassSha256Check or false
	
	if not deviceId then
		deviceId = WFDeviceInfo.uniqueDeviceId()
	end
	
	local jobAssetId = asset.jobAssetId
	
	if not jobAssetId then
		return KSPromises.resolvedPromiseP( 'error', 'No valid jobAssetId' )
	end
	
	local docValue = assert( asset.docValue )
	local sha256 = derefOpt( docValue, 'importSource', 'sha256' )
	
	if not docValue.captureDate then
		docValue.captureDate = KSDate.timeInIso8601Format( nil, false )
	end
	
	docValue.originalCaptureDate = docValue.captureDate
	
	if not docValue.userCreated then
		docValue.userCreated = KSDate.timeInIso8601Format()
		docValue.userUpdated = KSDate.timeInIso8601Format()
	end

	if not docValue.importSource then
		docValue.importSource = {}
	end
	
	if not docValue._localOnly then
		docValue._localOnly = {}
	end
	
	local localUrl = derefOpt( docValue, '_localOnly', 'localUrl' )
	local isFileUrl = localUrl and string.find( localUrl, 'file://' ) == 1 or false

	if isFileUrl then
		docValue._localOnly.localUrl = nil
	end
	
	local isVideo = docValue.video and true or false
	
	docValue.subtype = isVideo and 'video' or 'image'
	docValue.importSource[ 'type' ] = nil
	docValue.importSource[ 'importedOnDevice' ] = string.format( '%s {%s}', WFDeviceInfo.deviceName(), deviceId )
	docValue.importSource[ 'importedBy' ] = userId
	
	if not derefOpt( docValue, 'importSource', 'importTimestamp' ) then
		docValue.importSource[ 'importTimestamp' ] = KSDate.timeInIso8601Format( nil, false )
	end
	
	docValue.importSource[ 'localAssetId' ] = localUrl
	docValue.importSource[ 'uniqueDeviceId' ] = WFDeviceInfo.uniqueDeviceId()
	
	if docValue.height then
		docValue.importSource[ 'originalHeight' ] = docValue.height
	end
	
	if docValue.width then
		docValue.importSource[ 'originalWidth' ] = docValue.width
	end
	
	docValue.height = nil
	docValue.width = nil
	docValue.originalCaptureDate = nil
	
	local docStore = catalog:docStore()
		
	local newRevId = asset.revId or KSIdGenerator.uuidInLowercaseHex()
	
	return LriImport.hasAssetBySha256P( catalog, sha256, bypassSha256Check )
	
		:mapDataP_new( function( assetId )
		
			if assetId then
			
				return createImportResult( assetId, localUrl, jobAssetId, kImportStatusDuplicateAsset )
				
			end
			
			return catalog:getOriginalsDocumentsFolderP()

				:mapDataP_new( function( catalogPath )
									
					local assetId = asset.assetId or KSIdGenerator.uuidInLowercaseHex()
					
					if asset.master then
						applyFilesOverlay( asset, 'master', catalogPath, asset.master )
					end
					
					if asset.proxy then
						applyFilesOverlay( asset, 'proxy', catalogPath, asset.proxy )
					end
					
					if asset.renditions then
						for _, rendition in ipairs( asset.renditions ) do						
							applyFilesOverlay( asset, rendition.renditionName, catalogPath, rendition.renditionData )
						end
					end
										
					asset.docValue._newRevId = newRevId
									
					return docStore:applyOverlayToDocumentP {
						docId = assetId,
						docType = 'asset',
						overlay = asset.docValue,
					}
					:mapDataP_new( function()
					
						return createImportResult( assetId, localUrl, jobAssetId, kImportStatusNewAsset )
						
					end )
					
				end )
		
		end )
	
		:mapP( function( ... )
		
			local originalData = asset.original
			
			if originalData and KSData.isData( originalData ) then
				originalData:close()
			end
			
			local proxyData = asset.proxy
			
			if proxyData and KSData.isData( proxyData ) then
				proxyData:close()
			end
			
			return ...
			
		end )
		
		:runUntilDoneP()
		
end

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

function LriImport.addAssetArrayToCatalogP( catalog, assetArray, options )

--	check( 'OzCatalogServiceImp', 'table', 'table' )
	
	local minimumFreeDiskSpace = assert( options.minimumFreeDiskSpace )
	
	local function _addAssetToCatalogP( asset )
	
		if type( asset.docValue ) == 'string' then
		
			local result, docValue = pcall( KSJsonDecoder.decodeWebSafeJson, asset.docValue )
			
			asset.docValue = nil
			
			if result then
				
				asset.docValue = docValue
				
			end
			
		end
		
		if not asset.docValue or type( asset.docValue ) ~= 'table' then
			
			return KSPromises.resolvedPromiseP( 'error', 'No valid docValue for asset' )
			
		end
			
		local derivedFrom = asset.derivedFrom
		
		-- INFO [pkamin 2015-08-31]: we don't verify the revisionId as we might not have a revision
		-- due to not yet uploaded metadata
		
		if derivedFrom and derivedFrom.catalogId and derivedFrom.assetId then
		
			return catalog:getCatalogIdP()
			
				:mapDataP_new( function( catalogId )
			
					if catalogId == derivedFrom.catalogId then
					
						return catalog:docStore():docValueOnceP( 'asset', derivedFrom.assetId )
							
							:mapP( function( status, originDocValue )
							
								local newDocValue = asset.docValue
								
								if status == 'data' and originDocValue._changeCounter then
								
									local derivedFromInstance = derivedFrom.revisionId 
													and string.format( '/v2/catalogs/%s/assets/%s/revisions/%s', 
																		catalogId, 
																		originDocValue._docId, 
																		derivedFrom.revisionId ) 
													or nil
									
									local derivedFromDocument = string.format( '/v2/catalogs/%s/assets/%s', 
																	catalogId, 
																	originDocValue._docId )
									
									--------------------------------------------------------------------------------
									
									local derivedOverlay = {
										xmp = {
											xmpMM = {
												DerivedFromDocument = derivedFromDocument,
												DerivedFromInstance = derivedFromInstance,
											}
										}
									}
									
									local originXmp = derefOpt( originDocValue, 'xmp' )
									
									local copiedXmp = {
										xmp = originXmp,
									}
										
									WFTableMerge.applyOverlay( newDocValue, copiedXmp )
									
									WFTableMerge.applyOverlay( newDocValue, derivedOverlay )
									
								end
								
								asset.docValue = newDocValue
								
								return 'data', asset
							
							end )
							
							:mapDataP_new( function()
								return catalog:ozUserIdP()
							end )
							
							:mapDataP_new( function( userId )
								return addAssetToCatalogP( catalog, asset, userId, options )
							end )
						
					end
					
					return catalog:ozUserIdP()
							
						:mapDataP_new( function( userId )
							return addAssetToCatalogP( catalog, asset, userId, options )
						end )
					
				end )
			
		end
		
		return catalog:ozUserIdP()
			
			:mapDataP_new( function( userId )
			
				return addAssetToCatalogP( catalog, asset, userId, options )
				
			end )
		
	end
	
	local fileSystemFreeSpace = WFFileUtils.freeSpaceAtPath( KSPathUtils.getStandardFilePath 'carouselDocuments' )

	local spaceNeeded = minimumFreeDiskSpace - fileSystemFreeSpace
		
	if spaceNeeded > 0 then
		return KSPromises.resolvedPromiseP( 'error', 'LowDiskSpace' )
	end
			
	return KSPromises.mapArrayP( _addAssetToCatalogP, assetArray, 5 )
		:mapP( function( status, data )
		
			return status, data
			
		end )
			
end

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

function LriImport.hasAssetBySha256P( catalog, sha256, bypassSha256Check )

	if bypassSha256Check then
		return KSPromises.resolvedPromiseP( 'data', nil )
	end
	
	if not( type( sha256 ) == 'string' and #sha256 == 64 ) then
		error( "hasAssetBySha256P: sha256 must be a 64-byte hex string", 2 )
	end

	local sql = [[ SELECT docId 
					FROM coreInfo 
					WHERE sha256 = ?
					AND spaceId IS NULL ]] -- Only consider catalog-based assets.
					
	local params = { sha256 }
	
	return catalog:indexStore():queryP( sql, unpack( params ) )
		:mapDataP_new( function( row )
		
			return row and row[ 1 ] and row[ 1 ][ 1 ]
			
		end )
		
end

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

return LriImport
