//////////////////////////////////////////////////////////////////////////////
//
// Asura_Blueprint_System.h
//
// Created by NJD - 06 September 2006
//
// Copyright (C) 2006 Rebellion, All Rights Reserved.
//
//////////////////////////////////////////////////////////////////////////////

// B E G I N   F I L E   W R A P P E R ///////////////////////////////////////

#ifdef ASURA_USE_PRAGMA_ONCE
#pragma once
#endif

#ifndef __ASURA_BLUEPRINT_SYSTEM_H__
#define __ASURA_BLUEPRINT_SYSTEM_H__ 1

// E N D   F I L E   W R A P P E R ///////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

// asura
#include "Blueprint/Asura_Blueprint.h"
#include "Core/Asura_HashTree.h"
#include "Core/Asura_PlatformBasics.h"

#ifdef ASURA_TOOLS
// tools
#include "Core/Asura_List.h"
#include "Misc/Tools_Modal_Overlay.h"
#endif

// F O R W A R D   D E C L A R A T I O N S ///////////////////////////////////

class Asura_Chunk_Stream;

//////////////////////////////////////////////////////////////////////////////
//
// Asura_Blueprint_Template
//
//////////////////////////////////////////////////////////////////////////////

#ifdef ASURA_TOOLS
class Asura_Blueprint_Template
{
	friend class Asura_Blueprint_System;
	friend class Tools_Blueprint_Edit_Object;

public:
	Asura_Blueprint_Template() {}
	~Asura_Blueprint_Template()
	{
		for ( int i = 0; i < m_xList.GetNumEntries(); ++i )
		{
			Asura_String* pxStringPtr = m_xList.GetEntryByIndex( i );
			delete pxStringPtr;
		}
		m_xList.ClearList();
	}

    void SetMin(float fMin)         { m_fMin     = fMin; }
    void SetMax(float fMax)         { m_fMax     = fMax; }
    void SetInitial(float fInitial) { m_fInitial = fInitial; }
    void SetStep(float fStep)       { m_fStep    = fStep; }
    void SetMin(int iMin)           { m_iMin     = iMin; }
    void SetMax(int iMax)           { m_iMax     = iMax; }
    void SetInitial(int iInitial)   { m_iInitial = iInitial; }
    void SetStep(int iStep)         { m_iStep    = iStep; }

    int   GetMinInt()       const { return m_iMin; }
    int   GetMaxInt()       const { return m_iMax; }
    int   GetInitialInt()   const { return m_iInitial; }
    int   GetStepInt()      const { return m_iStep; }
    float GetMinFloat()     const { return m_fMin; }
    float GetMaxFloat()     const { return m_fMax; }
    float GetInitialFloat() const { return m_fInitial; }
    float GetStepFloat()    const { return m_fStep; }

	void SetString(const char* pszName)
	{
		Asura_String* pxString = new Asura_String(pszName);
		m_xList.AddToList(pxString);
	};
	const char* GetString(int iIndex) const { return m_xList.GetEntryByIndex(iIndex)->GetString(); }

	int GetStringCount() const { return m_xList.GetNumEntries(); }
	int GetStrings( ListSelection_Overlay& xOverlay ) const
	{
		int iCount = GetStringCount();
		for(int i = 0; i < iCount; i++)
		{
			xOverlay.AddItem(GetString(i));
		}
		return iCount;
	}
	void ClearStrings() { m_xList.ClearList(); }

	const char* GetName() const { return m_xName.GetString(); }
	void SetName(const char* pszName) { m_xName = pszName; }

	Asura_Value::VALUE_TYPE GetType() const { return m_eType; }
	void SetType( Asura_Value::VALUE_TYPE eType ) { m_eType = eType; }
	
	Asura_Hash_ID GetHashID() const { return m_uHash; }

protected:
	Asura_String m_xName;
	Asura_Hash_ID m_uHash;
	Asura_Value::VALUE_TYPE m_eType;
	Asura_Blueprint_Template* m_pNext;

	union
	{
		float m_fMax;
		int m_iMax;
	};
	union
	{
		float m_fMin;
		int m_iMin;
	};
	union
	{
		float m_fStep;
		int m_iStep;
	};
	union
	{
		float m_fInitial;
		int m_iInitial;
	};

	Asura_List<Asura_String> m_xList;
};
#endif

//////////////////////////////////////////////////////////////////////////////
//
// Asura_BlueprintType
//
//////////////////////////////////////////////////////////////////////////////

