/**
 * @class	HumanInventoryWithFSM
 * @brief	HumanInventory... with FSM (synchronous, no anims)
 **/
class HumanInventoryWithFSM : HumanInventory
{
	protected ref HandFSM m_FSM;  /// hand slot state machine
	protected ref HandStateBase m_Empty;
	protected ref HandStateBase m_Equipped;

	void HumanInventoryWithFSM ()
	{
		m_FSM = new HandFSM();
	}
	
	void CreateStableStates ()
	{
		// stable states are created only if they do not exist already (for example created by derived class like DayZPlayerInventory)
		// @NOTE: this cannot be done in constructor, as there is NO owner set yet. GetManOwner() will therefore return NULL.
		if (!m_Empty)
			m_Empty = new HandStateEmpty(GetManOwner());
		if (!m_Equipped)
			m_Equipped = new HandStateEquipped(GetManOwner());
	}

	override void Init ()
	{
		// setup state machine
		if (LogManager.IsInventoryHFSMLogEnable()) hndDebugPrint("[hndfsm] Initializing Human Inventory FSM");
		
		// basic states
		CreateStableStates();
		
		// events
		HandEventBase __T__ = new HandEventTake;
		HandEventBase __D__ = new HandEventDrop;
		HandEventBase __Tw_ = new HandEventThrow;
		HandEventBase __M__ = new HandEventMoveTo;
		HandEventBase __W__ = new HandEventSwap;
		HandEventBase __F__ = new HandEventForceSwap;
		HandEventBase __X__ = new HandEventDestroy;
		HandEventBase __Xd_ = new HandEventDestroyed;
		HandEventBase __R__ = new HandEventDestroyAndReplaceWithNew;
		HandEventBase __Re_ = new HandEventDestroyAndReplaceWithNewElsewhere;
		HandEventBase __Rh_ = new HandEventDestroyElsewhereAndReplaceWithNewInHands;
		HandEventBase __Rd_ = new HandEventReplaced;
		HandEventBase __Cd_ = new HandEventCreated;
		HandEventBase _fin_ = new HandEventHumanCommandActionFinished;
		HandEventBase _abt_ = new HandEventHumanCommandActionAborted;


		HandReplacingItemInHands replacing = new HandReplacingItemInHands(GetManOwner());
		HandReplacingItemInHands replacingElsewhere = new HandReplacingItemInHands(GetManOwner());
		HandReplacingItemElsewhereWithNewInHands replacingElsewhere3 = new HandReplacingItemElsewhereWithNewInHands(GetManOwner());

		// setup transitions
		m_FSM.AddTransition(new HandTransition( m_Empty   , __T__,  m_Equipped, new HandActionTake, new HandGuardHasItemInEvent(GetManOwner())));
		m_FSM.AddTransition(new HandTransition( m_Empty   , __Cd_,  m_Equipped, new HandActionCreated, new HandGuardHasItemInEvent(GetManOwner())));

		m_FSM.AddTransition(new HandTransition(m_Equipped, __D__,    m_Empty, new HandActionDrop, null));
		m_FSM.AddTransition(new HandTransition(m_Equipped, __Tw_,    m_Empty, new HandActionThrow, null));
		m_FSM.AddTransition(new HandTransition(m_Equipped, __M__,    m_Empty, new HandActionMoveTo, new HandGuardAnd(new HandGuardHasItemInHands(GetManOwner()), new HandGuardCanMove(GetManOwner()))));
		m_FSM.AddTransition(new HandTransition(m_Equipped, __X__,    m_Empty, new HandActionDestroy, new HandGuardHasItemInHands(GetManOwner())));
		m_FSM.AddTransition(new HandTransition(m_Equipped, __Xd_,    m_Empty, new HandActionDestroyed, new HandGuardHasDestroyedItemInHands(GetManOwner())));
		m_FSM.AddTransition(new HandTransition(m_Equipped, __Xd_, m_Equipped, new HandActionDestroyed, new HandGuardNot(new HandGuardHasDestroyedItemInHands(GetManOwner()))));

		m_FSM.AddTransition(new HandTransition(m_Equipped, __R__, replacing));
		m_FSM.AddTransition(new HandTransition(replacing, _fin_, m_Equipped));
			replacing.AddTransition(new HandTransition(replacing.m_Replacing, _abt_, m_Equipped));

		m_FSM.AddTransition(new HandTransition(m_Equipped, __Re_, replacingElsewhere));
		m_FSM.AddTransition(new HandTransition(replacingElsewhere, _fin_, m_Empty));
			replacingElsewhere.AddTransition(new HandTransition(replacingElsewhere.m_Replacing, _abt_, m_Equipped));
		
		m_FSM.AddTransition(new HandTransition(m_Empty, __Rh_, replacingElsewhere3));
		m_FSM.AddTransition(new HandTransition(replacingElsewhere3, _fin_, m_Equipped));
			replacingElsewhere3.AddTransition(new HandTransition(replacingElsewhere3.m_Replacing, _abt_, m_Empty));
		
		m_FSM.AddTransition(new HandTransition(m_Equipped, __Rd_, m_Equipped, new HandActionReplaced, null));
		m_FSM.AddTransition(new HandTransition(m_Equipped, __W__, m_Equipped, new HandActionSwap, new HandGuardAnd(new HandGuardHasItemInHands(GetManOwner()), new HandGuardCanSwap(GetManOwner()))));
		m_FSM.AddTransition(new HandTransition(m_Equipped, __F__, m_Equipped, new HandActionForceSwap, new HandGuardAnd(new HandGuardHasItemInHands(GetManOwner()), new HandGuardCanForceSwap(GetManOwner()))));

		// start FSM
		m_FSM.SetInitialState(m_Empty);
		m_FSM.Start();
		
		super.Init();
	}

