//-----------------------------------------------------------------------------------------------
// ProceduralNames.cpp
//
#include "GypsyPrecompiledHeader.hpp"

//-----------------------------------------------------------------------------------------------
#include "Engine/Core/ProceduralNames.hpp"


//-----------------------------------------------------------------------------------------------
STATIC const unsigned int ProceduralNameGenerator::SEED_NOT_PROVIDED = 0xFFFFFFFF;


//-----------------------------------------------------------------------------------------------
STATIC std::map< std::string, ProceduralNameGenerator* > ProceduralNameGenerator::s_proceduralNameGeneratorRegistry;


//-----------------------------------------------------------------------------------------------
STATIC ProceduralNameGenerator& ProceduralNameGenerator::GetProceduralNameGeneratorForType( const char* nameTypeDefinitionName )
{
	std::map< std::string, ProceduralNameGenerator* >::iterator found = s_proceduralNameGeneratorRegistry.find( nameTypeDefinitionName );
	if( found == s_proceduralNameGeneratorRegistry.end() )
	{
		if( s_proceduralNameGeneratorRegistry.empty() )
		{
			std::string errorMessage;
			errorMessage += Stringf( "Unable to get procedural name generator for type \"%s\";\n", nameTypeDefinitionName );
			errorMessage += Stringf( "There are currently no name generator types defined.\n" );
			errorMessage += Stringf( "You must create one (from code or data) before using it.\n" );
			FATAL_ERROR( errorMessage );
		}
		else
		{
			std::string errorMessage;
			errorMessage += Stringf( "Unknown procedural name generator type '%s'.\n\n", nameTypeDefinitionName );
			errorMessage += Stringf( "There are %d defined procedural name generator type(s):\n", s_proceduralNameGeneratorRegistry.size() );
			std::map< std::string, ProceduralNameGenerator* >::iterator nameGenIter = s_proceduralNameGeneratorRegistry.begin();
			for( ; nameGenIter != s_proceduralNameGeneratorRegistry.end(); ++ nameGenIter )
			{
				const std::string& nameGenTypeName = nameGenIter->first;
				errorMessage += Stringf( "  %s\n", nameGenTypeName.c_str() );
			}
			FATAL_ERROR( errorMessage );
		}
	}

	ProceduralNameGenerator* nameGenFound = found->second;
	return *nameGenFound;
}


//-----------------------------------------------------------------------------------------------
ProceduralNameGenerator::ProceduralNameGenerator( const char* nameTypeDefinitionName )
{
	std::map< std::string, ProceduralNameGenerator* >::iterator found = s_proceduralNameGeneratorRegistry.find( nameTypeDefinitionName );
	if( found != s_proceduralNameGeneratorRegistry.end() )
	{
		std::string errorMessage;
		errorMessage += Stringf( "Unable to create procedural name generator for type \"%s\";\n", nameTypeDefinitionName );
		errorMessage += Stringf( "A definition for that name type already exists!\n" );
		FATAL_ERROR( errorMessage );
	}
	
	s_proceduralNameGeneratorRegistry[ nameTypeDefinitionName ] = this;
}


//-----------------------------------------------------------------------------------------------
// Treats Y and null-terminator (everything except AEIOU) as non-vowels.
//
bool ProceduralNameGenerator::IsAlwaysAVowel( char testChar )
{
	switch( testChar )
	{
		case 'A':
		case 'E':
		case 'I':
		case 'O':
		case 'U':
		case 'a':
		case 'e':
		case 'i':
		case 'o':
		case 'u':
			return true;
		default:
			return false;
	}
}


//-----------------------------------------------------------------------------------------------
const std::string& ProceduralNameGenerator::GetRandomStringInList( const std::vector< StringWithQuantity >& listOfWeightedStrings, int totalOfWeights )
{
	std::vector< StringWithQuantity >::const_iterator listIter;

	if( totalOfWeights < 0 )
	{
		totalOfWeights = 0;
		for( listIter = listOfWeightedStrings.begin(); listIter != listOfWeightedStrings.end(); ++listIter )
		{
			const StringWithQuantity& listEntry = *listIter;
			totalOfWeights += listEntry.m_quantity;
		}
	}

	int randomCountBurnDown = GetRandomIntLessThan( totalOfWeights );
	for( listIter = listOfWeightedStrings.begin(); listIter != listOfWeightedStrings.end(); ++listIter )
	{
		const StringWithQuantity& listEntry = *listIter;
		if( randomCountBurnDown < listEntry.m_quantity )
			return listEntry.m_string;

		randomCountBurnDown -= listEntry.m_quantity;
	}

	FATAL_ERROR( "ProceduralNameGenerator::GetRandomStringInList() - failed to find a match with weight-count burn down!" );
}


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


