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

DownloadToFileStateMachine.lua

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

ADOBE CONFIDENTIAL
------------------
Copyright 2024 Adobe
All Rights Reserved.

NOTICE: All information contained herein is, and remains
the property of Adobe and its suppliers, if any. The intellectual
and technical concepts contained herein are proprietary to Adobe
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.
------------------------------------------------------------------------------]]

	-- wf.core
local WFFiniteStateMachine = require 'WFFiniteStateMachine'
local WFFunctionUtils = require 'WFFunctionUtils'

--==============================================================================
-- This file encapsulates all of the states we go through when downloading
-- directly to a file.
--==============================================================================

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

local _downloadToFileStateGroupName = "DownloadToFileStates"

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

local function constructDownloadToFileStateGroup( fsm )

	return WFFiniteStateMachine.addGroupedStates(
			fsm, _downloadToFileStateGroupName,
			{

				'NeedProxyDownloadToFile', 
				'DownloadingProxyToFile',

				'NeedProxyDownloadToFileForRender', 
				'DownloadingProxyToFileForRender',

				'NeedProxyDownloadToFileForBestPossibleLocalNegativePath', 
				'DownloadingProxyToFileForBestPossibleLocalNegativePath',

				'NeedOriginalDownloadToFile', 
				'DownloadingOriginalToFile',

				'NeedOriginalDownloadToFileForRender', 
				'DownloadingOriginalToFileForRender',

				'NeedOriginalDownloadToFileForBestPossibleLocalNegativePath', 
				'DownloadingOriginalToFileForBestPossibleLocalNegativePath', 

			} )

end

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

local CustomStateGroupPairs = 
	{
		{ _downloadToFileStateGroupName, constructDownloadToFileStateGroup } 
	}

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

local _DownloadToFileErrorSendDataStateNames = {
	'CannotDownloadToFileError',
	'CacheMissDownloadToFileError',
	'CannotSyncDownloadToFileError',
	'VolumeCannotSaveDownloadToFileError',
	'FatalDownloadToFileError',
	'TransientDownloadToFileError',
	'CannotDownloadBestPossibleNegativeToFileError',
	'CacheMissDownloadBestPossibleNegativeToFileError',
	'CannotSyncDownloadBestPossibleNegativeToFileError',
	'VolumeCannotSaveDownloadBestPossibleNegativeToFileError',
	'FatalDownloadBestPossibleNegativeToFileError',
	'TransientDownloadBestPossibleNegativeToFileError',
}

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

local function addSendDataStates( fsm, sendDataStateGroupName )

	return WFFiniteStateMachine.addGroupedStates( 
			fsm, sendDataStateGroupName, 
			_DownloadToFileErrorSendDataStateNames )

end

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