struct Asura_BlueprintType
{
	Asura_TemplateHashTree< Asura_Blueprint >	m_xBlueprintHashTree;

#ifdef ASURA_TOOLS
	Asura_String            					m_xTypeName;
	Asura_String            					m_xFileName;
#endif

	Asura_BlueprintType();
};

//////////////////////////////////////////////////////////////////////////////
//
// Asura_Blueprint_System
//
//////////////////////////////////////////////////////////////////////////////

class Asura_Blueprint_System
{
	friend class Asura_Blueprint_Type_Iterator;
	friend class Asura_Blueprint_Type_ConstIterator;
	friend class Asura_Blueprint_Iterator;
	friend class Asura_Blueprint_ConstIterator;
	friend class Tools_Blueprint_Edit_Object;
    friend class Tools_Mode_Blueprint;

public:
    static void Initialise();
    static void Shutdown();
	static void Reset();

    static Asura_Blueprint* GetBlueprintByHashID( const Asura_Hash_ID uTypeHashID, const Asura_Hash_ID uBlueprintHashID );

    static int              GetNumTypes();
    static int              GetNumBlueprintsInType( const Asura_Hash_ID uTypeHashID );
    // Gets the number of blueprints without a parent in a certain type.
    static int              GetNumRootBlueprintsInType( const Asura_Hash_ID uTypeHashID );
    static Asura_Blueprint* GetRootBlueprint( const Asura_Hash_ID uTypeHashID, const int iIndex );
    // If bOnlyDirectChildren is true then only blueprints with this parent as their direct parent will be counted.
    static int              GetNumChildBlueprints( const Asura_Hash_ID uTypeHashID, const Asura_Hash_ID uParentBlueprintHashID,
                                                   const bool bOnlyDirectChildren );
    static Asura_Blueprint* GetChildBlueprint( const Asura_Hash_ID uTypeHashID, const Asura_Hash_ID uParentBlueprintHashID,
                                               const bool bOnlyDirectChildren, const int iIndex );

    static void EstablishAllLinkage( const Asura_Hash_ID uTypeHashID );

#ifdef ASURA_TOOLS
    static void AddBlueprintType( const Asura_String& xTypeName );
    static void DeleteBlueprintType( const Asura_String& xTypeName );
	static bool RenameBlueprintType( const Asura_String& xOldName, const Asura_String& xNewName );
	static bool SetBlueprintTypeFileName( const Asura_Hash_ID uTypeHashID, const Asura_String& xTypeFileName );

    static void AddBlueprint( const Asura_Hash_ID uTypeHashID, const Asura_String& xBlueprintName, const Asura_Hash_ID uParentHashID = ASURA_HASH_ID_UNSET, const bool bEstablishLinkage = true );
    static void MarkBlueprintForDeletion( const Asura_Hash_ID uTypeHashID, const Asura_String& xBlueprintName );
    static void DeleteAllMarkedBlueprints( const Asura_Hash_ID uTypeHashID );

	static void StartExport();
	static void EndExport();

    static void WriteToChunkStream( Asura_Chunk_Stream& xStream, const Asura_Hash_ID uBlueprintTypeHashID );

	static Asura_Blueprint_Template* RegisterTemplate( Asura_Char* pszTemplateName, Asura_Hash_ID uTemplateHash, Asura_Value::VALUE_TYPE eType );
	static const int GetTemplateList( ListSelection_Overlay& xList, Asura_Value::VALUE_TYPE eType );
	static Asura_Blueprint_Template* GetTemplate( const Asura_Hash_ID uBlueprintTemplateHashID );
	static void Project_RefreshTemplate( Asura_Blueprint_Template* pxBlueprintTemplate );
	static void Project_InitialiseTemplates();

	static const Asura_String* GetTypeName( Asura_Hash_ID uTypeHashID );
	static const Asura_String* GetTypeFileName( Asura_Hash_ID uTypeHashID );
#endif

    static void ReadFromChunkStream( Asura_Chunk_Stream& xStream );

private:
	static Asura_BlueprintType* GetType( const Asura_Hash_ID uTypeHashID );

    static Asura_TemplateHashTree< Asura_BlueprintType > s_xBlueprintTypeContainer;
#ifdef ASURA_TOOLS
    static Asura_Blueprint_Template* s_pxTemplates;
#endif

