//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================


#include "stdafx.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

namespace GCSDK
{
CSchemaFull g_SchemaFull;
CSchemaFull & GSchemaFull()
{
	return g_SchemaFull;
}

//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CSchemaFull::CSchemaFull()
{
	m_pubScratchBuffer = NULL;
	m_cubScratchBuffer = 0;
	m_unCheckSum = 0;

	m_mapFTSEnabled.SetLessFunc( DefLessFunc( enum ESchemaCatalog ) );
}


//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CSchemaFull::~CSchemaFull()
{
	Uninit();
}



//-----------------------------------------------------------------------------
// Purpose: Call this after you've finished setting up the SchemaFull (either
//			by loading it or by in GenerateIntrinsic).  It calculates our checksum
//			and allocates our scratch buffer.
//-----------------------------------------------------------------------------
void CSchemaFull::FinishInit()
{
	// Calculate our checksum
	m_unCheckSum = 0;

	for ( int iSchema = 0; iSchema < m_VecSchema.Count(); iSchema++ )
		m_unCheckSum += m_VecSchema[iSchema].CalcChecksum();

	// Allocate our scratch buffer
	Assert( NULL == m_pubScratchBuffer );
	// Include some slop for field IDs and sizes in a sparse record
	// 2k is way overkill but still no big deal
	m_cubScratchBuffer = k_cubRecordMax + 2048;
	m_pubScratchBuffer = ( uint8 * ) malloc( m_cubScratchBuffer );
}

//-----------------------------------------------------------------------------
// Purpose: Call this after you've finished setting up the SchemaFull (either
//			by loading it or by in GenerateIntrinsic).  It calculates our checksum
//			and allocates our scratch buffer.
//-----------------------------------------------------------------------------

void CSchemaFull::SetITable( CSchema* pSchema, int iTable )
{
	// make sure we don't have this schema anywhere already
	for ( int iSchema = 0; iSchema < m_VecSchema.Count(); iSchema++ )
	{
		if ( pSchema != &m_VecSchema[iSchema] )
			AssertFatalMsg( m_VecSchema[iSchema].GetITable() != iTable, "Duplicate iTable in schema definition.\n" );
	}

	// set the pSchema object
	pSchema->SetITable( iTable );
}



//-----------------------------------------------------------------------------
// Purpose: Uninits the schema.  Need to call this explicitly before app shutdown
//			on static instances of this object, as the CSchema objects
//			point to memory in static memory pools which may destruct
//			before static instances of this object.
//-----------------------------------------------------------------------------
void CSchemaFull::Uninit()
{
	m_VecSchema.RemoveAll();
	if ( NULL != m_pubScratchBuffer )
	{
		free( m_pubScratchBuffer );
		m_pubScratchBuffer = NULL;
	}
}


//-----------------------------------------------------------------------------
// Purpose: Get the scratch buffer. It is large enough to handle any 
//			record, sparse or otherwise
//			
//-----------------------------------------------------------------------------
uint8* CSchemaFull::GetPubScratchBuffer( )
{
	return m_pubScratchBuffer;
}


//-----------------------------------------------------------------------------
// Purpose: This is used during the generation of our intrinsic schema.  We've
//			added a new schema to ourselves, and we need to make sure that it
//			matches the corresponding C class.
// Input:	pSchema -			Schema to check
//			cField -			Number of fields the schema should contain.
//			cubRecord -			Size of a record in the schema
//-----------------------------------------------------------------------------
void CSchemaFull::CheckSchema( CSchema *pSchema, int cField, uint32 cubRecord )
{
	// We generate our structures and our schema using macros that operate on the
	// same source.  We check a couple of things to make sure that they're properly in sync.

	// This will fail if the schema's definition specifies the wrong iTable
	if ( pSchema != &m_VecSchema[pSchema->GetITable()] )
	{
		EmitError( SPEW_SQL, "Table %s has a bad iTable\n", pSchema->GetPchName() );
	}

	// This will fail if there are missing lines in the schema definition
	if ( pSchema->GetCField() != cField )
	{
		EmitError( SPEW_SQL, "Badly formed table %s (blank line in schema def?)\n", pSchema->GetPchName() );
		AssertFatal( false );
	}

	// This is unlikely to fail.  It indicates some kind of size mismatch (maybe a packing problem?)
	if ( pSchema->CubRecordFixed() != cubRecord )
	{
		// You may hit this if END_FIELDDATA_HAS_VAR_FIELDS is not used properly
		EmitError( SPEW_SQL, "Table %s has an inconsistent size (class = %d, schema = %d)\n",
			pSchema->GetPchName(), cubRecord, pSchema->CubRecordFixed() );
		AssertFatal( false );
	}
}


//-----------------------------------------------------------------------------
// Purpose: Finds the table with a given name.
// Input:	pchName -			Name of the table to search for
// Output:	Index of the matching table ( k_iTableNil if there isn't one)
//-----------------------------------------------------------------------------
int CSchemaFull::FindITable( const char *pchName )
{
	for ( int iSchema = 0; iSchema < m_VecSchema.Count(); iSchema++ )
	{
		if ( 0 == Q_strcmp( pchName, m_VecSchema[iSchema].GetPchName() ) )
			return iSchema;
	}

	return k_iTableNil;
}


//-----------------------------------------------------------------------------
// Purpose: Finds the table with a given iTable (iSchema)
// Input:	iTable -
// Output:	NULL or a const char * to the name (for temporary use only)
//-----------------------------------------------------------------------------
const char * CSchemaFull::PchTableFromITable( int iTable )
{
	if ( iTable < 0 || iTable >= m_VecSchema.Count() )
		return NULL;
	else
		return m_VecSchema[ iTable ].GetPchName();
}



//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSchemaFull::AddFullTextCatalog( enum ESchemaCatalog eCatalog, const char *pstrCatalogName, int nFileGroup )
{
	CFTSCatalogInfo info;
	info.m_eCatalog = eCatalog;
	info.m_nFileGroup = nFileGroup;
	info.m_pstrName = strdup(pstrCatalogName);

	m_vecFTSCatalogs.AddToTail( info );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int CSchemaFull::GetFTSCatalogByName( enum ESchemaCatalog eCatalog, const char *pstrCatalogName )
{
	int nIndex = -1;
	FOR_EACH_VEC( m_vecFTSCatalogs, i )
	{
		CFTSCatalogInfo &refInfo = m_vecFTSCatalogs[ i ];
		if ( 0 == Q_stricmp( pstrCatalogName, refInfo.m_pstrName ) )
		{
			nIndex = i;
			break;
		}
	}

	return nIndex;
}


//-----------------------------------------------------------------------------
// Purpose: turn on FTS for the named schema catalog. Called by the
//			InitIntrinsic() function.
//-----------------------------------------------------------------------------
void CSchemaFull::EnableFTS( enum ESchemaCatalog eCatalog )
{
	// mark it enabled in the map
	m_mapFTSEnabled.Insert( eCatalog, true );
}


//-----------------------------------------------------------------------------
// Purpose: is FTS enabled for the supplied schema catalog?
//-----------------------------------------------------------------------------
bool CSchemaFull::GetFTSEnabled( enum ESchemaCatalog eCatalog )
{
	int iEntry = m_mapFTSEnabled.Find( eCatalog );
	if ( iEntry == m_mapFTSEnabled.InvalidIndex() )
		return false;
	return true;
}

//-----------------------------------------------------------------------------
// Purpose: Adds a schema conversion instruction (for use in converting from
//			a different SchemaFull to this one).
//-----------------------------------------------------------------------------
void CSchemaFull::AddDeleteTable( const char *pchTableName )
{
	DeleteTable_t &deleteTable = m_VecDeleteTable[m_VecDeleteTable.AddToTail()];
	Q_strncpy( deleteTable.m_rgchTableName, pchTableName, sizeof( deleteTable.m_rgchTableName ) );
}


//-----------------------------------------------------------------------------
// Purpose: Adds a schema conversion instruction (for use in converting from
//			a different SchemaFull to this one).
//-----------------------------------------------------------------------------
void CSchemaFull::AddRenameTable( const char *pchTableNameOld, const char *pchTableNameNew )
{
	RenameTable_t &renameTable = m_VecRenameTable[m_VecRenameTable.AddToTail()];
	Q_strncpy( renameTable.m_rgchTableNameOld, pchTableNameOld, sizeof( renameTable.m_rgchTableNameOld ) );
	renameTable.m_iTableDst = FindITable( pchTableNameNew );
	Assert( k_iTableNil != renameTable.m_iTableDst );
}


//-----------------------------------------------------------------------------
// Purpose: Adds a schema conversion instruction (for use in converting from
//			a different SchemaFull to this one).
//-----------------------------------------------------------------------------
void CSchemaFull::AddDeleteField( const char *pchTableName, const char *pchFieldName )
{
	int iSchema = FindITable( pchTableName );
	AssertFatal( k_iTableNil != iSchema );

	m_VecSchema[iSchema].AddDeleteField( pchFieldName );
}


//-----------------------------------------------------------------------------
// Purpose: Adds a schema conversion instruction (for use in converting from
//			a different SchemaFull to this one).
//-----------------------------------------------------------------------------
void CSchemaFull::AddRenameField( const char *pchTableName, const char *pchFieldNameOld, const char *pchFieldNameNew )
{
	int iSchema = FindITable( pchTableName );
	AssertFatal( k_iTableNil != iSchema );

	m_VecSchema[iSchema].AddRenameField( pchFieldNameOld, pchFieldNameNew );
}


//-----------------------------------------------------------------------------
// Purpose: Adds a schema conversion instruction (for use in converting from
//			a different SchemaFull to this one).
//-----------------------------------------------------------------------------
void CSchemaFull::AddAlterField( const char *pchTableName, const char *pchFieldNameOld, const char *pchFieldnameNew, PfnAlterField_t pfnAlterField )
{
	int iSchema = FindITable( pchTableName );
	AssertFatal( k_iTableNil != iSchema );

	m_VecSchema[iSchema].AddAlterField( pchFieldNameOld, pchFieldnameNew, pfnAlterField );
}


//-----------------------------------------------------------------------------
// Purpose: Add a trigger to the desired schema
//-----------------------------------------------------------------------------
void CSchemaFull::AddTrigger( ESchemaCatalog eCatalog, const char *pchTableName, const char *pchTriggerName, ETriggerType eTriggerType, const char *pchTriggerText )
{
	CTriggerInfo trigger;
	trigger.m_eTriggerType = eTriggerType;
	trigger.m_eSchemaCatalog = eCatalog;
	Q_strncpy( trigger.m_szTriggerName, pchTriggerName, Q_ARRAYSIZE( trigger.m_szTriggerName ) );
	Q_strncpy( trigger.m_szTriggerTableName, pchTableName, Q_ARRAYSIZE( trigger.m_szTriggerTableName ) );
	trigger.m_strText = pchTriggerText;

	// add it to our list
	m_VecTriggers.AddToTail( trigger );
}

		
//-----------------------------------------------------------------------------
// Purpose:	Figures out how to map a table from another SchemaFull into us.
//			First we check our conversion instructions to see if any apply,
//			and then we look for a straightforward match.
// Input:	pchTableName -		Name of the table we're trying to map
//			piTableDst -		[Return] Index of the table to map it to
// Output:	true if we know what to do with this table (if false, the conversion
//			is undefined and dangerous).
//-----------------------------------------------------------------------------
bool CSchemaFull::BCanConvertTable( const char *pchTableName, int *piTableDst )
{
	// Should this table be deleted?
	for ( int iDeleteTable = 0; iDeleteTable < m_VecDeleteTable.Count(); iDeleteTable++ )
	{
		if ( 0 == Q_strcmp( pchTableName, m_VecDeleteTable[iDeleteTable].m_rgchTableName ) )
		{
			*piTableDst = k_iTableNil;
			return true;
		}
	}

	// Should this table be renamed?
	for ( int iRenameTable = 0; iRenameTable < m_VecRenameTable.Count(); iRenameTable++ )
	{
		if ( 0 == Q_strcmp( pchTableName, m_VecRenameTable[iRenameTable].m_rgchTableNameOld ) )
		{
			*piTableDst = m_VecRenameTable[iRenameTable].m_iTableDst;
			return true;
		}
	}

	// Find out which of our tables this table maps to (if it doesn't map
	// to any of them, we don't know what to do with it).
	*piTableDst = FindITable( pchTableName );
	return ( k_iTableNil != *piTableDst );
}


//-----------------------------------------------------------------------------
// Purpose:	Gets the default SQL schema name for a catalog
//-----------------------------------------------------------------------------
const char *CSchemaFull::GetDefaultSchemaNameForCatalog( ESchemaCatalog eCatalog )
{
	// For all catalogs it's actually the same
	if ( m_strDefaultSchemaName.IsEmpty() )
	{
		m_strDefaultSchemaName.Set( CFmtStr( "App%u", GGCBase()->GetAppID() ) );
	}

	return m_strDefaultSchemaName.Get();
}


#ifdef DBGFLAG_VALIDATE
//-----------------------------------------------------------------------------
// Purpose: Run a global validation pass on all of our data structures and memory
//			allocations.
// Input:	validator -		Our global validator object
//			pchName -		Our name (typically a member var in our container)
//-----------------------------------------------------------------------------
void CSchemaFull::Validate( CValidator &validator, const char *pchName )
{
	VALIDATE_SCOPE();

	ValidateObj( m_VecSchema );
	for ( int iSchema = 0; iSchema < m_VecSchema.Count(); iSchema++ )
	{
		ValidateObj( m_VecSchema[iSchema] );
	}

	ValidateObj( m_VecDeleteTable );
	ValidateObj( m_VecRenameTable );

	ValidateObj( m_mapFTSEnabled );

	ValidateObj( m_vecFTSCatalogs );
	FOR_EACH_VEC( m_vecFTSCatalogs, i )
	{
		ValidateObj( m_vecFTSCatalogs[i] );
	}

	ValidateObj( m_VecTriggers );
	FOR_EACH_VEC( m_VecTriggers, i )
	{
		ValidateObj( m_VecTriggers[i] );
	}

	validator.ClaimMemory( m_pubScratchBuffer );
}
#endif // DBGFLAG_VALIDATE

} // namespace GCSDK