local function addToNeedsDownloadTransitions( 
				fsm, PixelPathProviderStateMachine )

	local LocalBinaryPathCheckStates = PixelPathProviderStateMachine.LocalBinaryPathCheckStates

	local DownloadToFileStates = PixelPathProviderStateMachine.DownloadToFileStates

	local PixelCacheCheckStates = PixelPathProviderStateMachine.PixelCacheCheckStates

	local SendDataStates = PixelPathProviderStateMachine.SendDataStates

	fsm = WFFiniteStateMachine.addTransitions( 
			fsm, LocalBinaryPathCheckStates.CheckingForLocalOriginal, 
			{
				{ 'need_download_original_to_file', DownloadToFileStates.NeedOriginalDownloadToFile },
			} )

	fsm = WFFiniteStateMachine.addTransitions( 
			fsm, LocalBinaryPathCheckStates.CheckingForAnyLocalNegativeForRender, 
			{
				{ 'need_download_original_to_file', DownloadToFileStates.NeedOriginalDownloadToFileForRender },
				{ 'need_download_proxy_to_file', DownloadToFileStates.NeedProxyDownloadToFileForRender },
			} )

	fsm = WFFiniteStateMachine.addTransitions( 
			fsm, LocalBinaryPathCheckStates.CheckingForLocalProxyForRender, 
			{
				{ 'need_download_proxy_to_file', DownloadToFileStates.NeedProxyDownloadToFileForRender },
			} )

	fsm = WFFiniteStateMachine.addTransitions( 
			fsm, LocalBinaryPathCheckStates.CheckingForLocalProxy, 
			{
				{ 'need_download_proxy_to_file', DownloadToFileStates.NeedProxyDownloadToFile },
			} )

	fsm = WFFiniteStateMachine.addTransitions( 
			fsm, LocalBinaryPathCheckStates.CheckingForAnyLocalNegative, 
			{
				{ 'need_download_proxy_to_file', DownloadToFileStates.NeedProxyDownloadToFile },
				{ 'need_download_original_to_file', DownloadToFileStates.NeedOriginalDownloadToFile },
			} )

	fsm = WFFiniteStateMachine.addTransitions( 
			fsm, LocalBinaryPathCheckStates.CheckingOriginalForBestPossibleLocalNegativePath,
			{
				{ 'need_download_original_to_file', DownloadToFileStates.NeedOriginalDownloadToFileForBestPossibleLocalNegativePath },
			} )

	fsm = WFFiniteStateMachine.addTransitions( 
			fsm, LocalBinaryPathCheckStates.CheckingProxyForBestPossibleLocalNegativePath,
			{
				{ 'need_download_proxy_to_file', DownloadToFileStates.NeedProxyDownloadToFileForBestPossibleLocalNegativePath },
			} )

	fsm = WFFiniteStateMachine.addTransitions( 
			fsm, PixelCacheCheckStates.ChooseRenditionStrategy, 
			{
				{ 'need_download_proxy_to_file', DownloadToFileStates.NeedProxyDownloadToFileForRender },
				{ 'need_download_original_to_file', DownloadToFileStates.NeedOriginalDownloadToFileForRender },
			} )

	fsm = WFFiniteStateMachine.addTransitions( 
			fsm, LocalBinaryPathCheckStates.ChooseBestPossibleLocalNegativePath, 
			{
				{ 'need_download_original_to_file', DownloadToFileStates.NeedOriginalDownloadToFileForBestPossibleLocalNegativePath },
				{ 'need_download_proxy_to_file', DownloadToFileStates.NeedProxyDownloadToFileForBestPossibleLocalNegativePath },
			} )

	fsm = WFFiniteStateMachine.addTransitions( 
			fsm, SendDataStates.HaveProxyForBestPossibleLocalNegativePath,
			{
				{ 'sent_path_need_download_original_to_file', DownloadToFileStates.NeedOriginalDownloadToFileForBestPossibleLocalNegativePath },
			} )

	return fsm

end

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

