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

PixelPathProviderStateMachine.lua

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

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
local WFFunctionUtils = require 'WFFunctionUtils'
local WFFiniteStateMachine = require 'WFFiniteStateMachine'

	-- wf.lightroomcc.pixel_provider.scheduler.state_machine
local LoupeRenditionStateMachine = require 'LoupeRenditionStateMachine'
local DownloadInvalidRenderStateMachine = require 'DownloadInvalidRenderStateMachine'
local ExportFullRenditionStateMachine = require 'ExportFullRenditionStateMachine'
local ExportBiggestPossibleRenditionStateMachine = require 'ExportBiggestPossibleRenditionStateMachine'
local XmpDevelopStateMachine = require 'XmpDevelopStateMachine'
local DownloadToFileStateMachine = require 'DownloadToFileStateMachine'
local OriginalOrFallbackProxyLocalNegativeStateMachine = require 'OriginalOrFallbackProxyLocalNegativeStateMachine'

local logger = require 'WFLogger'( 'PixelPathProviderStateMachine' )
--==============================================================================
-- This file encapsulates all of the states we can go through when processing
-- a pixel path request. In summary, we need to something like this:
--
-- 	a) find out info about an asset from the doc store
-- 	b) see if we've already rendered a path that meets our needs
-- 	c) see if we can download a render that meets our needs
-- 	d) see if we have or can download a proxy/original that meets our needs
-- 	e) do the render
-- 	f) handle invalidation (i.e. new settings while we render)
-- 	g) save off the file.
-- 	h) handle errors and retry operations when possible
--
-- 	We need to manage this state per asset for many assets.
--
-- 	So we create a state machine that can encapsulate where we are in the 
-- 	process.
--
-- 	The state machine breaks down this process via state groups, which cluster
-- 	related states. One example is DocstoreInfoStates which are all the states 
-- 	related to fetching asset info from the doc store.
--
-- 	To add new state groups (or modify existing state groups), there are three 
-- 	things you need to do:
--
-- 	1) Define the state group name
--
-- 	2) Define an StateGroupFSMConstructor entry for the state group name
--
-- 	3) Add transitions for the state
--
-- 	Each of these tasks is marked below in the comments.
--==============================================================================

--------------------------------------------------------------------------------
-- Define shortcuts for all the WFFiniteStateMachine functions we are using
--------------------------------------------------------------------------------

local addGroupedStates = WFFiniteStateMachine.addGroupedStates

local getStateGroupMap = WFFiniteStateMachine.getStateGroupMap 

local makeRetryableStates = WFFiniteStateMachine.makeRetryableStates

local addRetryTransitions = WFFiniteStateMachine.addRetryTransitions

local addTransitionToAllRetryStates = 
		WFFiniteStateMachine.addTransitionToAllRetryStates 

local addTransitions = WFFiniteStateMachine.addTransitions

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

local function addNoOpTransition( fsm, state, transition )

	return addTransitions( fsm, state, { { transition, state } } )

end

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

local function addNoOpTranstionToAllRetryStates( fsm, state, retryCount,
													transition )

	local retryStates = { makeRetryableStates( retryCount, state ) }

	return WFFunctionUtils.reduce( fsm,
									addNoOpTransition,
										retryStates, transition )

end

--==============================================================================
-- 	1) Define the state group name
--==============================================================================

local StateGroupNameArray = { 'DocstoreInfoStates',
								'PixelCacheCheckStates',
								'LocalBinaryPathCheckStates',
								'DownloadStates',
								'RenderStates',
								'SaveRenderStates',
								'SaveDownloadStates',
								'SendDataStates',
								'EndStates' }

--==============================================================================
-- retry counts
--==============================================================================

local _downloadRetryCount = 3

local _renderRetryCount = 2

local _saveRenderRetryCount = 2

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

local function addToStateGroup( fsm, name, ... )

	return addGroupedStates( fsm, name, { ... } )

end

--==============================================================================
-- 	2) Define an StateGroupFSMConstructor entry for the state group name
--
-- 	This is where we add all of the states to a state group
--==============================================================================

local StateGroupFSMConstructor = {}

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

StateGroupFSMConstructor.DocstoreInfoStates = function( fsm )

	fsm = addToStateGroup( 
			fsm, "DocstoreInfoStates",
					'NeedAssetInfoForRender',
					'NeedAssetInfoForRenderFromDocstore',
					'FetchingAssetInfoForRender',
					'NeedCheckRenderSettings' )
	
	fsm = LoupeRenditionStateMachine.addDocStoreInfoStates(  
			fsm, "DocstoreInfoStates" )
	
	fsm = ExportFullRenditionStateMachine.addDocStoreInfoStates(  
			fsm, "DocstoreInfoStates" )
	
	fsm = ExportBiggestPossibleRenditionStateMachine.addDocStoreInfoStates(  
			fsm, "DocstoreInfoStates" )

	fsm = addToStateGroup( 
			fsm, "DocstoreInfoStates",
					'NeedDownloadOriginalInfo',
					'NeedDownloadOriginalInfoFromDocstore',
					'FetchingDownloadOriginalInfo' )

	fsm = addToStateGroup( 
			fsm, "DocstoreInfoStates",
					'NeedDownloadProxyInfo',
					'NeedDownloadProxyInfoFromDocstore',
					'FetchingDownloadProxyInfo' )

	fsm = addToStateGroup( 
			fsm, "DocstoreInfoStates",
					'NeedBestPossibleLocalNegativePathInfo',
					'NeedBestPossibleLocalNegativePathInfoFromDocstore',
					'FetchingBestPossibleLocalNegativePathInfo' )

	fsm = addToStateGroup( 
			fsm, "DocstoreInfoStates",
					'NeedAnyLocalNegativePathInfo',
					'NeedAnyLocalNegativePathInfoFromDocstore',
					'FetchingAnyLocalNegativePathInfo' )

	fsm = OriginalOrFallbackProxyLocalNegativeStateMachine.addDocStoreInfoStates(  
			fsm, "DocstoreInfoStates" )
	
	fsm = XmpDevelopStateMachine.addDocStoreInfoStates(  
			fsm, "DocstoreInfoStates" )
	
	fsm = addToStateGroup(
			fsm, "DocstoreInfoStates",
					'NeedXmpSidecarInfo',
					'NeedXmpSidecarInfoFromDocstore',
					'FetchingXmpSidecarInfo'
					)

	return fsm

end

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

StateGroupFSMConstructor.PixelCacheCheckStates = function( fsm )

	fsm = addToStateGroup(
				fsm, "PixelCacheCheckStates", 
					'NeedCheckForPixelPathInCache',
					'CheckingForPixelPathInCache',
					'ChooseRenditionStrategy',
					'ChooseRenderOnlyRenditionStrategy' )

	fsm = LoupeRenditionStateMachine.addPixelCacheCheckStates(  
			fsm, "PixelCacheCheckStates" )

	fsm = ExportFullRenditionStateMachine.addPixelCacheCheckStates(  
			fsm, "PixelCacheCheckStates" )

	fsm = ExportBiggestPossibleRenditionStateMachine.addPixelCacheCheckStates(  
			fsm, "PixelCacheCheckStates" )

	return fsm

end

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

StateGroupFSMConstructor.LocalBinaryPathCheckStates = function( fsm )

	fsm = addToStateGroup(
				fsm, "LocalBinaryPathCheckStates", 
					'NeedCheckAnyLocalNegativeForRender',
					'CheckingForAnyLocalNegativeForRender',
					'NeedCheckLocalProxyForRender',
					'CheckingForLocalProxyForRender',
					'NeedCheckForLocalOriginal',
					'CheckingForLocalOriginal',
					'NeedCheckForLocalProxy',
					'CheckingForLocalProxy',
					'NeedCheckForAnyLocalNegative',
					'CheckingForAnyLocalNegative',
					'ChooseBestPossibleLocalNegativePath',
					'NeedCheckOriginalForBestPossibleLocalNegativePath',
					'CheckingOriginalForBestPossibleLocalNegativePath',
					'NeedCheckProxyForBestPossibleLocalNegativePath',
					'CheckingProxyForBestPossibleLocalNegativePath',
					'NeedCheckForLocalXmpSidecar',
					'CheckingForLocalXmpSidecar',
					'AnalyzeXmpSidecar',
					'AnalyzingXmpSidecar' )

	fsm = XmpDevelopStateMachine.addLocalBinaryPathCheckStates(  
			fsm, "LocalBinaryPathCheckStates" )

	fsm = OriginalOrFallbackProxyLocalNegativeStateMachine.addLocalBinaryPathCheckStates(  
			fsm, "LocalBinaryPathCheckStates" )

	return fsm

end

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

StateGroupFSMConstructor.DownloadStates = function( fsm )

	fsm = addToStateGroup(
				fsm, "DownloadStates", 
					makeRetryableStates( _downloadRetryCount,
						'NeedRenderDownload', 
						'DownloadingRender',
						'NeedProxyDownload', 
						'DownloadingProxy',
						'NeedProxyDownloadForRender', 
						'DownloadingProxyForRender',
						'NeedOriginalDownload', 
						'DownloadingOriginal',
						'NeedOriginalDownloadForRender', 
						'DownloadingOriginalForRender',
						'NeedXmpSidecarDownload',
						'DownloadingXmpSidecar',
						'NeedOriginalDownloadForBestPossibleLocalNegativePath', 
						'DownloadingOriginalForBestPossibleLocalNegativePath', 
						'NeedProxyDownloadForBestPossibleLocalNegativePath', 
						'DownloadingProxyForBestPossibleLocalNegativePath' ) )

	fsm = LoupeRenditionStateMachine.addDownloadStates(  
			fsm, "DownloadStates" )

	fsm = XmpDevelopStateMachine.addDownloadStates(  fsm, "DownloadStates" )

	fsm = DownloadInvalidRenderStateMachine.addDownloadStates(  
			fsm, "DownloadStates" )

	fsm = ExportFullRenditionStateMachine.addDownloadStates(  
			fsm, "DownloadStates" )

	fsm = ExportBiggestPossibleRenditionStateMachine.addDownloadStates(  
			fsm, "DownloadStates" )
			
	fsm = OriginalOrFallbackProxyLocalNegativeStateMachine.addDownloadStates(  
			fsm, "DownloadStates" )

	return fsm

