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

PixelProviderAnalyzeXmpSidecarTask.lua

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

ADOBE CONFIDENTIAL
------------------
Copyright 2017 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
local Foreign
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

	-- pixel provider
local PixelPathProviderStateMachine = require 'PixelPathProviderStateMachine'
local PixelProviderSharedTaskUtils = require 'PixelProviderSharedTaskUtils'
local PixelPathProviderSchedulerPrivateMethods =
		require "PixelPathProviderSchedulerPrivateMethods"

local NmbAcr = require 'NmbAcr'

	-- WF.imaging
local WFXmp = require 'WFXmp'

	-- WF.oz_client.managed_catalog
local WFXMLNS = require 'WFXMLNS'

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

local logger = require 'WFLogger'( 'PixelProviderAnalyzeXmpSidecarTask' )

local getBinarySubtypeForBinaryType = PixelProviderSharedTaskUtils.getBinarySubtypeForBinaryType

local WFCrContext = NmbAcr.WFCrContext

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

local LocalBinaryPathCheckStates = PixelPathProviderStateMachine.LocalBinaryPathCheckStates

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

local _AnalyzeXmpSidecarStates = {
		LocalBinaryPathCheckStates.AnalyzeXmpSidecar,
	}

--------------------------------------------------------------------------------
-- setupTask
--------------------------------------------------------------------------------

local function setupTask( docStore, maxConcurrentAnalyzers )

	return {
			docStore = docStore,
			maxConcurrentAnalyzers = maxConcurrentAnalyzers,
			pendingSubscriptions = {}
		}

end

--------------------------------------------------------------------------------
-- processRequests
--------------------------------------------------------------------------------

local function maybeGetUnusedPendingAnalyzerNumber( taskState )

	local pendingAnalyzerNumber

	for i = 1, taskState.maxConcurrentAnalyzers do

		pendingAnalyzerNumber =
			pendingAnalyzerNumber or
			( not taskState.pendingSubscriptions[ i ] and i ) or
			nil

	end

	return pendingAnalyzerNumber

end

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

local function metadataIsFromSidecar( docStore, localAssetId, negativePath, binaryBlob )

	local sidecarExistsObservable

	local metadataFromOz = tostring( binaryBlob )

	-- ACR sets EmbeddedXMPDigest when saving a sidecar so try this cheaper check first
	local sidecarXmpExists = string.find( metadataFromOz, "photoshop:EmbeddedXMPDigest=" ) ~= nil

	if sidecarXmpExists then

		sidecarExistsObservable = Observable.just( sidecarXmpExists )

	else
	
		local context = WFCrContext( negativePath )

		sidecarExistsObservable = context:readMetadata( negativePath )

		:map( function( metadataResults )

			local embeddedXmp = WFXmp.WFXmp()
			embeddedXmp:parse( metadataResults.metadata )
			local embededXmpModifyDate = embeddedXmp:get( WFXMLNS.xap, 'ModifyDate' )
			local embededXmpMetadataDate = embeddedXmp:get( WFXMLNS.xap, 'MetadataDate' )
			embeddedXmp:close()
			embeddedXmp = nil

			local ozXmp = WFXmp.WFXmp()
			ozXmp:parse( metadataFromOz )
			local ozXmpModifyDate = ozXmp:get( WFXMLNS.xap, 'ModifyDate' )
			local ozXmpMetadataDate = ozXmp:get( WFXMLNS.xap, 'MetadataDate' )
			ozXmp:close()
			ozXmp = nil

			return ozXmpModifyDate ~= embededXmpModifyDate or
					ozXmpMetadataDate ~= embededXmpMetadataDate

		end )

		:finally( function()

			context:close()

		end )

	end

	return sidecarExistsObservable:flatMap( function( sidecarXmpExists )

		local overlay = {
			_id = localAssetId,
			_localOnly = {
				xmpSidecarExists = sidecarXmpExists,
			},
		}

		return docStore:applyOverlay( overlay )
			:take( 1 )
			:map( function()
				return sidecarXmpExists
			end )

	end )

end

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

local function processRequests( scheduler, taskState, pendingSaveBlobCache, baseRenderInfoCache )

	local pendingAnalyzerNumber

	repeat

		pendingAnalyzerNumber = pendingAnalyzerNumber or maybeGetUnusedPendingAnalyzerNumber( taskState )

		if not pendingAnalyzerNumber then
			break
		end

		local request =
			{ PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState( 
				scheduler,  unpack( _AnalyzeXmpSidecarStates ) ) }

		local pixStreamId, localAssetId = unpack( request )

		if not pixStreamId then
			-- nothing needs analyzing
			break
		end

		local baseRenderInfo = baseRenderInfoCache:peekCacheItem( localAssetId ) or {}

		if not baseRenderInfo then

			scheduler:_applyTransitionToRequest( request, 'need_docstore_info' )

		else

			scheduler:_applyTransitionToRequest( request, 'start_analyze_xmp_sidecar' )

			local ozBinaryType = 'xmp_sidecar'

			local ozBinarySubtype = getBinarySubtypeForBinaryType( localAssetId,
																ozBinaryType,
																baseRenderInfoCache )

			local binaryBlob = pendingSaveBlobCache:peekBlob( localAssetId,
																ozBinaryType,
																ozBinarySubtype )

			local negativePath = baseRenderInfo.originalPath or baseRenderInfo.proxyPath

			local currPendingAnalyzerNumber = pendingAnalyzerNumber
			pendingAnalyzerNumber = nil

			taskState.pendingSubscriptions[ currPendingAnalyzerNumber ] =

				metadataIsFromSidecar( taskState.docStore, localAssetId, negativePath, binaryBlob )

					:subscribe( function( xmpIsFromSidecar )

						taskState.pendingSubscriptions[ currPendingAnalyzerNumber ] = nil

						logger:infof( "metadataIsFromSidecar for localAssetId %s = %s", localAssetId, xmpIsFromSidecar )

						if xmpIsFromSidecar then

							-- This is a thread callback, so must use _maybeSetNextState.
							scheduler:_maybeSetNextState( pixStreamId, localAssetId, 'save_sidecar_xmp' )

						else

							-- Remove the blob as it's not a sidecar and we just want to discard it.
							pendingSaveBlobCache:popBlob( localAssetId, ozBinaryType, ozBinarySubtype )

							scheduler:_maybeSetNextState( pixStreamId, localAssetId, 'no_sidecar_xmp' )

						end

					end )

		end

	until false

end

--------------------------------------------------------------------------------
-- cleanup
--------------------------------------------------------------------------------

local function cleanup()

end

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

return {

	setupTask = setupTask,

	processRequests = processRequests,

	cleanup = cleanup,

}