	static const u_int uCURRENT_CHUNK_VERSION = 1;
};

////////////////////////////////////////////////////////////////////////////////
//
// Blueprint type iterators.
//
////////////////////////////////////////////////////////////////////////////////

class Asura_Blueprint_Type_Iterator
{
public:
	Asura_Blueprint_Type_Iterator()
	: m_xHashTreeIter( Asura_Blueprint_System::s_xBlueprintTypeContainer )
	{
		Restart();
	}

	void Restart() { m_xHashTreeIter.Restart(); }
	Asura_BlueprintType* GetCurrent() const { return m_xHashTreeIter.GetCurrent(); }
	bool Done() const { return m_xHashTreeIter.Done(); }
	void Next() { m_xHashTreeIter.Next(); }

	Asura_Hash_ID GetCurrentHash() const { return m_xHashTreeIter.GetCurrentHash(); }

private:
	Asura_TemplateHashTree_Iterator< Asura_BlueprintType > m_xHashTreeIter;
};

class Asura_Blueprint_Type_ConstIterator
{
public:
	Asura_Blueprint_Type_ConstIterator()
	: m_xHashTreeIter( Asura_Blueprint_System::s_xBlueprintTypeContainer )
	{
		Restart();
	}

	void Restart() { m_xHashTreeIter.Restart(); }
	const Asura_BlueprintType* GetCurrent() const { return m_xHashTreeIter.GetCurrent(); }
	bool Done() const { return m_xHashTreeIter.Done(); }
	void Next() { m_xHashTreeIter.Next(); }

	Asura_Hash_ID GetCurrentHash() const { return m_xHashTreeIter.GetCurrentHash(); }

private:
	Asura_TemplateHashTree_ConstIterator< Asura_BlueprintType > m_xHashTreeIter;
};

//////////////////////////////////////////////////////////////////////////////
//
// Blueprint iterators.
//
//////////////////////////////////////////////////////////////////////////////

class Asura_Blueprint_Iterator
{
public:
	Asura_Blueprint_Iterator( const Asura_Hash_ID uTypeHash = ASURA_HASH_ID_UNSET )
	: m_uBlueprintTypeHash( uTypeHash )
	, m_uCurrentBlueprintTypeHash( ASURA_HASH_ID_UNSET )
	, m_pxCurrentBlueprintType( NULL )
	, m_pxBPTypeIter( NULL )
	, m_pxBPIter( NULL )
	{
		Restart();
	}
	~Asura_Blueprint_Iterator()
	{
		delete m_pxBPTypeIter;
		delete m_pxBPIter;
	}

	void Restart()
	{
		// Initialise the appropriate members...
		delete(m_pxBPTypeIter);
		if(m_uBlueprintTypeHash == ASURA_HASH_ID_UNSET)
		{
			m_pxBPTypeIter				= new Asura_Blueprint_Type_Iterator();
			m_uCurrentBlueprintTypeHash	= m_pxBPTypeIter->GetCurrentHash();
			m_pxCurrentBlueprintType	= m_pxBPTypeIter->GetCurrent();
		}
		else
		{
			m_pxBPTypeIter				= NULL;
			m_uCurrentBlueprintTypeHash	= m_uBlueprintTypeHash;
			m_pxCurrentBlueprintType	= Asura_Blueprint_System::GetType(m_uBlueprintTypeHash);
		}

		if ( m_pxCurrentBlueprintType )
		{
			delete m_pxBPIter;
			m_pxBPIter = new Asura_TemplateHashTree_Iterator< Asura_Blueprint >( m_pxCurrentBlueprintType->m_xBlueprintHashTree );
		}
	}

	Asura_Blueprint* GetCurrent() const
	{
		return m_pxBPIter ? m_pxBPIter->GetCurrent() : NULL;
	}

	bool Done() const
	{
		return m_pxBPIter ? m_pxBPIter->Done() : true;
	}