local function addFromNeedsDownloadTransitions( 
				fsm, PixelPathProviderStateMachine )

	local DownloadToFileStates = PixelPathProviderStateMachine.DownloadToFileStates
	local SendDataStates = PixelPathProviderStateMachine.SendDataStates

	fsm = WFFiniteStateMachine.addTransitions(
			fsm, DownloadToFileStates.NeedOriginalDownloadToFile,
			{
				-- errors
				{ 'cannot_download', SendDataStates.CannotDownloadToFileError },
				{ 'missing_download_info', SendDataStates.CacheMissDownloadToFileError },
				{ 'no_internet', SendDataStates.CannotSyncDownloadToFileError },
				{ 'volume_cannot_save', SendDataStates.VolumeCannotSaveDownloadToFileError },

				-- will download
				{ 'start_original_download_to_file', DownloadToFileStates.DownloadingOriginalToFile },
			} )

	fsm = WFFiniteStateMachine.addTransitions(
			fsm, DownloadToFileStates.NeedOriginalDownloadToFileForRender,
			{
				-- errors
				{ 'cannot_download', SendDataStates.CannotDownloadToFileError },
				{ 'missing_download_info', SendDataStates.CacheMissDownloadToFileError },
				{ 'no_internet', SendDataStates.CannotSyncDownloadToFileError },
				{ 'volume_cannot_save', SendDataStates.VolumeCannotSaveDownloadToFileError },

				-- will download
				{ 'start_original_download_to_file', DownloadToFileStates.DownloadingOriginalToFileForRender },
			} )

	fsm = WFFiniteStateMachine.addTransitions(
			fsm, DownloadToFileStates.NeedOriginalDownloadToFileForBestPossibleLocalNegativePath,
			{
				-- errors
				{ 'cannot_download', SendDataStates.CannotDownloadBestPossibleNegativeToFileError },
				{ 'missing_download_info', SendDataStates.CacheMissDownloadBestPossibleNegativeToFileError },
				{ 'no_internet', SendDataStates.CannotSyncDownloadBestPossibleNegativeToFileError },
				{ 'volume_cannot_save', SendDataStates.VolumeCannotSaveDownloadBestPossibleNegativeToFileError },

				-- will download
				{ 'start_original_download_to_file', DownloadToFileStates.DownloadingOriginalToFileForBestPossibleLocalNegativePath },
			} )

	fsm = WFFiniteStateMachine.addTransitions(
			fsm, DownloadToFileStates.NeedProxyDownloadToFile,
			{
				-- errors
				{ 'cannot_download', SendDataStates.CannotDownloadToFileError },
				{ 'missing_download_info', SendDataStates.CacheMissDownloadToFileError },
				{ 'no_internet', SendDataStates.CannotSyncDownloadToFileError },
				{ 'volume_cannot_save', SendDataStates.VolumeCannotSaveDownloadToFileError },

				-- will download
				{ 'start_proxy_download_to_file', DownloadToFileStates.DownloadingProxyToFile },
			} )

	fsm = WFFiniteStateMachine.addTransitions(
			fsm, DownloadToFileStates.NeedProxyDownloadToFileForRender,
			{
				-- errors
				{ 'cannot_download', SendDataStates.CannotDownloadToFileError },
				{ 'missing_download_info', SendDataStates.CacheMissDownloadToFileError },
				{ 'no_internet', SendDataStates.CannotSyncDownloadToFileError },
				{ 'volume_cannot_save', SendDataStates.VolumeCannotSaveDownloadToFileError },

				-- will download
				{ 'start_proxy_download_to_file', DownloadToFileStates.DownloadingProxyToFileForRender },
			} )

	fsm = WFFiniteStateMachine.addTransitions(
			fsm, DownloadToFileStates.NeedProxyDownloadToFileForBestPossibleLocalNegativePath,
			{
				-- errors
				{ 'cannot_download', DownloadToFileStates.NeedOriginalDownloadToFileForBestPossibleLocalNegativePath },
				{ 'missing_download_info', SendDataStates.CacheMissDownloadBestPossibleNegativeToFileError },
				{ 'no_internet', SendDataStates.CannotSyncDownloadBestPossibleNegativeToFileError },
				{ 'volume_cannot_save', SendDataStates.VolumeCannotSaveDownloadBestPossibleNegativeToFileError },

				-- will download
				{ 'start_proxy_download_to_file', DownloadToFileStates.DownloadingProxyToFileForBestPossibleLocalNegativePath },
			} )

	return fsm

end

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

