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

PixelProviderCheckFullRenditionDownloadLinkStatusTask.lua

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

ADOBE CONFIDENTIAL
------------------
Copyright 2019 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
local WFFunctionUtils = require 'WFFunctionUtils'

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

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

local WFFunctionUtils_reduce = 
		assert( WFFunctionUtils.reduce,
				"WFFunctionUtils.reduce does not exist" )

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

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

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

local CheckFullRenditionDownloadLinkStatusStates = 
	PixelPathProviderStateMachine.CheckFullRenditionDownloadLinkStatusStates

--==============================================================================
-- setupTask
--==============================================================================

local function setupTask( ozObj )

	return { 

		ozObj = ozObj
			
	}

end

--==============================================================================
-- processRequests
--==============================================================================

local _generateFullSizePOSTOptions = {

	requestMethod = 'POST',

	requestHeaders = {

		{
			header = 'X-Rendition-Type',
			value = 'fullsize',
		},

	},

}

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

local function cancelActiveGenerateFullRenditionRequest( 
				activeGenerateFullRenditionInfo )

	if activeGenerateFullRenditionInfo then

		activeGenerateFullRenditionInfo.subscription:dispose()

	end

end


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

local KSIdGenerator_uuidInLowercaseHex = 
		assert( require 'KSIdGenerator'.uuidInLowercaseHex,
					"missing KSIdGenerator.uuidInLowercaseHex" )
				
--------------------------------------------------------------------------------

local function setupGenerateFullRenditionSubscription( 
					taskState, scheduler, pixStreamId, localAssetId, 
					createFullRenditionURITemplate,
					baseRenderInfoCache )

	local createFullRenditionURI =
			createFullRenditionURITemplate
				:replace( 
					"{rendition_id}", KSIdGenerator_uuidInLowercaseHex() )

	return taskState.ozObj
			:putBinary( createFullRenditionURI, "", 
						_generateFullSizePOSTOptions )
			:subscribe(
				function( _, headers )

					local baseRenderInfo = 
							baseRenderInfoCache:peekCacheItem( localAssetId )

					local fullSizeRenditionDownloadLink = 
							baseRenderInfo and headers.Location 

					if fullSizeRenditionDownloadLink then

						baseRenderInfo.downloadFullRenditionURI =
								fullSizeRenditionDownloadLink

						baseRenderInfoCache:pushCacheItem( localAssetId, baseRenderInfo )
							

					end

					local logLevel = 
						fullSizeRenditionDownloadLink and "infof" or "warnf"

					logger[ logLevel ]( logger, "generate full size renditon request for %s.%s returned status code %s and uri: %s",
											pixStreamId, localAssetId, headers.status_code, fullSizeRenditionDownloadLink )

					local transition = 
							fullSizeRenditionDownloadLink and
								'created_fullsize_rendition' or
								'create_fullsize_request_fail'

					scheduler:_maybeSetNextState( pixStreamId, localAssetId, transition )

					taskState.activeGenerateFullRenditionInfo =
						cancelActiveGenerateFullRenditionRequest(
							taskState.activeGenerateFullRenditionInfo )

				end,
				function( errStr )

					logger:warnf( "Got an error trying to create a full size renditon for assetId %s with url %s: %s",
									localAssetId, createFullRenditionURI,
									errStr )
								
					scheduler
						:_maybeSetNextState( 
							pixStreamId, localAssetId, 'create_fullsize_request_fail' )

					taskState.activeGenerateFullRenditionInfo = nil

				end,
				nil )

end

--------------------------------------------------------------------------------
-- Handle each CheckForAnyFullRenditionLink requests. 
-- 

local function checkFullRenditionLinkStatusForRequestImp( 
					checkForFullSizeDownloadLink, checkForFullSizeCreateLink,
					request, scheduler, taskState, baseRenderInfoCache )

	local pixStreamId, localAssetId = unpack( request )

	local baseRenderInfo = baseRenderInfoCache:peekCacheItem( localAssetId )

	-- 1) check if we're missing basic info
	if not baseRenderInfo then

		logger:warnf( "missing expected baseRenderInfo for %s.%s", 
						pixStreamId, localAssetId )

		scheduler:_applyTransitionToRequest( request, 'need_docstore_info' )

		return

	end

	-- 2) Check if basic info has a download link
	if checkForFullSizeDownloadLink and 
			baseRenderInfo.downloadFullRenditionURI then

		scheduler:_applyTransitionToRequest( request, 'have_fullsize_link' )

		return

	end

	-- 3) Check if we can ask Oz to create a link
	if checkForFullSizeCreateLink and
			baseRenderInfo.createFullRenditionURITemplate then

		if taskState.activeGenerateFullRenditionInfo then

			-- already generating a full rendition, don't start another until
			-- we are done
			return

		end

		taskState.activeGenerateFullRenditionInfo = { 

			pixStreamId = pixStreamId,

			localAssetId = localAssetId,
			
			subscription =
				setupGenerateFullRenditionSubscription( 
					taskState, scheduler, pixStreamId, localAssetId,
					baseRenderInfo.createFullRenditionURITemplate, 
					baseRenderInfoCache )

		}

		scheduler:_applyTransitionToRequest( 
					request, 'sent_create_fullsize_request' )

		return

	end

	-- 4) We can't process this item. This is unexpected, so we log a warning
	logger:warnf( "Unable to download or create full size rendition for %s.%s", 
					pixStreamId, localAssetId )

	scheduler:_applyTransitionToRequest( request, 'no_fullsize_links' )