end

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

StateGroupFSMConstructor.RenderStates = function( fsm )

	fsm = addToStateGroup(
				fsm, "RenderStates",
						makeRetryableStates( _renderRetryCount,
							'NeedsRender', 
							'RenderScheduled' ) )

	return fsm

end

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

StateGroupFSMConstructor.SaveRenderStates = function( fsm )

	fsm = addToStateGroup(
				fsm, "SaveRenderStates",
						makeRetryableStates( _saveRenderRetryCount,
							'NeedToSaveRender',
							'SavingRender') )

	fsm = LoupeRenditionStateMachine.addSaveRenderStates(  
			fsm, "SaveRenderStates" )

	fsm = ExportFullRenditionStateMachine.addSaveRenderStates(  
			fsm, "SaveRenderStates" )

	fsm = ExportBiggestPossibleRenditionStateMachine.addSaveRenderStates(  
			fsm, "SaveRenderStates" )

	return fsm

end

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

StateGroupFSMConstructor.SaveDownloadStates = function( fsm )

	fsm = addToStateGroup(
				fsm, "SaveDownloadStates",
						'NeedToSaveOriginalForRender',
						'SavingOriginalForRender',
						'NeedToSaveProxyForRender',
						'SavingProxyForRender',
						'NeedToSaveOriginal',
						'SavingOriginal',
						'NeedToSaveProxy',
						'SavingProxy',
						'NeedToSaveOriginalForBestPossibleLocalNegativePath',
						'SavingOriginalForBestPossibleLocalNegativePath',
						'NeedToSaveProxyForBestPossibleLocalNegativePath',
						'SavingProxyForBestPossibleLocalNegativePath',
						'NeedToSaveXmpSidecar',
						'SavingXmpSidecar' ) 


	fsm = XmpDevelopStateMachine.addSaveDownloadStates(  
			fsm, "SaveDownloadStates" )
	
	return fsm

end

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

StateGroupFSMConstructor.SendDataStates = function( fsm )

	fsm = addToStateGroup(
				fsm, "SendDataStates",
						'HaveRenditionPath', 
						'HaveLocalNegativePath',
						'SendNoDownloadableRenderOrBinary',
						'SendNoDownloadableOriginal',
						'SendNoDownloadableBinary',
						'SendNoDownloadableBinaryForRender',
						'SendNoDownloadableXmpSidecar',
						'SendPermanentDownloadError',
						'SendPermanentBadAssetIdError',
						'SendPermanentError',
						'SendPermanentMissingProfileRenditionPathError',
						'SendPermanentDownloadErrorForBestPossibleLocalNegativePath',
						'SendPermanentErrorForBestPossibleLocalNegativePath',
						'HaveOriginalForBestPossibleLocalNegativePath',
						'HaveProxyForBestPossibleLocalNegativePath',
						'HaveLocalXmpSidecarPath',
						'NotXmpSidecar' )

	fsm = LoupeRenditionStateMachine.addSendDataStates(  
			fsm, "SendDataStates" )

	fsm = ExportFullRenditionStateMachine.addSendDataStates(  
			fsm, "SendDataStates" )

	fsm = ExportBiggestPossibleRenditionStateMachine.addSendDataStates(  
			fsm, "SendDataStates" )

	fsm = XmpDevelopStateMachine.addSendDataStates(  fsm, "SendDataStates" )

	fsm = DownloadToFileStateMachine.addSendDataStates(  fsm, "SendDataStates" )

	return fsm

end

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

StateGroupFSMConstructor.EndStates = function( fsm )

	fsm = addToStateGroup(
				fsm, "EndStates",
						'NoDownloadableRenderOrBinary',
						'NoDownloadableOriginal',
						'NoDownloadableBinary',
						'NoDownloadableBinaryForRender',
						'NoDownloadableXmpSidecar',
						'PermanentDownloadError',
						'PermanentError',
						'PermanentBadAssetIdError',
						'PermanentMissingProfileRenditionPathError',
						'WaitForDownloadableOriginalForBestPossibleLocalNegativePath',
						'FinishedBestPossibleNegativePath',
						'FinishedSendingRender',
						'FinishedSendingNegativePath',
						'FinishedSendingXmpSidecarPath' )

	fsm = LoupeRenditionStateMachine.addEndStates(  
			fsm, "EndStates" )

	fsm = ExportFullRenditionStateMachine.addEndStates(  
			fsm, "EndStates" )

	fsm = ExportBiggestPossibleRenditionStateMachine.addEndStates(  
			fsm, "EndStates" )

	fsm = XmpDevelopStateMachine.addEndStates(  fsm, "EndStates" )

	return fsm

end

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