local function addFromDownloadingToFileTransitions( 
				fsm, PixelPathProviderStateMachine )

	local DownloadToFileStates = PixelPathProviderStateMachine.DownloadToFileStates
	local RenderStates = PixelPathProviderStateMachine.RenderStates
	local SendDataStates = PixelPathProviderStateMachine.SendDataStates

	fsm = WFFiniteStateMachine.addTransitions(
		fsm, DownloadToFileStates.DownloadingProxyToFile,
		{
			{ 'download_to_file_finished', SendDataStates.HaveLocalNegativePath },
			{ 'download_to_file_fatal_fail', SendDataStates.FatalDownloadToFileError },
			{ 'download_to_file_transient_fail', SendDataStates.TransientDownloadToFileError },
			{ 'reschedule_download', DownloadToFileStates.NeedProxyDownloadToFile },
		} )

	fsm = WFFiniteStateMachine.addTransitions(
		fsm, DownloadToFileStates.DownloadingProxyToFileForRender,
		{
			{ 'download_to_file_finished', RenderStates.NeedsRender },
			{ 'download_to_file_fatal_fail', SendDataStates.FatalDownloadToFileError },
			{ 'download_to_file_transient_fail', SendDataStates.TransientDownloadToFileError },
			{ 'reschedule_download', DownloadToFileStates.NeedProxyDownloadToFileForRender },
		} )

	fsm = WFFiniteStateMachine.addTransitions(
		fsm, DownloadToFileStates.DownloadingProxyToFileForBestPossibleLocalNegativePath,
		{
			{ 'download_to_file_finished', SendDataStates.HaveProxyForBestPossibleLocalNegativePath },
			{ 'download_to_file_fatal_fail', DownloadToFileStates.NeedOriginalDownloadToFileForBestPossibleLocalNegativePath },
			{ 'download_to_file_transient_fail', DownloadToFileStates.NeedOriginalDownloadToFileForBestPossibleLocalNegativePath },
			{ 'reschedule_download', DownloadToFileStates.NeedProxyDownloadToFileForBestPossibleLocalNegativePath },
		} )

	fsm = WFFiniteStateMachine.addTransitions(
		fsm, DownloadToFileStates.DownloadingOriginalToFile,
		{
			{ 'download_to_file_finished', SendDataStates.HaveLocalNegativePath },
			{ 'download_to_file_fatal_fail', SendDataStates.FatalDownloadToFileError },
			{ 'download_to_file_transient_fail', SendDataStates.TransientDownloadToFileError },
			{ 'reschedule_download', DownloadToFileStates.NeedOriginalDownloadToFile },
		} )

	fsm = WFFiniteStateMachine.addTransitions(
		fsm, DownloadToFileStates.DownloadingOriginalToFileForRender,
		{
			{ 'download_to_file_finished', RenderStates.NeedsRender },
			{ 'download_to_file_fatal_fail', SendDataStates.FatalDownloadToFileError },
			{ 'download_to_file_transient_fail', SendDataStates.TransientDownloadToFileError },
			{ 'reschedule_download', DownloadToFileStates.NeedOriginalDownloadToFileForRender },
		} )

	fsm = WFFiniteStateMachine.addTransitions(
		fsm, DownloadToFileStates.DownloadingOriginalToFileForBestPossibleLocalNegativePath,
		{ 
			{ 'download_to_file_finished', SendDataStates.HaveOriginalForBestPossibleLocalNegativePath },
			{ 'download_to_file_fatal_fail', SendDataStates.FatalDownloadBestPossibleNegativeToFileError },
			{ 'download_to_file_transient_fail', SendDataStates.TransientDownloadBestPossibleNegativeToFileError },
			{ 'reschedule_download', DownloadToFileStates.NeedOriginalDownloadToFileForBestPossibleLocalNegativePath },
		} )

	return fsm

end

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

local function addSendErrorTransitionForState( 
				fsm, downloadToFileErrorSendDataStateName, endStateName )

	return WFFiniteStateMachine.addTransitions( 
		fsm, downloadToFileErrorSendDataStateName, 
		{

			{ 'send_error', endStateName },

		} )

end

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

local function addSendErrorTransitions( fsm, PixelPathProviderStateMachine )

	local EndStates = PixelPathProviderStateMachine.EndStates

	-- add send_error transition to all the download to file error states
	return WFFunctionUtils.reduce( fsm, 
				addSendErrorTransitionForState,
					_DownloadToFileErrorSendDataStateNames,
					EndStates.PermanentDownloadError )

end

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

local function addTransitions( fsm, PixelPathProviderStateMachine )

	fsm = addToNeedsDownloadTransitions( fsm, PixelPathProviderStateMachine )

	fsm = addFromNeedsDownloadTransitions( fsm, PixelPathProviderStateMachine )

	fsm = addFromDownloadingToFileTransitions( fsm, PixelPathProviderStateMachine )

	fsm = addSendErrorTransitions( fsm, PixelPathProviderStateMachine )

	return fsm

end

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

return {

	CustomStateGroupPairs = CustomStateGroupPairs,

	addSendDataStates = addSendDataStates,

	addTransitions = addTransitions,

}