//-----------------------------------------------------------------------------------------------
ProceduralNameGenerator_ClustersOnly::ProceduralNameGenerator_ClustersOnly( const char* nameTypeDefinitionName, const std::vector< std::string >& listOfExampleNames, bool allowFullMatches, bool allowWordMatches )
	: ProceduralNameGenerator( nameTypeDefinitionName )
	, m_totalWordShapesCount( 0 )
	, m_allowFullMatches( allowFullMatches )
	, m_allowWordMatches( allowWordMatches )
{
	for( int wordElementType = 0; wordElementType < NUM_WORD_ELEMENTS; ++wordElementType )
	{
		m_totalClusterCountForElementType[ wordElementType ] = 0;
	}

	FATAL_GUARANTEE( !listOfExampleNames.empty(), Stringf( "New procedural name generator \"%s\" was requested, but no example names were provided!", nameTypeDefinitionName ) );

	m_seedNames = listOfExampleNames;
	ProcessSeedNames();
}


//-----------------------------------------------------------------------------------------------
VIRTUAL void ProceduralNameGenerator_ClustersOnly::ProcessSeedNames()
{
	std::vector< std::string >::const_iterator seedNameIter;
	for( seedNameIter = m_seedNames.begin(); seedNameIter != m_seedNames.end(); ++seedNameIter )
	{
		const std::string& seedName = *seedNameIter;
		ProcessSeedName( seedName );
	}
}


//-----------------------------------------------------------------------------------------------
void ProceduralNameGenerator_ClustersOnly::ProcessSeedName( const std::string& seedName )
{
	if( seedName.empty() )
		return;

	const char* name = seedName.c_str();
	const char* scan = name;

	std::string wordShape;

	// First letter cluster (either consonant-cluster or vowel-cluster)
	std::string currentCluster;
	bool isCurrentClusterAVowel = false;
/*
	if( *scan == 'y' || *scan == 'Y' )
	{
		isCurrentClusterAVowel = !IsAlwaysAVowel( *(scan+1) ); // Y at start is a vowel if followed by consonant ("Year"), or consonant if followed by vowel ("Yvette").
		RegisterSeedClusterForWordElementType( *scan,  )
	}
	else */if( IsAlwaysAVowel( *scan ) )
	{
		isCurrentClusterAVowel = true;
		while( IsAlwaysAVowel( *scan ) )
		{
			currentCluster.push_back( *scan );
			++scan;
		}
		RegisterSeedClusterForWordElementType( currentCluster, WORD_ELEMENT_STARTING_VOWEL );
		wordShape.push_back( 'V' );
	}
	else
	{
		isCurrentClusterAVowel = false;
		while( *scan != '\0' && !IsAlwaysAVowel( *scan ) )
		{
			currentCluster.push_back( *scan );
			++scan;
		}
		RegisterSeedClusterForWordElementType( currentCluster, WORD_ELEMENT_STARTING_CONSONANT );
		wordShape.push_back( 'C' );
	}

	// Middle and/or end cluster(s)
	while( *scan != '\0' )
	{
		currentCluster.clear();
		if( IsAlwaysAVowel( *scan ) )
		{
			isCurrentClusterAVowel = true;
			while( IsAlwaysAVowel( *scan ) )
			{
				currentCluster.push_back( *scan );
				++scan;
			}

			if( *scan == '\0' )
			{
				RegisterSeedClusterForWordElementType( currentCluster, WORD_ELEMENT_ENDING_VOWEL );
				wordShape.push_back( 'e' );
				break;
			}
			else
			{
				RegisterSeedClusterForWordElementType( currentCluster, WORD_ELEMENT_MIDDLE_VOWEL );
				wordShape.push_back( 'v' );
			}
		}
// 		else if( *scan == 'y' || *scan == 'Y' )
// 		{
// 
// 		}
		else
		{
			isCurrentClusterAVowel = false;
			while( *scan != '\0' && !IsAlwaysAVowel( *scan ) )
			{
				currentCluster.push_back( *scan );
				++scan;
			}

			if( *scan == '\0' )
			{
				RegisterSeedClusterForWordElementType( currentCluster, WORD_ELEMENT_ENDING_CONSONANT );
				wordShape.push_back( 'f' );
				break;
			}
			else
			{
				RegisterSeedClusterForWordElementType( currentCluster, WORD_ELEMENT_MIDDLE_CONSONANT );
				wordShape.push_back( 'c' );
			}
		}
	}

	RegisterWordShape( wordShape );
}