local function addCustomStateGroupPair( customStateGroupPair )

	local stateGroupName, stateGroupFSMConstructor = 
			unpack( customStateGroupPair )

	assert( type( stateGroupName ) == 'string', 
			"stateGroupName must be a string" )

	assert( type( stateGroupFSMConstructor ) == 'function',
			"stateGroupFSMConstructor must be a function" )

	assert( not StateGroupFSMConstructor[ stateGroupName ],
			string.format( "State group name already exists: %s", 
							stateGroupName ) )

	StateGroupNameArray[ #StateGroupNameArray + 1 ] = stateGroupName

	StateGroupFSMConstructor[ stateGroupName ] = stateGroupFSMConstructor

	return StateGroupNameArray, StateGroupFSMConstructor

end

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

local function addCustomStateGroupPairs( customStateGroupPairs )

	assert( type( customStateGroupPairs ) == 'table',
				"customStateGroupPairs must be a table" )

	for _, customStateGroupPair in ipairs( customStateGroupPairs ) do

		StateGroupNameArray, StateGroupFSMConstructor = 
			addCustomStateGroupPair( customStateGroupPair )

	end

	return StateGroupNameArray, StateGroupFSMConstructor

end

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

StateGroupNameArray, StateGroupFSMConstructor =
	addCustomStateGroupPairs(
		ExportFullRenditionStateMachine.CustomStateGroupPairs )

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

StateGroupNameArray, StateGroupFSMConstructor =
	addCustomStateGroupPairs(
		DownloadToFileStateMachine.CustomStateGroupPairs )

--==============================================================================
-- make sure StateGroupFSMConstructor[ stateGroupName ] functions can be called with or without
-- an fsm
--==============================================================================

local function wrapInMaybeCreateFSM( f )

	return function( maybeFSM )

		return f( maybeFSM or WFFiniteStateMachine.make() )

	end

end

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

for k, f in pairs( StateGroupFSMConstructor ) do

	StateGroupFSMConstructor[ k ] = wrapInMaybeCreateFSM( f )

end

--------------------------------------------------------------------------------
-- A state group namespace is a simple lua table that has all the state group's
-- state names as key/values. This allows the state group to be accessed
-- outside this file. The namespace is extracted automatically
--
-- For example, 
-- PixelPathProviderStateMachine.DocstoreInfoStates = 
-- 		getStateGroupNamespace( "DocstoreInfoStates" )
--
-- generates:
--
-- PixelPathProviderStateMachine.DocstoreInfoStates = { 
-- 					NeedAssetInfoForRender = 'NeedAssetInfoForRender',
-- 					FetchingAssetInfoForRender = 'FetchingAssetInfoForRender',
-- 					}
--
-- 	That means other files can get state names like this:
--
-- 	if state == PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender then
-- 		-- blah
-- 	end
--
--------------------------------------------------------------------------------

local function makeReadOnlyMetamethod( tableName )

	return 
		function()

			error( string.format( "%s is read only", tableName ) )

		end

end

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

local function makeStrictAccessMetamethod( tableName )

	return 
		function( t, k )

			local v = rawget( t, k )

			if not v then

				logger:errorf( "PixelPathProviderStateMachine.%s.%s is not defined", tableName, k )

			end

			return v

		end

end

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

local function ONLY_IF( predicateVal, val )

	if predicateVal then

		return val

	end

end

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

local function getReadOnlyStateGroupNamespace( stateGroupName, wantStrict )

	-- generate the state group name space by adding the state group states to
	-- a temporary fsm then extracting the state group map.
	
	local MakeFSMWithStateGroupStates = 
			assert( StateGroupFSMConstructor[ stateGroupName ], 
						"must define StateGroupFSMConstructor [ '" .. stateGroupName ..
						"' ] " )

	local fsmWithStateGroupStates = MakeFSMWithStateGroupStates()

	local stateMapForStateGroupName = 
		getStateGroupMap( fsmWithStateGroupStates, stateGroupName )

	-- given a stateMapForStateGroupName returns a read only copy that will assert
	-- when attempting to index a key not in stateMapForStateGroupName
	local stateGroupNameTableName = 
			string.format( "PixelPathProviderStateMachine.%s", stateGroupName )

	return setmetatable( stateMapForStateGroupName,
			{

				__index = ONLY_IF( wantStrict,
									makeStrictAccessMetamethod(
										stateGroupNameTableName ) ),

				__newindex = makeReadOnlyMetamethod( stateGroupNameTableName ),

			} )

end

--==============================================================================
-- expose all of the state group namespaces
--
-- This adds the following to the PixelPathProviderStateMachine namespace:
--
-- 		PixelPathProviderStateMachine.DocstoreInfoStates 
-- 		PixelPathProviderStateMachine.PixelCacheCheckStates 
-- 		PixelPathProviderStateMachine.DownloadStates
-- 		... etc
--==============================================================================

local function getReadOnlyStrictStateGroupNamespace( stateGroupName )

	return getReadOnlyStateGroupNamespace( stateGroupName, true )

end

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

local function addStateGroupNamespace( PixelPathProviderStateMachine, 
										stateGroupName )

	PixelPathProviderStateMachine[ stateGroupName ] = 
		getReadOnlyStrictStateGroupNamespace( stateGroupName )

	return PixelPathProviderStateMachine

end

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

local PixelPathProviderStateMachine = WFFunctionUtils.reduce( {}, 
											addStateGroupNamespace, 
											StateGroupNameArray )

--==============================================================================
-- make the FSM containing all of the state groups
--==============================================================================

local function addStateGroup( fsm, stateGroupName )

	return StateGroupFSMConstructor[ stateGroupName ]( fsm )

end

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

PixelPathProviderStateMachine.make = function()

	local fsm = WFFiniteStateMachine.make()

	-- add all of the state groups' states to the fsm
	fsm = WFFunctionUtils.reduce( fsm, addStateGroup, StateGroupNameArray )

	--================================================
	-- 	3) Add transitions for the state groups.
	--
	-- 	Transitions are what make the state machine
	-- 	useful and model the pixel path generation
	-- 	process.
	--================================================

	------------------------------------------------------------
	-- DocstoreInfoStates transitions
	------------------------------------------------------------
	local DocstoreInfoStates = PixelPathProviderStateMachine.DocstoreInfoStates
	local DownloadStates = PixelPathProviderStateMachine.DownloadStates
	local PixelCacheCheckStates = PixelPathProviderStateMachine.PixelCacheCheckStates
	local RenderStates = PixelPathProviderStateMachine.RenderStates
	local SaveRenderStates = PixelPathProviderStateMachine.SaveRenderStates
	local SendDataStates = PixelPathProviderStateMachine.SendDataStates
	local EndStates = PixelPathProviderStateMachine.EndStates
	local LocalBinaryPathCheckStates = PixelPathProviderStateMachine.LocalBinaryPathCheckStates

	fsm = addTransitions( fsm, DocstoreInfoStates.NeedAssetInfoForRender, 
							{ { 'must_fetch', DocstoreInfoStates.NeedAssetInfoForRenderFromDocstore },
							  { 'already_present', DocstoreInfoStates.NeedCheckRenderSettings } } )

	fsm = addTransitions( fsm, DocstoreInfoStates.NeedAssetInfoForRenderFromDocstore, 
							{ { 'start_docstore_read', DocstoreInfoStates.FetchingAssetInfoForRender },
							  { 'received_docstore_read', DocstoreInfoStates.NeedCheckRenderSettings },
							  { 'new_docstore_render_info', DocstoreInfoStates.NeedCheckRenderSettings } } )

	fsm = addTransitions( fsm, DocstoreInfoStates.FetchingAssetInfoForRender, 
							  { { 'received_docstore_read', DocstoreInfoStates.NeedCheckRenderSettings },
							    { 'new_docstore_render_info', DocstoreInfoStates.NeedCheckRenderSettings }} )
	
	fsm = addTransitions( fsm, DocstoreInfoStates.NeedCheckRenderSettings,
							{ { 'have_xmp_develop', PixelCacheCheckStates.NeedCheckForPixelPathInCache },
							  { 'xmp_develop_is_external', PixelCacheCheckStates.NeedCheckForPixelPathInCache },
							  { 'xmp_develop_and_aux_files_are_external', PixelCacheCheckStates.NeedCheckForPixelPathInCache } } )
	
	fsm = addTransitions( fsm, DocstoreInfoStates.NeedXmpSidecarInfo,
							{ { 'must_fetch', DocstoreInfoStates.NeedXmpSidecarInfoFromDocstore },
							  { 'already_present', LocalBinaryPathCheckStates.NeedCheckForLocalXmpSidecar } } )

	fsm = addTransitions( fsm, DocstoreInfoStates.NeedXmpSidecarInfoFromDocstore,
							{ { 'start_docstore_read', DocstoreInfoStates.FetchingXmpSidecarInfo },
							  { 'received_docstore_read', LocalBinaryPathCheckStates.NeedCheckForLocalXmpSidecar } } )

	fsm = addTransitions( fsm, DocstoreInfoStates.FetchingXmpSidecarInfo,
							{ { 'received_docstore_read', LocalBinaryPathCheckStates.NeedCheckForLocalXmpSidecar } } )

	fsm = addTransitions( fsm, DocstoreInfoStates.NeedDownloadOriginalInfo, 
							{ { 'must_fetch', DocstoreInfoStates.NeedDownloadOriginalInfoFromDocstore },
							  { 'already_present', LocalBinaryPathCheckStates.NeedCheckForLocalOriginal } } )
								
	fsm = addTransitions( fsm, DocstoreInfoStates.NeedDownloadOriginalInfoFromDocstore, 
							{ { 'start_docstore_read', DocstoreInfoStates.FetchingDownloadOriginalInfo },
							  { 'received_docstore_read', LocalBinaryPathCheckStates.NeedCheckForLocalOriginal } } )

	fsm = addTransitions( fsm, DocstoreInfoStates.FetchingDownloadOriginalInfo, 
							{ { 'received_docstore_read', LocalBinaryPathCheckStates.NeedCheckForLocalOriginal } } )

	fsm = addTransitions( fsm, DocstoreInfoStates.NeedDownloadProxyInfo, 
							{ { 'must_fetch', DocstoreInfoStates.NeedDownloadProxyInfoFromDocstore },
							  { 'already_present', LocalBinaryPathCheckStates.NeedCheckForLocalProxy } } )

	fsm = addTransitions( fsm, DocstoreInfoStates.NeedDownloadProxyInfoFromDocstore, 
							{ { 'start_docstore_read', DocstoreInfoStates.FetchingDownloadProxyInfo },
							  { 'received_docstore_read', LocalBinaryPathCheckStates.NeedCheckForLocalProxy } } )

	fsm = addTransitions( fsm, DocstoreInfoStates.FetchingDownloadProxyInfo, 
							{ { 'received_docstore_read', LocalBinaryPathCheckStates.NeedCheckForLocalProxy } } )

	fsm = addTransitions( fsm, DocstoreInfoStates.NeedBestPossibleLocalNegativePathInfo, 
							{ { 'must_fetch', DocstoreInfoStates.NeedBestPossibleLocalNegativePathInfoFromDocstore },
							  { 'already_present', LocalBinaryPathCheckStates.ChooseBestPossibleLocalNegativePath } } )

	fsm = addTransitions( fsm, DocstoreInfoStates.NeedBestPossibleLocalNegativePathInfoFromDocstore, 
							{ { 'start_docstore_read', DocstoreInfoStates.FetchingBestPossibleLocalNegativePathInfo },
							  { 'received_docstore_read', LocalBinaryPathCheckStates.ChooseBestPossibleLocalNegativePath } } )

	fsm = addTransitions( fsm, DocstoreInfoStates.FetchingBestPossibleLocalNegativePathInfo, 
							{ { 'received_docstore_read', LocalBinaryPathCheckStates.ChooseBestPossibleLocalNegativePath } } )

	fsm = addTransitions( fsm, DocstoreInfoStates.NeedAnyLocalNegativePathInfo, 
							{ { 'must_fetch', DocstoreInfoStates.NeedAnyLocalNegativePathInfoFromDocstore },
							  { 'already_present', LocalBinaryPathCheckStates.NeedCheckForAnyLocalNegative } } )

	fsm = addTransitions( fsm, DocstoreInfoStates.NeedAnyLocalNegativePathInfoFromDocstore, 
							{ { 'start_docstore_read', DocstoreInfoStates.FetchingAnyLocalNegativePathInfo },
							  { 'received_docstore_read', LocalBinaryPathCheckStates.NeedCheckForAnyLocalNegative } } )

	fsm = addTransitions( fsm, DocstoreInfoStates.FetchingAnyLocalNegativePathInfo, 
							{ { 'received_docstore_read', LocalBinaryPathCheckStates.NeedCheckForAnyLocalNegative } } )

	-- bad_asset_id transition
	
	fsm = addTransitions( fsm, DocstoreInfoStates.NeedAssetInfoForRender,
				{ { 'bad_asset_id', SendDataStates.SendPermanentBadAssetIdError } } )

	fsm = addTransitions( fsm, DocstoreInfoStates.NeedDownloadOriginalInfo,
				{ { 'bad_asset_id', SendDataStates.SendPermanentBadAssetIdError } } )

	fsm = addTransitions( fsm, DocstoreInfoStates.NeedDownloadProxyInfo,
				{ { 'bad_asset_id', SendDataStates.SendPermanentBadAssetIdError } } )

	fsm = addTransitions( fsm, DocstoreInfoStates.NeedBestPossibleLocalNegativePathInfo,
				{ { 'bad_asset_id', SendDataStates.SendPermanentBadAssetIdError } } )

	fsm = addTransitions( fsm, DocstoreInfoStates.NeedAnyLocalNegativePathInfo,
				{ { 'bad_asset_id', SendDataStates.SendPermanentBadAssetIdError } } )

	fsm = addTransitions( fsm, DocstoreInfoStates.NeedXmpSidecarInfo,
				{ { 'bad_asset_id', SendDataStates.SendPermanentBadAssetIdError } } )

	fsm = addTransitions( fsm, DocstoreInfoStates.FetchingAssetInfoForRender,
			{ { 'bad_asset_id', SendDataStates.SendPermanentBadAssetIdError } } )

	fsm = addTransitions( fsm, DocstoreInfoStates.FetchingXmpSidecarInfo,
			{ { 'bad_asset_id', SendDataStates.SendPermanentBadAssetIdError } } )

	fsm = addTransitions( fsm, DocstoreInfoStates.FetchingDownloadOriginalInfo,
			{ { 'bad_asset_id', SendDataStates.SendPermanentBadAssetIdError } } )

	fsm = addTransitions( fsm, DocstoreInfoStates.FetchingDownloadProxyInfo,
			{ { 'bad_asset_id', SendDataStates.SendPermanentBadAssetIdError } } )

	fsm = addTransitions( fsm, DocstoreInfoStates.FetchingBestPossibleLocalNegativePathInfo,
			{ { 'bad_asset_id', SendDataStates.SendPermanentBadAssetIdError } } )

	fsm = addTransitions( fsm, DocstoreInfoStates.FetchingAnyLocalNegativePathInfo,
			{ { 'bad_asset_id', SendDataStates.SendPermanentBadAssetIdError } } )

	------------------------------------------------------------
	-- PixelCacheCheckStates transitions
	------------------------------------------------------------
	fsm = addTransitions( fsm, PixelCacheCheckStates.NeedCheckForPixelPathInCache, 
							{ { 'start_rendition_check', PixelCacheCheckStates.CheckingForPixelPathInCache },
							  { 'have_rendition', SendDataStates.HaveRenditionPath },
							  { 'no_rendition', PixelCacheCheckStates.ChooseRenditionStrategy },
							  { 'need_docstore_info', DocstoreInfoStates.NeedAssetInfoForRender },
							  { 'new_docstore_render_info', DocstoreInfoStates.NeedAssetInfoForRender },
							  { 'new_long_side', DocstoreInfoStates.NeedAssetInfoForRender } } )
							  
	fsm = addTransitions( fsm, PixelCacheCheckStates.CheckingForPixelPathInCache, 
							{ { 'have_local_rendition', SendDataStates.HaveRenditionPath },
							  { 'need_docstore_info', DocstoreInfoStates.NeedAssetInfoForRender },
							  { 'stale_check', PixelCacheCheckStates.CheckingForPixelPathInCache },
							  { 'no_local_rendition', PixelCacheCheckStates.ChooseRenditionStrategy } } )

	fsm = addTransitions( fsm, PixelCacheCheckStates.ChooseRenditionStrategy, 
							{ { 'new_docstore_render_info', DocstoreInfoStates.NeedAssetInfoForRender },
							  { 'new_docstore_binary_path_info', DocstoreInfoStates.NeedAssetInfoForRender },
							  { 'have_missing_profile_local_rendition', SendDataStates.HaveRenditionPath }, 
							  { 'bad_missing_profile_local_rendition_state', SendDataStates.SendPermanentMissingProfileRenditionPathError },
							  { 'new_long_side', DocstoreInfoStates.NeedAssetInfoForRender }, 
							  { 'need_develop_xmp', LocalBinaryPathCheckStates.RenditionStrategyNeedCheckIfExternalXmpDevelopIsLocal },
							  { 'has_pending_rendition_save', SaveRenderStates.NeedToSaveRender }, 
							  { 'has_active_rendition_save', SaveRenderStates.SavingRender }, 
							  { 'try_download_rendition', DownloadStates.NeedRenderDownload }, 
							  { 'confirm_any_local_negative_for_render', 
							  	LocalBinaryPathCheckStates.NeedCheckAnyLocalNegativeForRender }, 
							  { 'confirm_local_proxy_for_render', 
							  	LocalBinaryPathCheckStates.NeedCheckLocalProxyForRender }, 
							  { 'try_download_proxy', DownloadStates.NeedProxyDownloadForRender },
							  { 'try_download_original', DownloadStates.NeedOriginalDownloadForRender },
							  { 'wait_for_render_data_updates', SendDataStates.SendNoDownloadableRenderOrBinary } } )

	fsm = addTransitions( fsm, PixelCacheCheckStates.ChooseRenderOnlyRenditionStrategy, 
							{ { 'new_docstore_render_info', DocstoreInfoStates.NeedAssetInfoForRender },
							  { 'new_docstore_binary_path_info', DocstoreInfoStates.NeedAssetInfoForRender },
							  { 'have_missing_profile_local_rendition', SendDataStates.HaveRenditionPath },
							  { 'bad_missing_profile_local_rendition_state', SendDataStates.SendPermanentMissingProfileRenditionPathError },
							  { 'new_long_side', DocstoreInfoStates.NeedAssetInfoForRender }, 
							  { 'need_develop_xmp', LocalBinaryPathCheckStates.RenditionStrategyNeedCheckIfExternalXmpDevelopIsLocal },
							  { 'has_pending_rendition_save', SaveRenderStates.NeedToSaveRender }, 
							  { 'has_active_rendition_save', SaveRenderStates.SavingRender }, 
							  { 'confirm_any_local_negative_for_render', 
							  	LocalBinaryPathCheckStates.NeedCheckAnyLocalNegativeForRender }, 
							  { 'confirm_local_proxy_for_render', 
							  	LocalBinaryPathCheckStates.NeedCheckLocalProxyForRender }, 
							  { 'wait_for_render_data_updates', SendDataStates.SendNoDownloadableRenderOrBinary } } )

	------------------------------------------------------------
	-- LocalBinaryPathCheckStates transitions
	------------------------------------------------------------
	local SaveDownloadStates = PixelPathProviderStateMachine.SaveDownloadStates

	-- just check for original
	fsm = addTransitions( fsm, LocalBinaryPathCheckStates.NeedCheckForLocalOriginal, 
							{ { 'start_non_rendition_check', LocalBinaryPathCheckStates.CheckingForLocalOriginal } } )

	fsm = addTransitions( fsm, LocalBinaryPathCheckStates.CheckingForLocalOriginal, 
							{ { 'has_local', SendDataStates.HaveLocalNegativePath },
							  { 'need_download_original', DownloadStates.NeedOriginalDownload },
							  { 'no_downloadable_uri', SendDataStates.SendNoDownloadableOriginal } } )

	-- check for any local negative to use for rendering
	fsm = addTransitions( fsm, LocalBinaryPathCheckStates.NeedCheckAnyLocalNegativeForRender, 
							{ { 'start_non_rendition_check', LocalBinaryPathCheckStates.CheckingForAnyLocalNegativeForRender } } )

	fsm = addTransitions( fsm, LocalBinaryPathCheckStates.CheckingForAnyLocalNegativeForRender, 
							{ { 'has_local', RenderStates.NeedsRender },
							  { 'need_download_original', DownloadStates.NeedOriginalDownloadForRender },
							  { 'need_download_proxy', DownloadStates.NeedProxyDownloadForRender },
							  { 'no_downloadable_uri', SendDataStates.SendNoDownloadableRenderOrBinary } } )

	-- check for proxy to use for rendering
	fsm = addTransitions( fsm, LocalBinaryPathCheckStates.NeedCheckLocalProxyForRender, 
							{ { 'start_non_rendition_check', LocalBinaryPathCheckStates.CheckingForLocalProxyForRender },
							  { 'no_downloadable_uri', SendDataStates.SendNoDownloadableBinaryForRender } } )

	fsm = addTransitions( fsm, LocalBinaryPathCheckStates.CheckingForLocalProxyForRender, 
							{ { 'has_local', RenderStates.NeedsRender },
							  { 'need_download_proxy', DownloadStates.NeedProxyDownloadForRender },
							  { 'no_downloadable_uri', SendDataStates.SendNoDownloadableBinaryForRender } } )

	-- just check for proxy
	fsm = addTransitions( fsm, LocalBinaryPathCheckStates.NeedCheckForLocalProxy, 
							{ { 'start_non_rendition_check', LocalBinaryPathCheckStates.CheckingForLocalProxy },
							  { 'no_downloadable_uri', LocalBinaryPathCheckStates.NeedCheckForLocalOriginal } } )

	fsm = addTransitions( fsm, LocalBinaryPathCheckStates.CheckingForLocalProxy, 
							{ { 'has_local', SendDataStates.HaveLocalNegativePath },
							  { 'need_download_proxy', DownloadStates.NeedProxyDownload },
							  { 'no_downloadable_uri', LocalBinaryPathCheckStates.NeedCheckForLocalOriginal } } )

	-- get the most convenient local negative
	fsm = addTransitions( fsm, LocalBinaryPathCheckStates.NeedCheckForAnyLocalNegative, 
							{ { 'start_non_rendition_check', LocalBinaryPathCheckStates.CheckingForAnyLocalNegative } } )

	fsm = addTransitions( fsm, LocalBinaryPathCheckStates.CheckingForAnyLocalNegative, 
							{ { 'has_local', SendDataStates.HaveLocalNegativePath },
							  { 'need_docstore_info', DocstoreInfoStates.NeedAnyLocalNegativePathInfo },
							  { 'need_download_proxy', DownloadStates.NeedProxyDownload },
							  { 'need_download_original', DownloadStates.NeedOriginalDownload },
							  { 'no_downloadable_uri', SendDataStates.SendNoDownloadableBinary } } )
	
	-- just check for xmp_sidecar
	fsm = addTransitions( fsm, LocalBinaryPathCheckStates.NeedCheckForLocalXmpSidecar,
							{ { 'start_non_rendition_check', LocalBinaryPathCheckStates.CheckingForLocalXmpSidecar } } )

	fsm = addTransitions( fsm, LocalBinaryPathCheckStates.CheckingForLocalXmpSidecar,
							{ { 'has_local', SendDataStates.HaveLocalXmpSidecarPath },
							  { 'need_download_xmp_sidecar', DownloadStates.NeedXmpSidecarDownload },
							  { 'no_downloadable_uri', SendDataStates.SendNoDownloadableXmpSidecar } } )

	-- get the best possible local negative path
	fsm = addTransitions( fsm, LocalBinaryPathCheckStates.ChooseBestPossibleLocalNegativePath, 
							{ { 'have_local_original', LocalBinaryPathCheckStates.NeedCheckOriginalForBestPossibleLocalNegativePath },
							  { 'have_local_proxy', LocalBinaryPathCheckStates.NeedCheckProxyForBestPossibleLocalNegativePath },
							  { 'try_download_original', DownloadStates.NeedOriginalDownloadForBestPossibleLocalNegativePath },
							  { 'try_download_proxy', DownloadStates.NeedProxyDownloadForBestPossibleLocalNegativePath },
							  { 'has_active_original_download', DownloadStates.NeedOriginalDownloadForBestPossibleLocalNegativePath },
							  { 'has_active_proxy_download', DownloadStates.NeedProxyDownloadForBestPossibleLocalNegativePath },
							  { 'has_pending_original_save', SaveDownloadStates.NeedToSaveOriginalForBestPossibleLocalNegativePath },
							  { 'has_pending_proxy_save', SaveDownloadStates.NeedToSaveProxyForBestPossibleLocalNegativePath },
							  { 'has_active_original_save', SaveDownloadStates.NeedToSaveOriginalForBestPossibleLocalNegativePath },
							  { 'has_active_proxy_save', SaveDownloadStates.NeedToSaveProxyForBestPossibleLocalNegativePath },
							  { 'no_paths_available', EndStates.WaitForDownloadableOriginalForBestPossibleLocalNegativePath } } )

	fsm = addTransitions( fsm, LocalBinaryPathCheckStates.NeedCheckOriginalForBestPossibleLocalNegativePath, 
							{ { 'start_non_rendition_check', LocalBinaryPathCheckStates.CheckingOriginalForBestPossibleLocalNegativePath } } )

	fsm = addTransitions( fsm, LocalBinaryPathCheckStates.CheckingOriginalForBestPossibleLocalNegativePath,
							{ { 'has_local', SendDataStates.HaveOriginalForBestPossibleLocalNegativePath },
							  { 'need_download_original', DownloadStates.NeedOriginalDownloadForBestPossibleLocalNegativePath },
							  { 'no_downloadable_uri', EndStates.WaitForDownloadableOriginalForBestPossibleLocalNegativePath } } )

	fsm = addTransitions( fsm, LocalBinaryPathCheckStates.NeedCheckProxyForBestPossibleLocalNegativePath, 
							{ { 'start_non_rendition_check', LocalBinaryPathCheckStates.CheckingProxyForBestPossibleLocalNegativePath },
							  { 'no_downloadable_uri', EndStates.WaitForDownloadableOriginalForBestPossibleLocalNegativePath } } )

	fsm = addTransitions( fsm, LocalBinaryPathCheckStates.CheckingProxyForBestPossibleLocalNegativePath,
							{ { 'has_local', SendDataStates.HaveProxyForBestPossibleLocalNegativePath },
							  { 'need_download_proxy', DownloadStates.NeedProxyDownloadForBestPossibleLocalNegativePath },
							  { 'no_downloadable_uri', EndStates.WaitForDownloadableOriginalForBestPossibleLocalNegativePath } } )

	------------------------------------------------------------
	-- DownloadStates transitions
	------------------------------------------------------------
	
	-- download render
	fsm = addRetryTransitions( 
			fsm, _downloadRetryCount,
			DownloadStates.NeedRenderDownload, 'start_download',
			DownloadStates.DownloadingRender, 'retry_download',
			LocalBinaryPathCheckStates.NeedCheckAnyLocalNegativeForRender )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedRenderDownload, 'new_docstore_render_info',
			DocstoreInfoStates.NeedAssetInfoForRender )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingRender, 'cannot_retry_download',
			LocalBinaryPathCheckStates.NeedCheckAnyLocalNegativeForRender )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedRenderDownload, 'missing_download_info',
			SendDataStates.SendPermanentDownloadError )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingRender, 'missing_download_info',
			DocstoreInfoStates.NeedAssetInfoForRender )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedRenderDownload, 'new_long_side',
			DocstoreInfoStates.NeedAssetInfoForRender )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedRenderDownload, 'cannot_download',
			PixelCacheCheckStates.ChooseRenderOnlyRenditionStrategy )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedRenderDownload, 'no_internet',
			PixelCacheCheckStates.ChooseRenderOnlyRenditionStrategy )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedRenderDownload, 'volume_cannot_save',
			SendDataStates.SendPermanentDownloadError )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedRenderDownload, 'has_pending_rendition_save',
			SaveRenderStates.NeedToSaveRender )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedRenderDownload, 'has_active_rendition_save',
			SaveRenderStates.SavingRender )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingRender, 'received_download',
			SaveRenderStates.NeedToSaveRender )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingRender, 'reschedule_download',
			DownloadStates.NeedRenderDownload )
	
	-- download just proxy
	fsm = addRetryTransitions( 
			fsm, _downloadRetryCount,
			DownloadStates.NeedProxyDownload, 'start_download',
			DownloadStates.DownloadingProxy, 'retry_download',
			SendDataStates.SendPermanentDownloadError )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingProxy, 'missing_download_info',
			DocstoreInfoStates.NeedDownloadProxyInfo )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingProxy, 'cannot_retry_download',
			SendDataStates.SendPermanentDownloadError )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedProxyDownload, 'missing_download_info',
			SendDataStates.SendPermanentDownloadError )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedProxyDownload, 'start_save_download',
			SaveDownloadStates.SavingProxy )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedProxyDownload, 'cannot_download',
			SendDataStates.SendPermanentDownloadError )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedProxyDownload, 'no_internet',
			SendDataStates.SendPermanentDownloadError )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedProxyDownload, 'volume_cannot_save',
			SendDataStates.SendPermanentDownloadError )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedProxyDownload, 'have_pending_save_for_blob',
			SaveDownloadStates.NeedToSaveProxy )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingProxy, 'received_download',
			SaveDownloadStates.NeedToSaveProxy )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingProxy, 'reschedule_download',
			DownloadStates.NeedProxyDownload )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedProxyDownload, 'saved_valid_download',
			SendDataStates.HaveLocalNegativePath )

	-- download proxy as part of rendition render
	fsm = addRetryTransitions( 
			fsm, _downloadRetryCount,
			DownloadStates.NeedProxyDownloadForRender, 'start_download',
			DownloadStates.DownloadingProxyForRender, 'retry_download',
			SendDataStates.SendPermanentDownloadError )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedProxyDownloadForRender, 'missing_download_info',
			SendDataStates.SendPermanentDownloadError )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingProxyForRender, 'cannot_retry_download',
			SendDataStates.SendPermanentDownloadError )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedProxyDownloadForRender, 'start_save_download',
			SaveDownloadStates.SavingProxyForRender )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingProxyForRender, 'missing_download_info',
			DocstoreInfoStates.NeedAssetInfoForRender )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedProxyDownloadForRender, 'cannot_download',
			SendDataStates.SendPermanentDownloadError )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedProxyDownloadForRender, 'no_internet',
			SendDataStates.SendPermanentDownloadError )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedProxyDownloadForRender, 'volume_cannot_save',
			SendDataStates.SendPermanentDownloadError )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedProxyDownloadForRender, 'have_pending_save_for_blob',
			SaveDownloadStates.NeedToSaveProxyForRender )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingProxyForRender, 'received_download',
			SaveDownloadStates.NeedToSaveProxyForRender )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingProxyForRender, 'reschedule_download',
			DownloadStates.NeedProxyDownloadForRender )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedProxyDownloadForRender, 'saved_valid_download',
			RenderStates.NeedsRender )
			
	-- download just original
	fsm = addRetryTransitions( 
			fsm, _downloadRetryCount,
			DownloadStates.NeedOriginalDownload, 'start_download',
			DownloadStates.DownloadingOriginal, 'retry_download',
			SendDataStates.SendPermanentDownloadError )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedOriginalDownload, 'missing_download_info',
			SendDataStates.SendPermanentDownloadError )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingOriginal, 'cannot_retry_download',
			SendDataStates.SendPermanentDownloadError )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedOriginalDownload, 'start_save_download',
			SaveDownloadStates.SavingOriginal )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingOriginal, 'missing_download_info',
			DocstoreInfoStates.NeedDownloadOriginalInfo )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedOriginalDownload, 'cannot_download',
			SendDataStates.SendPermanentDownloadError )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedOriginalDownload, 'no_internet',
			SendDataStates.SendPermanentDownloadError )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedOriginalDownload, 'volume_cannot_save',
			SendDataStates.SendPermanentDownloadError )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedOriginalDownload, 'have_pending_save_for_blob',
			SaveDownloadStates.NeedToSaveOriginal )
			
	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingOriginal, 'received_download',
			SaveDownloadStates.NeedToSaveOriginal )
		
	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingOriginal, 'reschedule_download',
			DownloadStates.NeedOriginalDownload )
		
	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedOriginalDownload, 'saved_valid_download',
			SendDataStates.HaveLocalNegativePath )

	-- download original as part of rendition render
	fsm = addRetryTransitions( 
			fsm, _downloadRetryCount,
			DownloadStates.NeedOriginalDownloadForRender, 'start_download',
			DownloadStates.DownloadingOriginalForRender, 'retry_download',
			SendDataStates.SendPermanentDownloadError )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedOriginalDownloadForRender, 'missing_download_info',
			SendDataStates.SendPermanentDownloadError )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingOriginalForRender, 'cannot_retry_download',
			SendDataStates.SendPermanentDownloadError )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedOriginalDownloadForRender, 'start_save_download',
			SaveDownloadStates.SavingOriginalForRender )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedOriginalDownloadForRender, 'cannot_download',
			SendDataStates.SendPermanentDownloadError )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedOriginalDownloadForRender, 'no_internet',
			SendDataStates.SendPermanentDownloadError )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedOriginalDownloadForRender, 'volume_cannot_save',
			SendDataStates.SendPermanentDownloadError )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedOriginalDownloadForRender, 'have_pending_save_for_blob',
			SaveDownloadStates.NeedToSaveOriginalForRender )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingOriginalForRender, 'missing_download_info',
			DocstoreInfoStates.NeedAssetInfoForRender )
			
	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingOriginalForRender, 'received_download',
			SaveDownloadStates.NeedToSaveOriginalForRender )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingOriginalForRender, 'reschedule_download',
			DownloadStates.NeedOriginalDownloadForRender )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedOriginalDownloadForRender, 'saved_valid_download',
			RenderStates.NeedsRender )

	-- download original to get the best possible local negative path
	fsm = addRetryTransitions( 
			fsm, _downloadRetryCount,
			DownloadStates.NeedOriginalDownloadForBestPossibleLocalNegativePath, 'start_download',
			DownloadStates.DownloadingOriginalForBestPossibleLocalNegativePath, 'retry_download',
			SendDataStates.SendPermanentDownloadErrorForBestPossibleLocalNegativePath )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedOriginalDownloadForBestPossibleLocalNegativePath, 'missing_download_info',
			SendDataStates.SendPermanentDownloadErrorForBestPossibleLocalNegativePath )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingOriginalForBestPossibleLocalNegativePath, 'cannot_retry_download',
			SendDataStates.SendPermanentDownloadErrorForBestPossibleLocalNegativePath )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedOriginalDownloadForBestPossibleLocalNegativePath, 'start_save_download',
			SaveDownloadStates.SavingOriginalForBestPossibleLocalNegativePath )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedOriginalDownloadForBestPossibleLocalNegativePath, 'cannot_download',
			SendDataStates.SendPermanentDownloadErrorForBestPossibleLocalNegativePath )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedOriginalDownloadForBestPossibleLocalNegativePath, 'no_internet',
			SendDataStates.SendPermanentDownloadErrorForBestPossibleLocalNegativePath )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedOriginalDownloadForBestPossibleLocalNegativePath, 'volume_cannot_save',
			SendDataStates.SendPermanentDownloadErrorForBestPossibleLocalNegativePath )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedOriginalDownloadForBestPossibleLocalNegativePath, 'have_pending_save_for_blob',
			SaveDownloadStates.NeedToSaveOriginalForBestPossibleLocalNegativePath )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingOriginalForBestPossibleLocalNegativePath, 'missing_download_info',
			DocstoreInfoStates.NeedBestPossibleLocalNegativePathInfo )
			
	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingOriginalForBestPossibleLocalNegativePath, 'received_download',
			SaveDownloadStates.NeedToSaveOriginalForBestPossibleLocalNegativePath )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingOriginalForBestPossibleLocalNegativePath, 'reschedule_download',
			DownloadStates.NeedOriginalDownloadForBestPossibleLocalNegativePath )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedOriginalDownloadForBestPossibleLocalNegativePath, 'saved_valid_download',
			SendDataStates.HaveOriginalForBestPossibleLocalNegativePath )

	-- download proxy to get the best possible local negative path
	fsm = addRetryTransitions( 
			fsm, _downloadRetryCount,
			DownloadStates.NeedProxyDownloadForBestPossibleLocalNegativePath, 'start_download',
			DownloadStates.DownloadingProxyForBestPossibleLocalNegativePath, 'retry_download',
			DownloadStates.NeedOriginalDownloadForBestPossibleLocalNegativePath )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedProxyDownloadForBestPossibleLocalNegativePath, 'missing_download_info',
			SendDataStates.SendPermanentDownloadErrorForBestPossibleLocalNegativePath )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingProxyForBestPossibleLocalNegativePath, 'cannot_retry_download',
			DownloadStates.NeedOriginalDownloadForBestPossibleLocalNegativePath )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedProxyDownloadForBestPossibleLocalNegativePath, 'start_save_download',
			SaveDownloadStates.SavingProxyForBestPossibleLocalNegativePath )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedProxyDownloadForBestPossibleLocalNegativePath, 'cannot_download',
			DownloadStates.NeedOriginalDownloadForBestPossibleLocalNegativePath )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedProxyDownloadForBestPossibleLocalNegativePath, 'no_internet',
			DownloadStates.NeedOriginalDownloadForBestPossibleLocalNegativePath )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedProxyDownloadForBestPossibleLocalNegativePath, 'volume_cannot_save',
			DownloadStates.NeedOriginalDownloadForBestPossibleLocalNegativePath )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedProxyDownloadForBestPossibleLocalNegativePath, 'have_pending_save_for_blob',
			SaveDownloadStates.NeedToSaveProxyForBestPossibleLocalNegativePath )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingProxyForBestPossibleLocalNegativePath, 'missing_download_info',
			DocstoreInfoStates.NeedBestPossibleLocalNegativePathInfo )
			
	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingProxyForBestPossibleLocalNegativePath, 'received_download',
			SaveDownloadStates.NeedToSaveProxyForBestPossibleLocalNegativePath )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingProxyForBestPossibleLocalNegativePath, 'reschedule_download',
			DownloadStates.NeedProxyDownloadForBestPossibleLocalNegativePath )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedProxyDownloadForBestPossibleLocalNegativePath, 'saved_valid_download',
			SendDataStates.HaveProxyForBestPossibleLocalNegativePath )
	
	-- download xmp sidecar
	fsm = addRetryTransitions(
			fsm, _downloadRetryCount,
			DownloadStates.NeedXmpSidecarDownload, 'start_download',
			DownloadStates.DownloadingXmpSidecar, 'retry_download',
			SendDataStates.SendPermanentDownloadError )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedXmpSidecarDownload, 'missing_download_info',
			SendDataStates.SendPermanentDownloadError )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingXmpSidecar, 'cannot_retry_download',
			SendDataStates.SendPermanentDownloadError )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedXmpSidecarDownload, 'start_save_download',
			SaveDownloadStates.SavingXmpSidecar )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedXmpSidecarDownload, 'cannot_download',
			SendDataStates.SendPermanentDownloadError )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedXmpSidecarDownload, 'no_internet',
			SendDataStates.SendPermanentDownloadError )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedXmpSidecarDownload, 'volume_cannot_save',
			SendDataStates.SendPermanentDownloadError )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedXmpSidecarDownload, 'have_pending_save_for_blob',
			SaveDownloadStates.NeedToSaveXmpSidecar )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingXmpSidecar, 'missing_download_info',
			DocstoreInfoStates.NeedXmpSidecarInfo )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingXmpSidecar, 'received_download',
			LocalBinaryPathCheckStates.AnalyzeXmpSidecar )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingXmpSidecar, 'reschedule_download',
			DownloadStates.NeedXmpSidecarDownload )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedXmpSidecarDownload, 'saved_valid_download',
			SendDataStates.HaveLocalXmpSidecarPath )

	------------------------------------------------------------
	-- xmp analyze transitions
	------------------------------------------------------------

	fsm = addTransitions( fsm, LocalBinaryPathCheckStates.AnalyzeXmpSidecar,
							{ { 'start_analyze_xmp_sidecar', LocalBinaryPathCheckStates.AnalyzingXmpSidecar } } )

	fsm = addTransitions( fsm, LocalBinaryPathCheckStates.AnalyzingXmpSidecar,
							{ { 'need_docstore_info', DocstoreInfoStates.NeedAssetInfoForRender },
							  { 'save_sidecar_xmp', SaveDownloadStates.NeedToSaveXmpSidecar },
							  { 'no_sidecar_xmp', SendDataStates.NotXmpSidecar } } )

	------------------------------------------------------------
	-- RenderStates transitions
	------------------------------------------------------------
	fsm = addRetryTransitions( 
			fsm, _renderRetryCount,
			RenderStates.NeedsRender, 'start_render',
			RenderStates.RenderScheduled, 'retry_render',
			SendDataStates.SendPermanentError )

	fsm = addTransitionToAllRetryStates( fsm, _renderRetryCount,
			RenderStates.NeedsRender, 'retry_render',
			RenderStates.NeedsRender )

	fsm = addTransitionToAllRetryStates( fsm, _renderRetryCount,
			RenderStates.NeedsRender, 'cannot_render',
			SendDataStates.SendPermanentError )

	fsm = addTransitionToAllRetryStates( fsm, _renderRetryCount,
			RenderStates.NeedsRender, 'has_pending_rendition_save',
			SaveRenderStates.NeedToSaveRender )

	fsm = addTransitionToAllRetryStates( fsm, _renderRetryCount,
			RenderStates.NeedsRender, 'has_active_rendition_save',
			SaveRenderStates.NeedToSaveRender )

	fsm = addTransitionToAllRetryStates( fsm, _renderRetryCount,
			RenderStates.NeedsRender, 'rendered',
			SaveRenderStates.NeedToSaveRender )

	fsm = addTransitionToAllRetryStates( fsm, _renderRetryCount,
			RenderStates.RenderScheduled, 'rendered',
			SaveRenderStates.NeedToSaveRender )

	fsm = addTransitionToAllRetryStates( fsm, _renderRetryCount,
			RenderStates.RenderScheduled, 'reschedule_render',
			RenderStates.NeedsRender )

	fsm = addTransitionToAllRetryStates( fsm, _renderRetryCount,
			RenderStates.NeedsRender, 'no_local_negative',
			DocstoreInfoStates.NeedAssetInfoForRender )

	fsm = addTransitionToAllRetryStates( fsm, _renderRetryCount,
			RenderStates.NeedsRender, 'no_render_size',
			SendDataStates.SendPermanentError )

	fsm = addTransitionToAllRetryStates( fsm, _renderRetryCount,
			RenderStates.NeedsRender, 'new_docstore_render_info',
			DocstoreInfoStates.NeedAssetInfoForRender )

	-- if we saved a render, we want to start our render state machine
	-- back at the beginning in case the just saved render can
	-- be used instead of doing a render
	fsm = addTransitionToAllRetryStates( fsm, _renderRetryCount,
			RenderStates.NeedsRender, 'renditions_status_change',
			DocstoreInfoStates.NeedAssetInfoForRender )

	fsm = addTransitionToAllRetryStates( fsm, _renderRetryCount,
			RenderStates.NeedsRender, 'new_long_side',
			DocstoreInfoStates.NeedAssetInfoForRender )

	------------------------------------------------------------
	-- SaveRenderStates
	------------------------------------------------------------
	fsm = addRetryTransitions( 
			fsm, _saveRenderRetryCount,
			SaveRenderStates.NeedToSaveRender, 'start_save_render',
			SaveRenderStates.SavingRender, 'retry_save_render',
			SendDataStates.SendPermanentError )

	fsm = addTransitionToAllRetryStates( fsm, _renderRetryCount,
			RenderStates.NeedsRender, 'start_save_render',
			SaveRenderStates.SavingRender )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedRenderDownload, 'start_save_render',
			SaveRenderStates.SavingRender )

	fsm = addTransitionToAllRetryStates( fsm, _saveRenderRetryCount,
			SaveRenderStates.SavingRender, 'saved_render',
			SendDataStates.HaveRenditionPath )

	fsm = addNoOpTranstionToAllRetryStates( fsm,
			SaveRenderStates.NeedToSaveRender, _saveRenderRetryCount,
			'has_pending_rendition_save' )

	fsm = addTransitionToAllRetryStates( fsm, _saveRenderRetryCount,
			SaveRenderStates.NeedToSaveRender, 'no_render_blob',
			SendDataStates.SendPermanentError )

	fsm = addTransitionToAllRetryStates( fsm, _saveRenderRetryCount,
			SaveRenderStates.NeedToSaveRender, 'no_save_path',
			SendDataStates.SendPermanentError )
	
	------------------------------------------------------------
	-- SaveDownloadStates
	------------------------------------------------------------
	-- save just original
	fsm = addTransitions( fsm, SaveDownloadStates.NeedToSaveOriginal, 
			{ { 'start_save_download', SaveDownloadStates.SavingOriginal },
			  { 'saved_valid_download', SendDataStates.HaveLocalNegativePath },
			  { 'no_binary_blob', SendDataStates.SendPermanentError } } )

	fsm = addTransitions( fsm, SaveDownloadStates.SavingOriginal, 
			{ { 'save_downloaded_blob_failed', SendDataStates.SendPermanentError },
			  { 'saved_valid_download', SendDataStates.HaveLocalNegativePath } } )

	-- save original then render
	fsm = addTransitions( fsm, SaveDownloadStates.NeedToSaveOriginalForRender, 
			{ { 'start_save_download', SaveDownloadStates.SavingOriginalForRender },
			  { 'saved_valid_download', RenderStates.NeedsRender },
			  { 'no_binary_blob', SendDataStates.SendPermanentError } } )

	fsm = addTransitions( fsm, SaveDownloadStates.SavingOriginalForRender, 
			{ { 'save_downloaded_blob_failed', SendDataStates.SendPermanentError },
			  { 'saved_valid_download', RenderStates.NeedsRender } } )
			
	-- save just proxy
	fsm = addTransitions( fsm, SaveDownloadStates.NeedToSaveProxy, 
			{ { 'start_save_download', SaveDownloadStates.SavingProxy },
			  { 'saved_valid_download', SendDataStates.HaveLocalNegativePath },
			  { 'no_binary_blob', SendDataStates.SendPermanentError } } )

	fsm = addTransitions( fsm, SaveDownloadStates.SavingProxy, 
			{ { 'save_downloaded_blob_failed', SendDataStates.SendPermanentError },
			  { 'saved_valid_download', SendDataStates.HaveLocalNegativePath } } )

	-- save proxy then render
	fsm = addTransitions( fsm, SaveDownloadStates.NeedToSaveProxyForRender, 
			{ { 'start_save_download', SaveDownloadStates.SavingProxyForRender },
			  { 'saved_valid_download', RenderStates.NeedsRender },
			  { 'no_binary_blob', SendDataStates.SendPermanentError } } )

	fsm = addTransitions( fsm, SaveDownloadStates.SavingProxyForRender, 
			{ { 'save_downloaded_blob_failed', SendDataStates.SendPermanentError },
			  { 'saved_valid_download', RenderStates.NeedsRender } } )

	-- save original for best possible negative
	fsm = addTransitions( fsm, SaveDownloadStates.NeedToSaveOriginalForBestPossibleLocalNegativePath, 
			{ { 'start_save_download', SaveDownloadStates.SavingOriginalForBestPossibleLocalNegativePath },
			  { 'saved_valid_download', SendDataStates.HaveOriginalForBestPossibleLocalNegativePath },
			  { 'no_binary_blob', SendDataStates.SendPermanentErrorForBestPossibleLocalNegativePath } } )

	fsm = addTransitions( fsm, SaveDownloadStates.SavingOriginalForBestPossibleLocalNegativePath, 
			{ { 'save_downloaded_blob_failed', SendDataStates.SendPermanentErrorForBestPossibleLocalNegativePath },
			  { 'saved_valid_download', SendDataStates.HaveOriginalForBestPossibleLocalNegativePath } } )

	-- save proxy for best possible negative, failures lead us to attempt to download an
	-- original which will transition to error eventually if we just can't download
	fsm = addTransitions( fsm, SaveDownloadStates.NeedToSaveProxyForBestPossibleLocalNegativePath, 
			{ { 'start_save_download', SaveDownloadStates.SavingProxyForBestPossibleLocalNegativePath },
			  { 'saved_valid_download', SendDataStates.HaveProxyForBestPossibleLocalNegativePath },
			  { 'no_binary_blob', SendDataStates.SendPermanentErrorForBestPossibleLocalNegativePath } } )

	fsm = addTransitions( fsm, SaveDownloadStates.SavingProxyForBestPossibleLocalNegativePath, 
			{ { 'save_downloaded_blob_failed', DownloadStates.NeedOriginalDownloadForBestPossibleLocalNegativePath },
			  { 'saved_valid_download', SendDataStates.HaveProxyForBestPossibleLocalNegativePath } } )

	-- just save xmp_sidecar
	fsm = addTransitions( fsm, SaveDownloadStates.NeedToSaveXmpSidecar, 
			{ { 'start_save_download', SaveDownloadStates.SavingXmpSidecar },
			  { 'saved_valid_download', SendDataStates.HaveLocalXmpSidecarPath },
			  { 'no_binary_blob', SendDataStates.SendPermanentError } } )

	fsm = addTransitions( fsm, SaveDownloadStates.SavingXmpSidecar,
			{ { 'save_downloaded_blob_failed', SendDataStates.SendPermanentError },
			  { 'saved_valid_download', SendDataStates.HaveLocalXmpSidecarPath } } )

	------------------------------------------------------------
	-- SendDataStates
	------------------------------------------------------------
	fsm = addTransitions( fsm, SendDataStates.HaveRenditionPath,
							{ { 'stale_data_sent', DocstoreInfoStates.NeedAssetInfoForRender },
							  { 'missing_data', DocstoreInfoStates.NeedAssetInfoForRender },
							  { 'missing_expected_path_computed_in_previous_step', SendDataStates.SendPermanentError },
							  { 'sent', EndStates.FinishedSendingRender } } )

	fsm = addTransitions( fsm, SendDataStates.HaveLocalNegativePath,
							{ { 'missing_expected_path_computed_in_previous_step', SendDataStates.SendPermanentError },
							  { 'sent', EndStates.FinishedSendingNegativePath } } )

	fsm = addTransitions( fsm, SendDataStates.HaveLocalXmpSidecarPath,
							{ { 'missing_expected_path_computed_in_previous_step', SendDataStates.SendPermanentError },
							  { 'sent', EndStates.FinishedSendingXmpSidecarPath } } )

	fsm = addTransitions( fsm, SendDataStates.NotXmpSidecar,
							{ { 'sent', EndStates.FinishedSendingXmpSidecarPath } } )

	fsm = addTransitions( fsm, SendDataStates.SendNoDownloadableRenderOrBinary,
							{ { 'sent', EndStates.NoDownloadableRenderOrBinary },
							  { 'new_long_side', DocstoreInfoStates.NeedAssetInfoForRender },
							  { 'new_docstore_render_info', DocstoreInfoStates.NeedAssetInfoForRender },
							  { 'new_docstore_binary_path_info', DocstoreInfoStates.NeedAssetInfoForRender } } )

	fsm = addTransitions( fsm, SendDataStates.SendNoDownloadableBinaryForRender,
							{ { 'sent', EndStates.NoDownloadableBinaryForRender },
							  { 'new_docstore_binary_path_info', DocstoreInfoStates.NeedAssetInfoForRender } } )

	fsm = addTransitions( fsm, SendDataStates.SendNoDownloadableBinary,
							{ { 'sent', EndStates.NoDownloadableBinary },
							  { 'new_docstore_binary_path_info', LocalBinaryPathCheckStates.CheckingForAnyLocalNegative } } )

	fsm = addTransitions( fsm, SendDataStates.SendNoDownloadableOriginal,
							{ { 'sent', EndStates.NoDownloadableOriginal },
							  { 'new_docstore_binary_path_info', LocalBinaryPathCheckStates.CheckingForLocalOriginal } } )

	fsm = addTransitions( fsm, SendDataStates.SendNoDownloadableXmpSidecar,
							{ { 'sent', EndStates.NoDownloadableXmpSidecar },
							  { 'new_docstore_render_info', DocstoreInfoStates.NeedXmpSidecarInfo } } )

	fsm = addTransitions( fsm, SendDataStates.SendPermanentDownloadError,
							{ { 'sent', EndStates.PermanentDownloadError } } )

	fsm = addTransitions( fsm, SendDataStates.SendPermanentError,
							{ { 'sent', EndStates.PermanentError } } )

	fsm = addTransitions( fsm, SendDataStates.SendPermanentBadAssetIdError,
							{ { 'sent', EndStates.PermanentBadAssetIdError } } )

	fsm = addTransitions( fsm, SendDataStates.SendPermanentMissingProfileRenditionPathError,
							{ { 'sent', EndStates.PermanentMissingProfileRenditionPathError } } )

	fsm = addTransitions( fsm, SendDataStates.SendPermanentDownloadErrorForBestPossibleLocalNegativePath,
							{ { 'sent', EndStates.WaitForDownloadableOriginalForBestPossibleLocalNegativePath } } )

	fsm = addTransitions( fsm, SendDataStates.SendPermanentErrorForBestPossibleLocalNegativePath,
							{ { 'sent', EndStates.WaitForDownloadableOriginalForBestPossibleLocalNegativePath } } )

	fsm = addTransitions( fsm, SendDataStates.HaveOriginalForBestPossibleLocalNegativePath,
							{ { 'missing_expected_path_computed_in_previous_step', SendDataStates.SendPermanentErrorForBestPossibleLocalNegativePath },
							  { 'sent', EndStates.FinishedBestPossibleNegativePath } } )

	fsm = addTransitions( fsm, SendDataStates.HaveProxyForBestPossibleLocalNegativePath,
							{ { 'missing_expected_path_computed_in_previous_step', SendDataStates.SendPermanentErrorForBestPossibleLocalNegativePath },
							  { 'sent_have_local_original', LocalBinaryPathCheckStates.NeedCheckOriginalForBestPossibleLocalNegativePath },
							  { 'sent_can_download_original', DownloadStates.NeedOriginalDownloadForBestPossibleLocalNegativePath },
							  { 'sent_cannot_download_original', EndStates.WaitForDownloadableOriginalForBestPossibleLocalNegativePath } } )

	------------------------------------------------------------
	-- EndStates
	------------------------------------------------------------
	fsm = addTransitions( fsm, EndStates.FinishedSendingRender,
							{ { 'new_long_side', DocstoreInfoStates.NeedAssetInfoForRender },
							  { 'new_docstore_render_info', DocstoreInfoStates.NeedAssetInfoForRender },
							  { 'reset', DocstoreInfoStates.NeedAssetInfoForRender } } )

	fsm = addTransitions( fsm, EndStates.NoDownloadableRenderOrBinary,
							{ { 'new_long_side', DocstoreInfoStates.NeedAssetInfoForRender },
							  { 'new_docstore_render_info', DocstoreInfoStates.NeedAssetInfoForRender },
							  { 'new_docstore_binary_path_info', DocstoreInfoStates.NeedAssetInfoForRender } } )

	fsm = addTransitions( fsm, EndStates.NoDownloadableBinaryForRender,
							{ { 'new_docstore_binary_path_info', DocstoreInfoStates.NeedAssetInfoForRender } } )

	fsm = addTransitions( fsm, EndStates.NoDownloadableBinary,
							{ { 'new_docstore_binary_path_info', LocalBinaryPathCheckStates.CheckingForAnyLocalNegative } } )
							  
	fsm = addTransitions( fsm, EndStates.NoDownloadableOriginal,
							{ { 'new_docstore_binary_path_info', LocalBinaryPathCheckStates.CheckingForLocalOriginal } } )

	fsm = addTransitions( fsm, EndStates.NoDownloadableXmpSidecar,
							{ { 'new_docstore_render_info', DocstoreInfoStates.NeedXmpSidecarInfo } } )

	fsm = addTransitions( fsm, EndStates.FinishedBestPossibleNegativePath,
							{ { 'reset', DocstoreInfoStates.NeedBestPossibleLocalNegativePathInfo } } )

	fsm = addTransitions( fsm, EndStates.WaitForDownloadableOriginalForBestPossibleLocalNegativePath,
							{ { 'reset', DocstoreInfoStates.NeedBestPossibleLocalNegativePathInfo },
							  { 'new_docstore_binary_path_info', DocstoreInfoStates.NeedBestPossibleLocalNegativePathInfo } } )

	fsm = LoupeRenditionStateMachine.addTransitions( 
			fsm, PixelPathProviderStateMachine )

	fsm = DownloadInvalidRenderStateMachine.addTransitions( 
			fsm, PixelPathProviderStateMachine )

	fsm = ExportFullRenditionStateMachine.addTransitions( 
			fsm, PixelPathProviderStateMachine )

	fsm = ExportBiggestPossibleRenditionStateMachine.addTransitions( 
			fsm, PixelPathProviderStateMachine )

	fsm = XmpDevelopStateMachine.addTransitions( 
			fsm, PixelPathProviderStateMachine )

	fsm = DownloadToFileStateMachine.addTransitions( 
			fsm, PixelPathProviderStateMachine )

	fsm = OriginalOrFallbackProxyLocalNegativeStateMachine.addTransitions( 
			fsm, PixelPathProviderStateMachine )

	return fsm

