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

WFCatExportOriginalProvider.lua

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

ADOBE CONFIDENTIAL
------------------
Copyright 2015 Adobe Systems Incorporated
All Rights Reserved.

NOTICE: All information contained herein is, and remains 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 are protected by all applicable
intellectual property laws, including trade secret and copyright laws.
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

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


	-- WF.core.utils
local WFFileUtils = require 'WFFileUtils'
local KSTableUtils = require 'KSTableUtils'

	-- WF.core.os
local KSPathUtils = require 'KSPathUtils'

	-- WF.oz_client.managedCatalog.metadata
local WFCatExportHelper = require 'WFCatExportHelper'

	-- lightroomcc
local NmbAcr = require 'NmbAcr'

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

local logger = WFLogger 'WFCatExportOriginalProvider'
	--:setLevel( 'DEBUG' )

local derefOpt = assert( KSTableUtils.derefOpt )

local WFCrContext = NmbAcr.WFCrContext

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

-- @mark WFCatExportOriginalProvider
--------------------------------------------------------------------------------

local WFCatExportOriginalProvider = {}

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

local function getOriginalPath( exportPixelProviderTask, assetId, fallbackToProxySize )
	
	local originalStream = exportPixelProviderTask:getOriginal( assetId )
	
	return originalStream
			:take( 1 )
			:flatMapLatest( function( success, originalFilePath )
			
				if success and originalFilePath then
					return Observable.just( originalFilePath )
				end
				
				-- We dont have the original file
				-- Send proxy file instead, if settings
				-- permit.
				if fallbackToProxySize then
					
					return WFCatExportHelper.getProxyPath( exportPixelProviderTask, assetId )
						:map( function( negativePath )
							return negativePath, true -- Did fallback.
						end )
				end

				-- No fallback either
				return Observable.throw( 'original_unavailable' )
				
			end)
	
end

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

local function getXmpForExport( exportPixelProviderTask, originalPath, 
	docValue, userId, optDevSettingsOverride )

	return WFCatExportHelper.getXmpForExport( exportPixelProviderTask, 
		originalPath, docValue, userId, optDevSettingsOverride, false )
	
		:map( function( metadata ) 
			
			return tostring( metadata:serialize() )
		
		end )
			
end

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

function WFCatExportOriginalProvider:exportAsset( request, wfCatalog, exportPixelProviderTask, maybeProfileDownloaderTask )
	
	--Basic input validation
	local assetId = request.id 
	
	if not assetId then
		logger:warnf( 'Export failed as asset id supplied is nil' )
		return Observable.throw( 'invalid_asset_id' )
	end
	
	
	local destinationFolder = request.settings and request.settings.destinationFolder
                                                               
	if not destinationFolder or WFFileUtils.exists( destinationFolder ) ~= 'directory' then
		logger:warnf( "Destination folder '%s' seems to be invalid", destinationFolder )
		return Observable.throw( 'invalid_destination' )
	end
	
	--Check if we really need to update metadata
	local export720pRendition = request.settings and request.settings.export720pRendition or false
	local plainOriginal = request.settings and request.settings.plainOriginal or false
	local devSettingsOverride = request.devSettingsOverride
	local userId = wfCatalog:ozUserId()
	
	
	local sourceFilePathObservable = export720pRendition
		and WFCatExportHelper.getProxyPath( exportPixelProviderTask, assetId )
		or getOriginalPath( exportPixelProviderTask, assetId, request.settings.fallbackToProxySize )
	
	return Observable.combineLatest(
			wfCatalog:docStore():docValue( assetId ), sourceFilePathObservable )
	
		:flatMapLatest( function( docValue, originalPath, didFallback )
				
			if docValue._empty or docValue.type ~= 'asset' then
				logger:warnf( 'Could not find asset with asset id: %s', tostring( assetId ) )
				return Observable.throw( 'invalid_asset_id' )
			end

			-- When exporting as plain original, we dont need the xmp data.
			local xmpObservable = plainOriginal and Observable.just( nil ) 
								or getXmpForExport( exportPixelProviderTask, originalPath, docValue, userId, devSettingsOverride ) 
			
			return Observable.combineLatest( 
							Observable.just( originalPath ),
							Observable.just( derefOpt( docValue, 'importSource', 'fileName' ) ),
							xmpObservable,
							Observable.just( didFallback ),
							Observable.just( docValue.subtype ) )

		end )
		
		:flatMapLatest( function( originalFilePath, destinationFileName, xmp, didFallback, subtype )
			
			destinationFileName = request.fileName or destinationFileName
			
			if WFFileUtils.exists( originalFilePath ) ~= 'file' then
				logger:warnf( 'Invalid original path: %s', tostring( originalFilePath ) )
				return Observable.throw( 'invalid_original' )
			end
			
			-- Replace any invalid char with _. Note that the name can still be
			-- invalid since windows doesn't support filenames with some
			-- reserved keywords as well. But this will reduce the chances
			-- by a huge margin.
			destinationFileName = string.gsub( destinationFileName, "[<>:\"/\\|\?\*]", "_" )
			
			-- [anmurark]: NMB-17421 - extension may be missing in the 'originalFilePath' or 
			-- incorrect in the 'destinationFileName' when falling back to proxy. Use known 
			-- extensions in that case based on the asset subtype
			if didFallback or export720pRendition then
			
				local extension = subtype == "image" and '.dng' or '.mp4'
				local fileName = destinationFileName:match( "(.+)%..*$" )
				destinationFileName = fileName..extension
				
			end
			
			local destPath = KSPathUtils.child( destinationFolder, destinationFileName )
			
			--ashj: TODO: Need to check name conflict resolution policy
			-- This same provider should be able to save the edits
			-- as xmp as well, if the input settings say so.
			
			local ok, copiedPath = pcall( WFFileUtils.copyToUniquelyNamedFile, originalFilePath, destPath )
			
			if not ok or not copiedPath then
				local errMsg = copiedPath or 'WFFileUtils.copyToUniquelyNamedFile return nil'
				logger:warnf( "Failed to copy path '%s' to path '%s' because: %s", originalFilePath, destPath, errMsg  )

				return Observable.throw( 'write_failed' )
			end
			
			local warnings = didFallback and  { 'exported_with_fallback' }
			
			
			if plainOriginal then
				
				--Return the exported path since it can be different
				-- from the <destination>/<fileName> in case of
				-- conflicts.
				return  Observable.just {
					path = copiedPath,
					warning = warnings,
				}
			else
				
				local success, renderContext = pcall( WFCrContext, copiedPath )
		
				if not success then
					logger:warnf( 'WFCrContent init failed for %s', tostring( copiedPath ) )
					return Observable.throw( 'invalid_original' )
				end
				
				return renderContext
					:updateMetadata( xmp )
					:map( function( result )

						if not result then
							logger:warnf( "UpdateMetadata didn't return any result" )
							error( 'metadata_updation_failed' )
						end

						return {
							path = copiedPath,
							warning = warnings,
						}

					end )
			end
			
		end)
	
end


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

return WFCatExportOriginalProvider