	void Next()
	{
		if ( !m_pxBPIter ) { return; }

		m_pxBPIter->Next();
		while ( m_pxBPIter && m_pxBPIter->Done() )
		{
			delete m_pxBPIter;
			m_pxBPIter = NULL;

			if ( m_pxBPTypeIter == NULL )
			{
				m_uCurrentBlueprintTypeHash	= ASURA_HASH_ID_UNSET;
				m_pxCurrentBlueprintType	= NULL;
			}
			else
			{
				// If we weren't given a specific type to stick to then advance along the types.
				m_pxBPTypeIter->Next();
				m_uCurrentBlueprintTypeHash	= m_pxBPTypeIter->GetCurrentHash();
				m_pxCurrentBlueprintType	= m_pxBPTypeIter->GetCurrent();
				if ( m_pxCurrentBlueprintType )
				{
					m_pxBPIter = new Asura_TemplateHashTree_Iterator< Asura_Blueprint >( m_pxCurrentBlueprintType->m_xBlueprintHashTree );
				}
			}
		}
	}

	Asura_Hash_ID GetCurrentBlueprintTypeHash() const { return(m_uCurrentBlueprintTypeHash); }
	Asura_Hash_ID GetBlueprintTypeHash() const        { return(m_uBlueprintTypeHash); }

protected:
	// The hash-ID of the blueprint-type to be iterated over.
	Asura_Hash_ID										m_uBlueprintTypeHash;
	// A pointer to the blueprint-type to iterate over.
	Asura_Hash_ID										m_uCurrentBlueprintTypeHash;
	Asura_BlueprintType*								m_pxCurrentBlueprintType;
	// The current blueprint iterators.
	Asura_Blueprint_Type_Iterator*						m_pxBPTypeIter;
	Asura_TemplateHashTree_Iterator< Asura_Blueprint >* m_pxBPIter;

private:
	Asura_Blueprint_Iterator(const Asura_Blueprint_Iterator& xOther);
	Asura_Blueprint_Iterator& operator=(const Asura_Blueprint_Iterator& xOther);
};

class Asura_Blueprint_ConstIterator
{
public:
	Asura_Blueprint_ConstIterator( const Asura_Hash_ID uTypeHash = ASURA_HASH_ID_UNSET )
	: m_uBlueprintTypeHash( uTypeHash )
	, m_uCurrentBlueprintTypeHash( ASURA_HASH_ID_UNSET )
	, m_pxCurrentBlueprintType( NULL )
	, m_pxBPTypeIter( NULL )
	, m_pxBPIter( NULL )
	{
		Restart();
	}
	~Asura_Blueprint_ConstIterator()
	{
		delete(m_pxBPTypeIter);
		delete m_pxBPIter;
	}

	void Restart()
	{
		// Initialise the appropriate members...
		delete(m_pxBPTypeIter);
		if(m_uBlueprintTypeHash == ASURA_HASH_ID_UNSET)
		{
			m_pxBPTypeIter				= new Asura_Blueprint_Type_ConstIterator();
			m_uCurrentBlueprintTypeHash	= m_pxBPTypeIter->GetCurrentHash();
			m_pxCurrentBlueprintType	= m_pxBPTypeIter->GetCurrent();
		}
		else
		{
			m_pxBPTypeIter				= NULL;
			m_uCurrentBlueprintTypeHash	= m_uBlueprintTypeHash;
			m_pxCurrentBlueprintType	= Asura_Blueprint_System::GetType(m_uBlueprintTypeHash);
		}

		if ( m_pxCurrentBlueprintType )
		{
			delete m_pxBPIter;
			m_pxBPIter = new Asura_TemplateHashTree_ConstIterator< Asura_Blueprint >( m_pxCurrentBlueprintType->m_xBlueprintHashTree );
		}
	}

	const Asura_Blueprint* GetCurrent() const
	{
		return m_pxBPIter ? m_pxBPIter->GetCurrent() : NULL;
	}

	bool Done() const
	{
		return m_pxBPIter ? m_pxBPIter->Done() : true;
	}

	void Next()
	{
		if ( !m_pxBPIter ) { return; }

		m_pxBPIter->Next();
		while ( m_pxBPIter && m_pxBPIter->Done() )
		{
			delete m_pxBPIter;
			m_pxBPIter = NULL;

			if( m_pxBPTypeIter == NULL )
			{
				m_uCurrentBlueprintTypeHash	= ASURA_HASH_ID_UNSET;
				m_pxCurrentBlueprintType	= NULL;
			}
			else
			{
				// If we weren't given a specific type to stick to then advance along the types.
				m_pxBPTypeIter->Next();
				m_uCurrentBlueprintTypeHash	= m_pxBPTypeIter->GetCurrentHash();
				m_pxCurrentBlueprintType	= m_pxBPTypeIter->GetCurrent();
				if ( m_pxCurrentBlueprintType )
				{
					m_pxBPIter = new Asura_TemplateHashTree_ConstIterator< Asura_Blueprint >( m_pxCurrentBlueprintType->m_xBlueprintHashTree );
				}
			}
		}
	}

