#pragma once

#include "AnimationGraph.h"
#include "STLPoolAllocator.h"

class CAnimationGraphStates
	: public IAnimationGraphState
{
private:
	typedef unsigned char LayerIndex;
	struct TQueryIDBinding
	{
		LayerIndex layerIndex;
		TAnimationGraphQueryID layerQueryId;
		TAnimationGraphQueryID wrapperQueryId;
	};
	typedef std::list< TQueryIDBinding, stl::STLPoolAllocator<TQueryIDBinding> > QueryIDBindings;
	QueryIDBindings m_QueryIDBindings;

	std::vector< IAnimationGraphState* > m_layers;


	std::map<string, InputID> m_wrapperInputIDs; // InputID signalID = m_wrapperInputIDs["signal"];
	std::map<InputID, std::vector<InputID> > m_wrapperToLayerInputIDs; // InputID inputIDLayer = m_wrapperToLayerInputIDs[inputID][layerIndex];

/*
	// union of all inputs in all layers (input IDs are generated by the wrapper).
	// TODO: map internal IDs to layer IDs, precache IDs to not have to lookup strings every time.
	std::map<string, InputID> m_inputID; 

	// use this to convert a wrapper InputId to a layer InputID
	// m_layerInputIDs[wrapperInputID][layerIndex];
	//std::vector<InputID> m_wrapperToLayerInputIDs[CAnimationGraph::MAX_INPUTS];
	std::vector<InputID[CAnimationGraph::MAX_INPUTS]> m_wrapperToLayerInputIDs;
*/

	unsigned int m_NextQueryID;
	unsigned char m_NextInputID;

	typedef std::vector< TAnimationGraphQueryID > VectorQueryIDs;
	VectorQueryIDs m_waitForEnterStateWrapperIDs;
	TAnimationGraphQueryID m_nextLeaveStateQueryID;
	bool m_bQueryCompleteReceivedWhileSettingInput;

	struct SListener
	{
		IAnimationGraphStateListener * pListener;
		char name[16 - sizeof(IAnimationGraphStateListener*)];

		bool operator==(const SListener& other) const
		{
			return pListener == other.pListener;
		}
	};

	std::vector<SListener> m_listeners;
	std::vector<SListener> m_callingListeners;

	// IAnimationGraphStateListener
	class LayerListener : public IAnimationGraphStateListener
	{
	private:
		CAnimationGraphStates* owner;

	public:
		LayerIndex layerIndex;
		LayerListener( CAnimationGraphStates* _owner, LayerIndex _layerIndex ) : owner(_owner), layerIndex(_layerIndex) {}

	private:
		virtual void SetOutput( const char * output, const char * value ) { owner->SetOutput( output, value ); }
		virtual void QueryComplete( TAnimationGraphQueryID queryID, bool succeeded );
		virtual void DestroyedState( IAnimationGraphState* agState ) { owner->DestroyedState( agState ); }
	};
	std::list< LayerListener > m_layerListeners;

	void SetOutput( const char * output, const char * value );
	void QueryComplete( TAnimationGraphQueryID queryID, bool succeeded, LayerIndex layerIndex );
	void DestroyedState( IAnimationGraphState* agState );

	TAnimationGraphQueryID GenerateWrapperQueryID()
	{
		// simple, increasing running number
		return ++m_NextQueryID;
	}
	TAnimationGraphQueryID GenerateWrapperInputID()
	{
		// simple, increasing running number
		return m_NextInputID++;
	}

	TAnimationGraphQueryID* AddQueryIDPair( TAnimationGraphQueryID wrapperQueryID, LayerIndex layerIndex );
	void RemoveLastQueryIDPair( TAnimationGraphQueryID wrapperQueryID, LayerIndex layerIndex );
	int FindAndRemoveQueryIDPair( TAnimationGraphQueryID layerQueryID, LayerIndex layerIndex, TAnimationGraphQueryID & wrapperQueryID );
	void SendQueryComplete( TAnimationGraphQueryID wrapperQueryID, bool succeeded );


	std::set< TAnimationGraphQueryID, std::less<TAnimationGraphQueryID>, stl::STLPoolAllocator<TAnimationGraphQueryID> > m_succeeded;
	bool HasAlreadySucceeded( TAnimationGraphQueryID wrapperQueryID ) const { return m_succeeded.find( wrapperQueryID ) != m_succeeded.end(); }
	void RememberSucceeded( TAnimationGraphQueryID wrapperQueryID ) { m_succeeded.insert( wrapperQueryID ); }
	void ClearSucceeded( TAnimationGraphQueryID wrapperQueryID ) { m_succeeded.erase( wrapperQueryID ); }


public:
	CAnimationGraphStates() : m_NextQueryID(0), m_NextInputID(0) {}
	~CAnimationGraphStates();

	void AddLayerReference( IAnimationGraphState* pAnimationGraphState );
	void RebindInputs();

	// recurse setting. query mechanism needs to be wrapped by wrapper.
	// Associated QueryID will be given to QueryComplete when ALL layers supporting the input have reached their matching states.
	// wrapper generates it's own query IDs which are associated to a bunch of sub IDs with rules for how to handle the sub IDs into wrapped IDs.
private:
	template < class InputType >
	bool SetInputT( InputID inputID, InputType inputValue, TAnimationGraphQueryID * pQueryID = 0 );
public:
	virtual bool SetInput( InputID inputID, float inputValue, TAnimationGraphQueryID * pQueryID = 0 );
	virtual bool SetInput( InputID inputID, int inputValue, TAnimationGraphQueryID * pQueryID = 0 );
	virtual bool SetInput( InputID inputID, const char * inputValue, TAnimationGraphQueryID * pQueryID = 0 );
	virtual bool SetInputOptional( InputID, const char *, TAnimationGraphQueryID * pQueryID = 0 );

	virtual void ClearInput( InputID );
	virtual void LockInput( InputID, bool locked );

	// assert all equal, use any (except if signalled, then return the one not equal to default, or return default of all default)
	virtual void GetInput( InputID, char * ) const;

	// AND all layers
	virtual bool IsDefaultInputValue( InputID ) const;

	// returns NULL if InputID is doesn't exist
	virtual const char* GetInputName( InputID ) const;

	// When QueryID of SetInput (reached queried state) is emitted this function is called by the outside, by convention(verify!).
	// Remember which layers supported the SetInput query and emit QueryLeaveState QueryComplete when all those layers have left those states.
	virtual void QueryLeaveState( TAnimationGraphQueryID * pQueryID );

	// assert all equal, forward to all layers, complete when all have changed once (trivial, since all change at once via SetInput).
	// (except for signalled, forward only to layers which currently are not default, complete when all those have changed).
	virtual void QueryChangeInput( InputID, TAnimationGraphQueryID * );

	// Just register and non-selectivly call QueryComplete on all listeners (regardless of what ID's they are actually interested in).
	virtual void AddListener( const char * name, IAnimationGraphStateListener * pListener );
	virtual void RemoveListener( IAnimationGraphStateListener * pListener );

	// Not used
	virtual bool DoesInputMatchState( InputID);

	// TODO: This should be turned into registered callbacks or something instead (look at AnimationGraphStateListener).
	// Use to access the SelectLocomotionState() callback in CAnimatedCharacter.
	// Only set for fullbody, null for upperbody.
	virtual void SetAnimatedCharacter( class CAnimatedCharacter* animatedCharacter, int layerIndex, IAnimationGraphState* parentLayerState );

	// simply recurse
	virtual bool Update();
	virtual void Release();
	virtual void ForceTeleportToQueriedState();

	// simply recurse (will be ignored by each layer individually if state not found)
	virtual void PushForcedState( const char * state, TAnimationGraphQueryID * pQueryID = 0 );

	// simply recurse
	virtual void ClearForcedStates();

	// simply recurse
	virtual void SetBasicStateData( const SAnimationStateData& );

	// same as GetInput above
	virtual float GetInputAsFloat( InputID inputId );

	// wrapper generates it's own input IDs for the union of all inputs in all layers, and for each input it maps to the layer specific IDs.
	virtual InputID GetInputId( const char *input );

	// simply recurse (preserve order), and don't forget to serialize the wrapper stuff, ID's or whatever.
	virtual void Serialize( TSerialize ser );

	// simply recurse
	virtual void SetAnimationActivation( bool activated );
	virtual bool GetAnimationActivation();

	// Concatenate all layers state names with '+'. Use only fullbody layer state name if upperbody layer is not allowed/mixed.
	virtual const char * GetCurrentStateName();

	// simply recurse
	virtual void Pause( bool pause, EAnimationGraphPauser pauser );

	// is the same for all layers (equal assertion should not even be needed)
	virtual bool IsInDebugBreak();

	// find highest layer that has output id, or null (this allows upperbody to override fullbody).
	// Use this logic when calling SetOutput on listeners.
	virtual const char * QueryOutput( const char * name );

	// Exact positioning: Forward to fullbody layer only (hardcoded)
	virtual IAnimationSpacialTrigger * SetTrigger( const SAnimationTargetRequest& req, EAnimationGraphTriggerUser user, TAnimationGraphQueryID * pQueryStart, TAnimationGraphQueryID * pQueryEnd );
	virtual void ClearTrigger( EAnimationGraphTriggerUser user );
	virtual const SAnimationTarget* GetAnimationTarget();
	virtual void SetTargetPointVerifier( IAnimationGraphTargetPointVerifier * );
	virtual bool IsUpdateReallyNecessary();

	// (only used by vehicle code) (to support simultaneous layer query, IAnimationGraphExistanceQuery must implement it).
	// Forward to fullbody layer only (hardcoded)
	virtual IAnimationGraphExistanceQuery * CreateExistanceQuery();

	// simply recurse
	virtual void Reset();

	// we've been idle for a while, try to catch up and disrespect blending laws
	// simply recurse
	virtual void SetCatchupFlag();

	// (hardcoded forward to fullbody layer only) (used for exact positioning trigger and PMC::UpdateMovementState()).
	virtual Vec2 GetQueriedStateMinMaxSpeed();

	// simply recurse (hurry all layers, let them hurry independently where they can)
	virtual void Hurry();

	// simply recurse (first person skippable states are skipped independently by each layer)
	virtual void SetFirstPersonMode( bool on );

	// simply recurse (will add all layer's containers to the sizer)
	virtual void GetMemoryStatistics(ICrySizer * s);

	// the wrapper simply returns false. shouldn't be called
	virtual bool IsMixingAllowedForCurrentState() const { assert(0); return false; }

	// used by CAnimationGraphStates
	virtual bool IsSignalledInput( InputID intputId ) const;
};