	bool CanProcessHandEvents () { return m_FSM && m_FSM.IsRunning(); }

	/**@fn			ProcessHandEvent
	 * @brief		hand's fsm handling of events
	 * @NOTE:		warning: ProcessHandEvent can be called only within DayZPlayer::HandleInventory (or ::CommandHandler)
	 **/
	override bool ProcessHandEvent (HandEventBase e)
	{
		//SyncRemote(e);
		if (m_FSM.ProcessEvent(e) == ProcessEventResult.FSM_OK)
		{
			#ifdef ENABLE_LOGGING
			if ( LogManager.IsInventoryHFSMLogEnable() )
			{
				Debug.InventoryHFSMLog("STS = " + GetManOwner().GetSimulationTimeStamp() , e.ToString(), "ProcessHandEvent", e.m_Player.ToString() );
			}
			#endif
			return true;
		}
		else
		{
			//if (LogManager.IsInventoryHFSMLogEnable()) hndDebugPrint("FSM refused to process event (no transition): src=" + GetCurrentState().ToString() + " event=" + e.ToString());
			return false;
		}
	}
	
	bool ProcessHandAbortEvent (HandEventBase e)
	{
		//SyncRemote(e);
		ProcessEventResult aa;
		m_FSM.ProcessAbortEvent(e, aa);
		if (aa == ProcessEventResult.FSM_OK)
		{
			if (LogManager.IsInventoryHFSMLogEnable()) hndDebugSpam("[hndfsm]  STS = " + GetManOwner().GetSimulationTimeStamp() + " Processed event e=" + e.ToString());
			return true;
		}
		else
		{
			//if (LogManager.IsInventoryHFSMLogEnable()) hndDebugPrint("FSM refused to process event (no transition): src=" + GetCurrentState().ToString() + " event=" + e.ToString());
			return false;
		}
	}
	
		
	void CheckFSMState()
	{
		if (GetEntityInHands())
		{
			if (!m_FSM.IsRunning() || GetCurrentStateID() == HandStateID.Empty) //forcing when not running or in stable state only
				m_FSM.SetCurrentState(m_Equipped);
		}
		else
		{
			if (!m_FSM.IsRunning() || GetCurrentStateID() == HandStateID.Equipped) //forcing when not running or in stable state only
				m_FSM.SetCurrentState(m_Empty);
		}
	}

	override bool OnStoreLoad (ParamsReadContext ctx, int version)
	{
		if ( !super.OnStoreLoad(ctx, version) )
			return false;
		
		//@TODO
		//m_FSM.OnStoreLoad(ctx, version);

		return true;
	}

	override void OnStoreSave (ParamsWriteContext ctx)
	{
		super.OnStoreSave(ctx);
    //@TODO
		//m_FSM.OnStoreSave(ctx, version);
	}

	/**@fn			GetCurrentStateID
	 * @return		identifier of current stable state
	 **/
	int GetCurrentStateID () { return m_FSM.GetCurrentStateID(); }
	
	bool IsIdle () { return m_FSM.GetCurrentState().IsIdle(); }

	/**@fn			NetSyncCurrentStateID
	 * @brief		Engine callback - network synchronization of FSM's state. not intended to direct use.
	 **/
	void NetSyncCurrentStateID (int id)
	{
		m_FSM.NetSyncCurrentStateID(id);
	}
};