//-----------------------------------------------------------------------------------------------
void ProceduralNameGenerator_ClustersOnly::RegisterSeedClusterForWordElementType( const std::string& seedCluster, WordElementType wordElementType )
{
	++m_totalClusterCountForElementType[ wordElementType ];
	std::vector< StringWithQuantity >::iterator clusterIter;
	std::vector< StringWithQuantity >& listOfClustersForElement = m_letterClustersForElementType[ wordElementType ];
	for( clusterIter = listOfClustersForElement.begin(); clusterIter != listOfClustersForElement.end(); ++clusterIter )
	{
		StringWithQuantity& cluster = *clusterIter;
		if( cluster.m_string == seedCluster )
		{
			++cluster.m_quantity;
			return;
		}
	}

	StringWithQuantity newCluster;
	newCluster.m_quantity = 1;
	newCluster.m_string = seedCluster;
	listOfClustersForElement.push_back( newCluster );
}


//-----------------------------------------------------------------------------------------------
void ProceduralNameGenerator_ClustersOnly::RegisterWordShape( const std::string& wordShape )
{
	++m_totalWordShapesCount;
	std::vector< StringWithQuantity >::iterator wordShapeIter;
	for( wordShapeIter = m_wordShapes.begin(); wordShapeIter != m_wordShapes.end(); ++wordShapeIter )
	{
		StringWithQuantity& wordShapeEntry = *wordShapeIter;
		if( wordShapeEntry.m_string == wordShape )
		{
			++wordShapeEntry.m_quantity;
			return;
		}
	}

	StringWithQuantity newWordShape;
	newWordShape.m_quantity = 1;
	newWordShape.m_string = wordShape;
	m_wordShapes.push_back( newWordShape );
}


//-----------------------------------------------------------------------------------------------
VIRTUAL void ProceduralNameGenerator_ClustersOnly::GenerateProceduralName( std::string& out_generatedName, unsigned int randomSeed )
{
	UNUSED( randomSeed );
	out_generatedName.clear();

	// Pick a random word shape
	const std::string& wordShape = GetRandomStringInList( m_wordShapes, m_totalWordShapesCount );

	// For each letter-cluster type in the word, pick a letter cluster of the appropriate word element type and append it
	for( unsigned int wordShapeIndex = 0; wordShapeIndex < wordShape.size(); ++ wordShapeIndex )
	{
		char wordShapeCharacter = wordShape[ wordShapeIndex ];
		WordElementType wordElementType = GetWordElementTypeForWordShapeCharacter( wordShapeCharacter );
		const std::string& cluster = GetRandomStringInList( m_letterClustersForElementType[ wordElementType ], m_totalClusterCountForElementType[ wordElementType ] );
		out_generatedName += cluster;
	}
}


//-----------------------------------------------------------------------------------------------
ProceduralNameGenerator_ClustersOnly::WordElementType ProceduralNameGenerator_ClustersOnly::GetWordElementTypeForWordShapeCharacter( char wordShapeCharacter )
{
	switch( wordShapeCharacter )
	{
	case 'C':	return WORD_ELEMENT_STARTING_CONSONANT;
	case 'V':	return WORD_ELEMENT_STARTING_VOWEL;
	case 'c':	return WORD_ELEMENT_MIDDLE_CONSONANT;
	case 'v':	return WORD_ELEMENT_MIDDLE_VOWEL;
	case 'f':	return WORD_ELEMENT_ENDING_CONSONANT;
	case 'e':	return WORD_ELEMENT_ENDING_VOWEL;
	}

	FATAL_ERROR( Stringf( "ProceduralNameGenerator::GetWordElementTypeForWordShapeCharacter() - unknown word shape character '%c'\n", wordShapeCharacter ) );
}