	Asura_Hash_ID GetCurrentBlueprintTypeHash() const { return(m_uCurrentBlueprintTypeHash); }
	Asura_Hash_ID GetBlueprintTypeHash() const        { return(m_uBlueprintTypeHash); }

protected:
	// The hash-ID of the blueprint-type to be iterated over.
	Asura_Hash_ID											 m_uBlueprintTypeHash;
	// A pointer to the blueprint-type to iterate over.
	Asura_Hash_ID											m_uCurrentBlueprintTypeHash;
	const Asura_BlueprintType*								m_pxCurrentBlueprintType;
	// The current blueprint iterator.
	Asura_Blueprint_Type_ConstIterator*						 m_pxBPTypeIter;
	Asura_TemplateHashTree_ConstIterator< Asura_Blueprint >* m_pxBPIter;

private:
	Asura_Blueprint_ConstIterator(const Asura_Blueprint_ConstIterator& xOther);
	Asura_Blueprint_ConstIterator& operator=(const Asura_Blueprint_ConstIterator& xOther);
};

////////////////////////////////////////////////////////////////////////////////
//
// Blueprint parameter iterators.
//
// Asura_Blueprint_Parameter_Iterator
// Asura_Blueprint_Parameter_ConstIterator
// are designed to iterate over all parameters in whatever blueprints you specify.
//
// Asura_Blueprint_UniqueParam_Iterator
// Asura_Blueprint_UniqueParam_ConstIterator
// are designed to iterate over only unique parameters that a particular blueprint can access
// (unique parameters that it or any of its parents contain).
//
////////////////////////////////////////////////////////////////////////////////

class Asura_Blueprint_Parameter_Iterator
{
public:
	Asura_Blueprint_Parameter_Iterator(const Asura_Hash_ID uTypeHash = ASURA_HASH_ID_UNSET,
									   const Asura_Hash_ID uBlueprintHash = ASURA_HASH_ID_UNSET)
	: m_xBlueprintIterator(uTypeHash)
	, m_uBlueprintHash(uBlueprintHash)
	{
		Restart();
	}

	void Restart()
	{
		m_xBlueprintIterator.Restart();

		if ( m_uBlueprintHash == ASURA_HASH_ID_UNSET )
		{
			m_pxCurrentBlueprint = m_xBlueprintIterator.GetCurrent();
		}
		else
		{
			const Asura_Hash_ID	uTypeHash = m_xBlueprintIterator.GetBlueprintTypeHash();
			m_pxCurrentBlueprint = Asura_Blueprint_System::GetBlueprintByHashID( uTypeHash, m_uBlueprintHash );
		}

		m_iCurrentParameterIndex = -1;
		Next();
	}

	const Asura_Parameter* GetCurrent() const
	{
		// Do we have data to fetch - if so, then fetch it...
		return ( m_pxCurrentBlueprint ? m_pxCurrentBlueprint->GetParameterByIndex( m_iCurrentParameterIndex ) : NULL );
	}

	bool Done() const
	{
		// Do we have any blueprint data to iterate through?
		return ( m_pxCurrentBlueprint == NULL );
	}

	void Next()
	{
		// Move on to the next parameter in a subsequent blueprint if necessary.
		while ( m_pxCurrentBlueprint && ++m_iCurrentParameterIndex >= m_pxCurrentBlueprint->GetNumParameters() )
		{
			if ( m_uBlueprintHash == ASURA_HASH_ID_UNSET )
			{
				// We've done with the current blueprint, continue with the next one...
				m_xBlueprintIterator.Next();
				m_pxCurrentBlueprint = m_xBlueprintIterator.GetCurrent();
			}
			else
			{
				// We were only iterating through one blueprint - we're done...
				m_pxCurrentBlueprint = NULL;
			}

			m_iCurrentParameterIndex = -1;
		}
	}

	Asura_Blueprint*		GetCurrentBlueprint() const			{ return(m_pxCurrentBlueprint); }
	Asura_Hash_ID			GetCurrentBlueprintHash() const		{ return((m_pxCurrentBlueprint == NULL) ? ASURA_HASH_ID_UNSET : m_pxCurrentBlueprint->GetHashID()); }
	Asura_Hash_ID			GetCurrentBlueprintTypeHash() const	{ return(m_xBlueprintIterator.GetCurrentBlueprintTypeHash()); }