end

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

local function lhsIfNotEqual( a, b )

	if a ~= b then

		return a

	end

end

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

PixelPathProviderStateMachine.allStateGroupsExcept = function( stateGroupName )

	return WFFunctionUtils.filter( lhsIfNotEqual,
									StateGroupNameArray, stateGroupName )

end

--==============================================================================
-- export retry counts
--==============================================================================

PixelPathProviderStateMachine.DownloadRetryCount = _downloadRetryCount

PixelPathProviderStateMachine.RenderRetryCount = _renderRetryCount

PixelPathProviderStateMachine.SaveRenderRetryCount = _saveRenderRetryCount

--==============================================================================
-- initial states
--
-- define all initial states here.
--==============================================================================

PixelPathProviderStateMachine.StartStates = 
	setmetatable( 
		{

			NeedAnyLocalNegativePathInfo = PixelPathProviderStateMachine.DocstoreInfoStates.NeedAnyLocalNegativePathInfo,

			NeedAssetInfoForExportBiggestPossibleRendition = PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForExportBiggestPossibleRendition,

			NeedAssetInfoForExportFullRendition = PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForExportFullRendition,

			NeedAssetInfoForRender = PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender,

			NeedBestPossibleLocalNegativePathInfo = PixelPathProviderStateMachine.DocstoreInfoStates.NeedBestPossibleLocalNegativePathInfo,

			NeedDownloadOriginalInfo = PixelPathProviderStateMachine.DocstoreInfoStates.NeedDownloadOriginalInfo,

			NeedDownloadProxyInfo = PixelPathProviderStateMachine.DocstoreInfoStates.NeedDownloadProxyInfo,

			NeedXmpDevelopInfo = PixelPathProviderStateMachine.DocstoreInfoStates.NeedXmpDevelopInfo,

			NeedXmpSidecarInfo = PixelPathProviderStateMachine.DocstoreInfoStates.NeedXmpSidecarInfo,

			MaybeSendLoupeRenditionPathFromInMemoryCache = PixelPathProviderStateMachine.SendDataStates.MaybeSendLoupeRenditionPathFromInMemoryCache,
			
			NeedOriginalOrFallbackProxyLocalNegativePathInfo = PixelPathProviderStateMachine.DocstoreInfoStates.NeedOriginalOrFallbackProxyLocalNegativePathInfo,

		},
		{

			__newindex = makeReadOnlyMetamethod( 
							"PixelPathProviderStateMachine.StartStates" )

		} ) 

--==============================================================================
-- finish states
--==============================================================================

-- FinishStates is a non strict read only mirror of 
-- PixelPathProviderStateMachine.EndStates used to generate html documentation
PixelPathProviderStateMachine.getFinishStates = function()

	return getReadOnlyStateGroupNamespace( "EndStates" )

end

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

return PixelPathProviderStateMachine