end

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

local function checkForAnyFullRenditionLinkForRequest( 
					request, scheduler, taskState, baseRenderInfoCache )

	return checkFullRenditionLinkStatusForRequestImp(
			true, true, request, scheduler, taskState, baseRenderInfoCache )

end

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

local function checkForCreateFullRenditionLinkForRequest( 
					request, scheduler, taskState, baseRenderInfoCache )

	return checkFullRenditionLinkStatusForRequestImp(
			false, true, request, scheduler, taskState, baseRenderInfoCache )

end

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

local function checkForUpdatedFullRenditionLinkForRequest( 
					request, scheduler, taskState, baseRenderInfoCache )

	return checkFullRenditionLinkStatusForRequestImp(
			true, false, request, scheduler, taskState, baseRenderInfoCache )

end

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

local PixelPathProviderSchedulerPrivateMethods_getDeletedRequestsWithState =
	PixelPathProviderSchedulerPrivateMethods.getDeletedRequestsWithState

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

local function isActiveGenerateFullRenditionInfoRequestDeleted( 
				isDeleted, deletedWaitingForFullRendtionCreationRequest,
				activeGenerateFullRenditionPixStreamId,
				activeGenerateFullRenditionLocalAssetId )

	if isDeleted then

		return true

	end

	local pixStreamId, localAssetId = 
			unpack( deletedWaitingForFullRendtionCreationRequest )

	return pixStreamId == activeGenerateFullRenditionPixStreamId and
			localAssetId == activeGenerateFullRenditionLocalAssetId

end

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

local function processRequests( scheduler, taskState, baseRenderInfoCache )

	-- is our create rendition link request deleted?
	if taskState.activeGenerateFullRenditionInfo then

		local deletedWaitingForFullRendtionCreationRequests =
				PixelPathProviderSchedulerPrivateMethods_getDeletedRequestsWithState(
					scheduler,
					CheckFullRenditionDownloadLinkStatusStates.WaitingForFullRenditionLinkCreation )

		if WFFunctionUtils_reduce( 
				false,
				isActiveGenerateFullRenditionInfoRequestDeleted,
					deletedWaitingForFullRendtionCreationRequests,
					taskState.activeGenerateFullRenditionInfo.pixStreamId,
					taskState.activeGenerateFullRenditionInfo.localAssetId ) then

			taskState.activeGenerateFullRenditionInfo =
				cancelActiveGenerateFullRenditionRequest( 
					taskState.activeGenerateFullRenditionInfo )

		end

	end

	-- handle the rest of the requests
	scheduler:_mapAllRequestsWithState( 
		CheckFullRenditionDownloadLinkStatusStates.CheckForAnyFullRenditionLink,
			checkForAnyFullRenditionLinkForRequest,
				scheduler, taskState, baseRenderInfoCache )

	scheduler:_mapAllRequestsWithState( 
		CheckFullRenditionDownloadLinkStatusStates.CheckForCreateFullRenditionLink,
			checkForCreateFullRenditionLinkForRequest,
				scheduler, taskState, baseRenderInfoCache )

	scheduler:_mapAllRequestsWithState( 
		CheckFullRenditionDownloadLinkStatusStates.CheckForUpdatedFullRenditionLink,
			checkForUpdatedFullRenditionLinkForRequest,
				scheduler, taskState, baseRenderInfoCache )

end

--==============================================================================
-- cleanup
--==============================================================================

local function cleanup( taskState )

	taskState.activeGenerateFullRenditionInfo =
		cancelActiveGenerateFullRenditionRequest( 
			taskState.activeGenerateFullRenditionInfo )

end


--==============================================================================
-- cleanup
--==============================================================================

local function injectMockPutBinaryFunc( taskState, mockPutBinaryFunc )

	taskState.ozObj = {

		putBinary = mockPutBinaryFunc 

	}

	return taskState

end

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

return {

	setupTask = setupTask,

	processRequests = processRequests,

	cleanup = cleanup,

	privateUnitTestExport = {

		injectMockPutBinaryFunc = injectMockPutBinaryFunc

	}

}
