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

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'

--==============================================================================
-- 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 addTransitionAsNoOpUnlessSet = 
		WFFiniteStateMachine.addTransitionAsNoOpUnlessSet

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

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 _saveDownloadRetryCount = 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 = addToStateGroup( 
			fsm, "DocstoreInfoStates",
					'NeedXmpDevelopInfo',
					'NeedXmpDevelopInfoFromDocstore',
					'FetchingXmpDevelopInfo',
					'NeedCheckRenderSettingsForXmpDevelopInfo',
					'UseDefaultRenderSettingsForMissingXmpDevelopRender' )
	
	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',
					'NeedCheckForLocalXmpDevelopForRender',
					'CheckingForLocalXmpDevelopForRender',
					'FetchLocalXmpDevelopForRender',
					'FetchingLocalXmpDevelopForRender',
					'NeedCheckForLocalXmpDevelop',
					'CheckingForLocalXmpDevelop',
					'NeedCheckForLocalXmpSidecar',
					'CheckingForLocalXmpSidecar',
					'AnalyzeXmpSidecar',
					'AnalyzingXmpSidecar' )

	return fsm

end

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

StateGroupFSMConstructor.DownloadStates = function( fsm )

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

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

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

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

	fsm = ExportBiggestPossibleRenditionStateMachine.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",
						makeRetryableStates( _saveDownloadRetryCount,
							'NeedToSaveOriginalForRender',
							'SavingOriginalForRender',
							'NeedToSaveProxyForRender',
							'SavingProxyForRender',
							'NeedToSaveOriginal',
							'SavingOriginal',
							'NeedToSaveProxy',
							'SavingProxy',
							'NeedToSaveOriginalForBestPossibleLocalNegativePath',
							'SavingOriginalForBestPossibleLocalNegativePath',
							'NeedToSaveProxyForBestPossibleLocalNegativePath',
							'SavingProxyForBestPossibleLocalNegativePath',
							'NeedToSaveXmpDevelop',
							'NeedToSaveXmpDevelopForRender',
							'SavingXmpDevelop',
							'SavingXmpDevelopForRender',
							'NeedToSaveXmpSidecar',
							'SavingXmpSidecar' ) )
	
	return fsm

end

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

StateGroupFSMConstructor.SendDataStates = function( fsm )

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

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

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

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

	return fsm

end

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

StateGroupFSMConstructor.EndStates = function( fsm )

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

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

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

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

	return fsm

end

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

StateGroupNameArray, StateGroupFSMConstructor =
	ExportFullRenditionStateMachine.addCustomStateGroups( 
		StateGroupNameArray, StateGroupFSMConstructor )