	Asura_Hash_ID			GetBlueprintTypeHash() const		{ return(m_xBlueprintIterator.GetBlueprintTypeHash()); }
	Asura_Hash_ID			GetBlueprintHash() const			{ return(m_uBlueprintHash); }

protected:
	Asura_Blueprint_Iterator		m_xBlueprintIterator;		// The iterator to be used for the blueprints themselves.
	Asura_Hash_ID					m_uBlueprintHash;			// The hash-ID of the blueprint to be iterated over.
	Asura_Blueprint*				m_pxCurrentBlueprint;		// A pointer to the blueprint to iterate over.
	int								m_iCurrentParameterIndex;	// The index of the parameter within the current blueprint.

private:
	Asura_Blueprint_Parameter_Iterator(const Asura_Blueprint_Parameter_Iterator& xOther);
	Asura_Blueprint_Parameter_Iterator& operator=(const Asura_Blueprint_Parameter_Iterator& xOther);
};

class Asura_Blueprint_Parameter_ConstIterator
{
public:
	Asura_Blueprint_Parameter_ConstIterator(const Asura_Hash_ID uTypeHash = ASURA_HASH_ID_UNSET,
											const Asura_Hash_ID uBlueprintHash = ASURA_HASH_ID_UNSET)
	: m_xBlueprintIterator(uTypeHash)
	, m_uBlueprintHash(uBlueprintHash)
	{
		Restart();
	}

	void Restart()
	{
		m_xBlueprintIterator.Restart();

		if ( m_uBlueprintHash == ASURA_HASH_ID_UNSET )
		{
			m_pxCurrentBlueprint = m_xBlueprintIterator.GetCurrent();
		}
		else
		{
			const Asura_Hash_ID	uTypeHash = m_xBlueprintIterator.GetBlueprintTypeHash();
			m_pxCurrentBlueprint = Asura_Blueprint_System::GetBlueprintByHashID( uTypeHash, m_uBlueprintHash );
		}

		m_iCurrentParameterIndex = -1;
		Next();
	}

	const Asura_Parameter* GetCurrent() const
	{
		// Do we have data to fetch - if so, then fetch it...
		return ( m_pxCurrentBlueprint ? m_pxCurrentBlueprint->GetParameterByIndex( m_iCurrentParameterIndex ) : NULL );
	}

	bool Done() const
	{
		// Do we have any blueprint data to iterate through?
		return ( m_pxCurrentBlueprint == NULL );
	}

	void Next()
	{
		// Move on to the next parameter in a subsequent blueprint if necessary.
		while ( m_pxCurrentBlueprint && ++m_iCurrentParameterIndex >= m_pxCurrentBlueprint->GetNumParameters() )
		{
			if ( m_uBlueprintHash == ASURA_HASH_ID_UNSET )
			{
				// We've done with the current blueprint, continue with the next one...
				m_xBlueprintIterator.Next();
				m_pxCurrentBlueprint = m_xBlueprintIterator.GetCurrent();
			}
			else
			{
				// We were only iterating through one blueprint - we're done...
				m_pxCurrentBlueprint = NULL;
			}

			m_iCurrentParameterIndex = -1;
		}
	}

	const Asura_Blueprint*	GetCurrentBlueprint() const			{ return(m_pxCurrentBlueprint); }
	Asura_Hash_ID			GetCurrentBlueprintHash() const		{ return((m_pxCurrentBlueprint == NULL) ? ASURA_HASH_ID_UNSET : m_pxCurrentBlueprint->GetHashID()); }
	Asura_Hash_ID			GetCurrentBlueprintTypeHash() const	{ return(m_xBlueprintIterator.GetCurrentBlueprintTypeHash()); }

	Asura_Hash_ID			GetBlueprintTypeHash() const		{ return(m_xBlueprintIterator.GetBlueprintTypeHash()); }
	Asura_Hash_ID			GetBlueprintHash() const			{ return(m_uBlueprintHash); }

protected:
	Asura_Blueprint_ConstIterator	m_xBlueprintIterator;		// The iterator to be used for the blueprints themselves.
	Asura_Hash_ID					m_uBlueprintHash;			// The hash-ID of the blueprint to be iterated over.
	const Asura_Blueprint*			m_pxCurrentBlueprint;		// A pointer to the blueprint to iterate over.
	int								m_iCurrentParameterIndex;	// The index of the parameter within the current blueprint.

private:
	Asura_Blueprint_Parameter_ConstIterator(const Asura_Blueprint_Parameter_ConstIterator& xOther);
	Asura_Blueprint_Parameter_ConstIterator& operator=(const Asura_Blueprint_Parameter_ConstIterator& xOther);
};

class Asura_Blueprint_UniqueParam_Iterator
{
public:
	Asura_Blueprint_UniqueParam_Iterator( const Asura_Hash_ID uTypeHash, const Asura_Hash_ID uBlueprintHash )
	: m_pxStartBlueprint( Asura_Blueprint_System::GetBlueprintByHashID( uTypeHash, uBlueprintHash ) )
	{
		Restart();
	}
	Asura_Blueprint_UniqueParam_Iterator( Asura_Blueprint* pxBlueprint )
	: m_pxStartBlueprint( pxBlueprint )
	{
		Restart();
	}

	void Restart()
	{
        m_pxCurrentBlueprint     = m_pxStartBlueprint;
		m_iCurrentParameterIndex = -1;
        m_uCurrentParamHashID    = ASURA_HASH_ID_UNSET;

		Next();
	}

	Asura_Parameter* GetCurrent() const
	{
		// We want to return the lowest instance in the sub-tree of a parameter with this hash.
		if ( m_uCurrentParamHashID != ASURA_HASH_ID_UNSET )
		{
			const Asura_Blueprint* pxBlueprint = m_pxStartBlueprint;
			while ( pxBlueprint )
			{
				for ( int iParam = 0; iParam < pxBlueprint->GetNumParameters(); ++iParam )
				{
					Asura_Parameter* pxParam = pxBlueprint->GetParameterByIndex( iParam );
					if ( pxParam->GetHashID() == m_uCurrentParamHashID )
					{
						return pxParam;
					}
				}
				pxBlueprint = pxBlueprint->GetParent();
			}
		}

		return NULL;
	}

	Asura_Hash_ID GetCurrentParameterHash() const
	{
		return m_uCurrentParamHashID;
	}

	bool GetCurrentParameterValue( Asura_Value& xValue ) const
	{
		if ( m_pxStartBlueprint && m_uCurrentParamHashID != ASURA_HASH_ID_UNSET )
		{
			return m_pxStartBlueprint->GetParameterValueByHashID( m_uCurrentParamHashID, xValue );
		}
		else
		{
			return false;
		}
	}

	Asura_Blueprint* GetCurrentBlueprint() const { return m_pxCurrentBlueprint; }

	Asura_Hash_ID GetCurrentBlueprintHash() const
	{
		return ( ( m_pxCurrentBlueprint == NULL ) ? ASURA_HASH_ID_UNSET : m_pxCurrentBlueprint->GetHashID() );
	}

	bool Done() const
	{
		return ( m_pxStartBlueprint == NULL || m_pxCurrentBlueprint == NULL );
	}

	void Next()
	{
		// Move on to the next unique parameter in a subsequent blueprint if necessary.
		bool bDone = false;
		while ( m_pxCurrentBlueprint && !bDone )
		{
			++m_iCurrentParameterIndex;

			if ( m_iCurrentParameterIndex >= m_pxCurrentBlueprint->GetNumParameters() )
			{
                m_pxCurrentBlueprint     = m_pxCurrentBlueprint->GetParent();
				m_iCurrentParameterIndex = -1;
				m_uCurrentParamHashID    = ASURA_HASH_ID_UNSET;
			}
			else
			{
				const Asura_Parameter* pxParam = m_pxCurrentBlueprint->GetParameterByIndex( m_iCurrentParameterIndex );
				if ( pxParam->GetOverrideType() == ABOT_NONE )
				{
					m_uCurrentParamHashID = pxParam->GetHashID();
					bDone = true;
				}
			}
		}
	}

protected:
	Asura_Blueprint* m_pxStartBlueprint;
	Asura_Blueprint* m_pxCurrentBlueprint;
	int				 m_iCurrentParameterIndex;
	Asura_Hash_ID	 m_uCurrentParamHashID;

private:
	Asura_Blueprint_UniqueParam_Iterator(const Asura_Blueprint_UniqueParam_Iterator& xOther);
	Asura_Blueprint_UniqueParam_Iterator& operator=(const Asura_Blueprint_UniqueParam_Iterator& xOther);
};