--==============================================================================
-- 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 getStateGroupNamespace( stateGroupName )

	-- 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()

	return getStateGroupMap( fsmWithStateGroupStates, stateGroupName )

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 addStateGroupNamespace( PixelPathProviderStateMachine, 
										stateGroupName )

	PixelPathProviderStateMachine[ stateGroupName ] = 
		getStateGroupNamespace( 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 } } )
	
	
	fsm = addTransitions( fsm, DocstoreInfoStates.NeedXmpDevelopInfo, 
							{ { 'must_fetch', DocstoreInfoStates.NeedXmpDevelopInfoFromDocstore },
							  { 'already_present', DocstoreInfoStates.NeedCheckRenderSettingsForXmpDevelopInfo } } )
							  
	fsm = addTransitions( fsm, DocstoreInfoStates.NeedXmpDevelopInfoFromDocstore, 
							{ { 'start_docstore_read', DocstoreInfoStates.FetchingXmpDevelopInfo },
							  { 'received_docstore_read', DocstoreInfoStates.NeedCheckRenderSettingsForXmpDevelopInfo } } )
	
	fsm = addTransitions( fsm, DocstoreInfoStates.FetchingXmpDevelopInfo,
							{ { 'received_docstore_read', DocstoreInfoStates.NeedCheckRenderSettingsForXmpDevelopInfo } } )
	
	fsm = addTransitions( fsm, DocstoreInfoStates.NeedCheckRenderSettingsForXmpDevelopInfo,
							{ { 'xmp_develop_is_external',  LocalBinaryPathCheckStates.NeedCheckForLocalXmpDevelop } } )
	
	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.NeedXmpDevelopInfo,
				{ { '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.FetchingXmpDevelopInfo,
			{ { '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 },
							  { 'new_long_side', DocstoreInfoStates.NeedAssetInfoForRender }, 
							  { 'need_develop_xmp', LocalBinaryPathCheckStates.NeedCheckForLocalXmpDevelopForRender },
							  { '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 },
							  { 'no_original', SendDataStates.SendNoDownloadableRenderOrBinary } } )

	fsm = addTransitions( fsm, PixelCacheCheckStates.ChooseRenderOnlyRenditionStrategy, 
							{ { 'new_docstore_render_info', DocstoreInfoStates.NeedAssetInfoForRender },
							  { 'new_docstore_binary_path_info', DocstoreInfoStates.NeedAssetInfoForRender },
							  { 'new_long_side', DocstoreInfoStates.NeedAssetInfoForRender }, 
							  { 'need_develop_xmp', LocalBinaryPathCheckStates.NeedCheckForLocalXmpDevelopForRender },
							  { '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 }, 
							  { 'no_original', 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_binary', 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 } } )

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

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

	fsm = addTransitions( fsm, LocalBinaryPathCheckStates.CheckingForLocalProxy, 
							{ { 'has_local', SendDataStates.HaveLocalNegativePath },
							  { 'need_download_binary', 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_develop
	fsm = addTransitions( fsm, LocalBinaryPathCheckStates.NeedCheckForLocalXmpDevelop, 
							{ { 'start_non_rendition_check', LocalBinaryPathCheckStates.CheckingForLocalXmpDevelop } } )

	fsm = addTransitions( fsm, LocalBinaryPathCheckStates.CheckingForLocalXmpDevelop, 
							{ { 'has_local', SendDataStates.HaveLocalXmpDevelopPath },
							  { 'need_download_binary', DownloadStates.NeedXmpDevelopDownload },
							  { 'no_downloadable_uri', SendDataStates.SendNoDownloadableXmpDevelop } } )

	-- check for xmp_develop to use for rendering
	fsm = addTransitions( fsm, LocalBinaryPathCheckStates.NeedCheckForLocalXmpDevelopForRender, 
							{ { 'start_non_rendition_check', LocalBinaryPathCheckStates.CheckingForLocalXmpDevelopForRender } } )

	fsm = addTransitions( fsm, LocalBinaryPathCheckStates.CheckingForLocalXmpDevelopForRender, 
							{ { 'has_local', LocalBinaryPathCheckStates.FetchLocalXmpDevelopForRender },
							  { 'need_download_binary', DownloadStates.NeedXmpDevelopDownloadForRender },
							  { 'no_downloadable_uri', DocstoreInfoStates.UseDefaultRenderSettingsForMissingXmpDevelopRender } } )
							  
	fsm = addTransitions( fsm, DocstoreInfoStates.UseDefaultRenderSettingsForMissingXmpDevelopRender,
							{ { 'use_default_settings', PixelCacheCheckStates.NeedCheckForPixelPathInCache } } )
	
	fsm = addTransitions( fsm, LocalBinaryPathCheckStates.FetchLocalXmpDevelopForRender,
							{ { 'start_read_dev_xmp', LocalBinaryPathCheckStates.FetchingLocalXmpDevelopForRender },
							  { 'need_download_dev_xmp', DownloadStates.NeedXmpDevelopDownloadForRender } } )
	
	fsm = addTransitions( fsm, LocalBinaryPathCheckStates.FetchingLocalXmpDevelopForRender,
							{ { 'finished_read_dev_xmp', PixelCacheCheckStates.NeedCheckForPixelPathInCache },
							  { 'need_download_dev_xmp', DownloadStates.NeedXmpDevelopDownloadForRender } } )
	
	-- 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_binary', 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_binary', DocstoreInfoStates.NeedBestPossibleLocalNegativePathInfo },
							  { 'no_downloadable_uri', EndStates.WaitForDownloadableOriginalForBestPossibleLocalNegativePath } } )

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

	fsm = addTransitions( fsm, LocalBinaryPathCheckStates.CheckingProxyForBestPossibleLocalNegativePath,
							{ { 'has_local', SendDataStates.HaveProxyForBestPossibleLocalNegativePath },
							  { 'need_download_binary', DocstoreInfoStates.NeedBestPossibleLocalNegativePathInfo },
							  { '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 develop
	fsm = addRetryTransitions( 
			fsm, _downloadRetryCount,
			DownloadStates.NeedXmpDevelopDownload, 'start_download',
			DownloadStates.DownloadingXmpDevelop, 'retry_download',
			SendDataStates.SendPermanentDownloadError )

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

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

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedXmpDevelopDownload, 'start_save_download',
			SaveDownloadStates.SavingXmpDevelop )

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

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

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

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedXmpDevelopDownload, 'have_pending_save_for_blob',
			SaveDownloadStates.NeedToSaveXmpDevelop )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingXmpDevelop, 'missing_download_info',
			DocstoreInfoStates.NeedXmpDevelopInfo )
			
	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingXmpDevelop, 'received_download',
			SaveDownloadStates.NeedToSaveXmpDevelop )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingXmpDevelop, 'reschedule_download',
			DownloadStates.NeedXmpDevelopDownload )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedXmpDevelopDownload, 'saved_valid_download',
			SendDataStates.HaveLocalXmpDevelopPath )
			
	-- download xmp develop as part of rendition render
	fsm = addRetryTransitions( 
			fsm, _downloadRetryCount,
			DownloadStates.NeedXmpDevelopDownloadForRender, 'start_download',
			DownloadStates.DownloadingXmpDevelopForRender, 'retry_download',
			SendDataStates.SendPermanentDownloadError )

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

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

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedXmpDevelopDownloadForRender, 'start_save_download',
			SaveDownloadStates.SavingXmpDevelopForRender )

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

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

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

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedXmpDevelopDownloadForRender, 'have_pending_save_for_blob',
			SaveDownloadStates.NeedToSaveXmpDevelopForRender )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingXmpDevelopForRender, 'missing_download_info',
			DocstoreInfoStates.NeedAssetInfoForRender )
			
	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingXmpDevelopForRender, 'received_download',
			SaveDownloadStates.NeedToSaveXmpDevelopForRender )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.DownloadingXmpDevelopForRender, 'reschedule_download',
			DownloadStates.NeedXmpDevelopDownloadForRender )

	fsm = addTransitionToAllRetryStates( fsm, _downloadRetryCount,
			DownloadStates.NeedXmpDevelopDownloadForRender, 'saved_valid_download',
			LocalBinaryPathCheckStates.FetchLocalXmpDevelopForRender )

	-- 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 = addRetryTransitions( 
			fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveOriginal, 'start_save_download',
			SaveDownloadStates.SavingOriginal, 'retry_save_download',
			SendDataStates.SendPermanentError )

	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveOriginal, 'saved_valid_download',
			SendDataStates.HaveLocalNegativePath )

	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.SavingOriginal, 'saved_valid_download',
			SendDataStates.HaveLocalNegativePath )

	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveOriginal, 'no_binary_blob',
			SendDataStates.SendPermanentError )
	
	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveOriginal, 'cannot_save',
			SendDataStates.SendPermanentError )

	-- save original then render
	fsm = addRetryTransitions( 
			fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveOriginalForRender, 'start_save_download',
			SaveDownloadStates.SavingOriginalForRender, 'retry_save_download',
			SendDataStates.SendPermanentError )

	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveOriginalForRender, 'saved_valid_download',
			RenderStates.NeedsRender )

	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.SavingOriginalForRender, 'saved_valid_download',
			RenderStates.NeedsRender )

	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveOriginalForRender, 'no_binary_blob',
			SendDataStates.SendPermanentError )
	
	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveOriginalForRender, 'cannot_save',
			SendDataStates.SendPermanentError )
			
	-- save just proxy
	fsm = addRetryTransitions( 
			fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveProxy, 'start_save_download',
			SaveDownloadStates.SavingProxy, 'retry_save_download',
			SendDataStates.SendPermanentError )

	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveProxy, 'saved_valid_download',
			SendDataStates.HaveLocalNegativePath )

	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.SavingProxy, 'saved_valid_download',
			SendDataStates.HaveLocalNegativePath )
	
	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveProxy, 'no_binary_blob',
			SendDataStates.SendPermanentError )
	
	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveProxy, 'cannot_save',
			SendDataStates.SendPermanentError )

	-- save proxy then render
	fsm = addRetryTransitions( 
			fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveProxyForRender, 'start_save_download',
			SaveDownloadStates.SavingProxyForRender, 'retry_save_download',
			SendDataStates.SendPermanentError )

	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveProxyForRender, 'saved_valid_download',
			RenderStates.NeedsRender )

	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.SavingProxyForRender, 'saved_valid_download',
			RenderStates.NeedsRender )
	
	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveProxyForRender, 'no_binary_blob',
			SendDataStates.SendPermanentError )
	
	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveProxyForRender, 'cannot_save',
			SendDataStates.SendPermanentError )

	-- save original for best possible negative
	fsm = addRetryTransitions( 
			fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveOriginalForBestPossibleLocalNegativePath, 'start_save_download',
			SaveDownloadStates.SavingOriginalForBestPossibleLocalNegativePath, 'retry_save_download',
			SendDataStates.SendPermanentErrorForBestPossibleLocalNegativePath )

	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveOriginalForBestPossibleLocalNegativePath, 'saved_valid_download',
			SendDataStates.HaveOriginalForBestPossibleLocalNegativePath )

	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.SavingOriginalForBestPossibleLocalNegativePath, 'saved_valid_download',
			SendDataStates.HaveOriginalForBestPossibleLocalNegativePath )

	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveOriginalForBestPossibleLocalNegativePath, 'no_binary_blob',
			SendDataStates.SendPermanentErrorForBestPossibleLocalNegativePath )
	
	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveOriginalForBestPossibleLocalNegativePath, 'cannot_save',
			SendDataStates.SendPermanentErrorForBestPossibleLocalNegativePath )

	-- 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 = addRetryTransitions( 
			fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveProxyForBestPossibleLocalNegativePath, 'start_save_download',
			SaveDownloadStates.SavingProxyForBestPossibleLocalNegativePath, 'retry_save_download',
			DownloadStates.NeedOriginalDownloadForBestPossibleLocalNegativePath )

	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveProxyForBestPossibleLocalNegativePath, 'saved_valid_download',
			SendDataStates.HaveProxyForBestPossibleLocalNegativePath )

	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.SavingProxyForBestPossibleLocalNegativePath, 'saved_valid_download',
			SendDataStates.HaveProxyForBestPossibleLocalNegativePath )

	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveProxyForBestPossibleLocalNegativePath, 'no_binary_blob',
			SendDataStates.SendPermanentErrorForBestPossibleLocalNegativePath )
	
	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveProxyForBestPossibleLocalNegativePath, 'cannot_save',
			SendDataStates.SendPermanentErrorForBestPossibleLocalNegativePath )
	
	-- just save xmp_develop
	fsm = addRetryTransitions( 
			fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveXmpDevelop, 'start_save_download',
			SaveDownloadStates.SavingXmpDevelop, 'retry_save_download',
			SendDataStates.SendPermanentError )

	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveXmpDevelop, 'saved_valid_download',
			SendDataStates.HaveLocalXmpDevelopPath )

	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.SavingXmpDevelop, 'saved_valid_download',
			SendDataStates.HaveLocalXmpDevelopPath )
	
	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveXmpDevelop, 'no_binary_blob',
			SendDataStates.SendPermanentError )
	
	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveXmpDevelop, 'cannot_save',
			SendDataStates.SendPermanentError )
	
	-- save xmp_develop then render
	fsm = addRetryTransitions( 
			fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveXmpDevelopForRender, 'start_save_download',
			SaveDownloadStates.SavingXmpDevelopForRender, 'retry_save_download',
			SendDataStates.SendPermanentError )

	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveXmpDevelopForRender, 'saved_valid_download',
			LocalBinaryPathCheckStates.FetchLocalXmpDevelopForRender )

	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.SavingXmpDevelopForRender, 'saved_valid_download',
			LocalBinaryPathCheckStates.FetchLocalXmpDevelopForRender )

	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveXmpDevelopForRender, 'no_binary_blob',
			SendDataStates.SendPermanentError )
	
	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveXmpDevelopForRender, 'cannot_save',
			SendDataStates.SendPermanentError )

	-- just save xmp_sidecar
	fsm = addRetryTransitions(
			fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveXmpSidecar, 'start_save_download',
			SaveDownloadStates.SavingXmpSidecar, 'retry_save_download',
			SendDataStates.SendPermanentError )

	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveXmpSidecar, 'saved_valid_download',
			SendDataStates.HaveLocalXmpSidecarPath )

	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.SavingXmpSidecar, 'saved_valid_download',
			SendDataStates.HaveLocalXmpSidecarPath )

	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveXmpSidecar, 'no_binary_blob',
			SendDataStates.SendPermanentError )

	fsm = addTransitionToAllRetryStates( fsm, _saveDownloadRetryCount,
			SaveDownloadStates.NeedToSaveXmpSidecar, 'cannot_save',
			SendDataStates.SendPermanentError )

	------------------------------------------------------------
	-- 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.HaveLocalXmpDevelopPath,
							{ { 'missing_expected_path_computed_in_previous_step', SendDataStates.SendPermanentError },
							  { 'sent', EndStates.FinishedSendingXmpDevelopPath } } )

	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.SendNoDownloadableXmpDevelop,
							{ { 'sent', EndStates.NoDownloadableXmpDevelop },
							  { 'new_long_side', DocstoreInfoStates.NeedXmpDevelopInfo },
							  { 'new_docstore_render_info', DocstoreInfoStates.NeedXmpDevelopInfo } } )

	fsm = addTransitions( fsm, SendDataStates.SendNoDownloadableXmpSidecar,
							{ { 'sent', EndStates.NoDownloadableXmpSidecar },
							  { 'new_long_side', DocstoreInfoStates.NeedXmpSidecarInfo },
							  { '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.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.NoDownloadableXmpDevelop,
							{ { 'new_long_side', DocstoreInfoStates.NeedXmpDevelopInfo },
							  { 'new_docstore_render_info', DocstoreInfoStates.NeedXmpDevelopInfo } } )

	fsm = addTransitions( fsm, EndStates.NoDownloadableXmpSidecar,
							{ { 'new_long_side', DocstoreInfoStates.NeedXmpSidecarInfo },
							  { '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 )

	------------------------------------------------------------
	-- noOp transitions that can occur at any time can be 
	-- ignored
	------------------------------------------------------------

	fsm = addTransitionAsNoOpUnlessSet( fsm, 'new_long_side' )

	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

PixelPathProviderStateMachine.SaveDownloadRetryCount = _saveDownloadRetryCount

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

return PixelPathProviderStateMachine