class Asura_Blueprint_UniqueParam_ConstIterator
{
public:
	Asura_Blueprint_UniqueParam_ConstIterator( const Asura_Hash_ID uTypeHash, const Asura_Hash_ID uBlueprintHash )
	: m_pxStartBlueprint( Asura_Blueprint_System::GetBlueprintByHashID( uTypeHash, uBlueprintHash ) )
	{
		Restart();
	}
	Asura_Blueprint_UniqueParam_ConstIterator( const Asura_Blueprint* pxBlueprint )
	: m_pxStartBlueprint( pxBlueprint )
	{
		Restart();
	}

	void Restart()
	{
        m_pxCurrentBlueprint     = m_pxStartBlueprint;
		m_iCurrentParameterIndex = -1;
        m_uCurrentParamHashID    = ASURA_HASH_ID_UNSET;

		Next();
	}

	const Asura_Parameter* GetCurrent() const
	{
		// We want to return the lowest instance in the sub-tree of a parameter with this hash.
		if ( m_uCurrentParamHashID != ASURA_HASH_ID_UNSET )
		{
			const Asura_Blueprint* pxBlueprint = m_pxStartBlueprint;
			while ( pxBlueprint )
			{
				for ( int iParam = 0; iParam < pxBlueprint->GetNumParameters(); ++iParam )
				{
					Asura_Parameter* pxParam = pxBlueprint->GetParameterByIndex( iParam );
					if ( pxParam->GetHashID() == m_uCurrentParamHashID )
					{
						return pxParam;
					}
				}
				pxBlueprint = pxBlueprint->GetParent();
			}
		}

		return NULL;
	}

	Asura_Hash_ID GetCurrentParameterHash() const
	{
		return m_uCurrentParamHashID;
	}

	bool GetCurrentParameterValue( Asura_Value& xValue ) const
	{
		if ( m_pxStartBlueprint && m_uCurrentParamHashID != ASURA_HASH_ID_UNSET )
		{
			return m_pxStartBlueprint->GetParameterValueByHashID( m_uCurrentParamHashID, xValue );
		}
		else
		{
			return false;
		}
	}

	const Asura_Blueprint* GetCurrentBlueprint() const { return m_pxCurrentBlueprint; }

	Asura_Hash_ID GetCurrentBlueprintHash() const
	{
		return ( ( m_pxCurrentBlueprint == NULL ) ? ASURA_HASH_ID_UNSET : m_pxCurrentBlueprint->GetHashID() );
	}

	bool Done() const
	{
		return ( m_pxStartBlueprint == NULL || m_pxCurrentBlueprint == NULL );
	}

	void Next()
	{
		// Move on to the next unique parameter in a subsequent blueprint if necessary.
		bool bDone = false;
		while ( m_pxCurrentBlueprint && !bDone )
		{
			++m_iCurrentParameterIndex;

			if ( m_iCurrentParameterIndex >= m_pxCurrentBlueprint->GetNumParameters() )
			{
                m_pxCurrentBlueprint     = m_pxCurrentBlueprint->GetParent();
				m_iCurrentParameterIndex = -1;
				m_uCurrentParamHashID    = ASURA_HASH_ID_UNSET;
			}
			else
			{
				const Asura_Parameter* pxParam = m_pxCurrentBlueprint->GetParameterByIndex( m_iCurrentParameterIndex );
				if ( pxParam->GetOverrideType() == ABOT_NONE )
				{
					m_uCurrentParamHashID = pxParam->GetHashID();
					bDone = true;
				}
			}
		}
	}

protected:
	const Asura_Blueprint* m_pxStartBlueprint;
	const Asura_Blueprint* m_pxCurrentBlueprint;
	int					   m_iCurrentParameterIndex;
	Asura_Hash_ID		   m_uCurrentParamHashID;

private:
	Asura_Blueprint_UniqueParam_ConstIterator(const Asura_Blueprint_UniqueParam_ConstIterator& xOther);
	Asura_Blueprint_UniqueParam_ConstIterator& operator=(const Asura_Blueprint_UniqueParam_ConstIterator& xOther);
};

//////////////////////////////////////////////////////////////////////////////
//
// 
//
//////////////////////////////////////////////////////////////////////////////

#endif //__ASURA_BLUEPRINT_SYSTEM_H__
