#include "gamebuilder.h"
#include <bu/sio.h>

#include "game.h"
#include "astbranch.h"
#include "astleaf.h"
#include "astleafliteral.h"
#include "astfunction.h"
#include "command.h"
#include "situation.h"
#include "parser.tab.h"

using namespace Bu;

GameBuilder::GameBuilder() :
	pGame( NULL ),
	bGlobal( false ),
	pCurNode( NULL ),
	pCurRoot( NULL ),
	pCurCmd( NULL ),
	pCurFnc( NULL ),
	pCurSit( NULL ),
	iStackHeight( 0 )
{
	pGame = new Game();
}

GameBuilder::~GameBuilder()
{
}

typedef void *yyscan_t;
void yylex_init( yyscan_t * );
void yylex_destroy( yyscan_t );
void yyparse( yyscan_t, GameBuilder &bld );
void yyset_in( FILE *, yyscan_t );

void GameBuilder::parse( const Bu::String &sFile )
{
	yyscan_t scanner;

	yylex_init( &scanner );

	FILE *in = fopen( sFile.getStr(), "rb" );
	yyset_in( in, scanner );

	yyparse( scanner, *this );
	yylex_destroy( scanner );

	fclose( in );
}

void GameBuilder::endCmpltExpr()
{
	while( iStackHeight > 0 )
	{
		addNode( AstNode::tPop );
	}
	iStackHeight = 0;
}

void GameBuilder::setLiteral( const Variable &v )
{
	vLiteral = v;
}

void GameBuilder::setGameParam( const Bu::String &sName )
{
	pGame->hGlobalParam.insert( sName, vLiteral );
}

void GameBuilder::beginFunction( const Bu::String &sName )
{
	pCurNode = pCurRoot = new AstBranch( AstNode::tScope );
	pCurFnc = new AstFunction( sName );
	//sio << "New function: " << sName << sio.nl;
}

void GameBuilder::addFunctionParam( const Bu::String &sName )
{
	pCurFnc->addParam( sName );
	//sio << " - Param added '" << sName << "'" << sio.nl;
}

void GameBuilder::endFunctionParams()
{
	Bu::StringList lRev;
	for( Bu::StringList::const_iterator i = pCurFnc->getParamList().begin();
		 i; i++ )
	{
		lRev.prepend( *i );
	}

	for( Bu::StringList::iterator i = lRev.begin(); i; i++ )
	{
		addVarRef( *i, sidLocal );
		addNode( AstNode::tStoreRev );
	}
}

void GameBuilder::endFunction()
{
	//sio << "Function ended: " << *pCurRoot << sio.nl;
	
	pCurNode->addNode( new AstLeafLiteral( Variable::tNull ) );
	pCurFnc->setAst( pCurRoot );
	pCurRoot = pCurNode = NULL;
	pGame->hFunction.insert( pCurFnc->getName(), pCurFnc );
	iStackHeight = 0;
}

void GameBuilder::beginSituation( const Bu::String &sName, Situation::InputType tInput )
{
	pCurSit = new Situation( sName, tInput );
	//sio << "New situation: " << sName << sio.nl;
}

void GameBuilder::beginSituationMode( Situation::Mode m )
{
	pCurNode = pCurRoot = new AstBranch( AstNode::tScope );
	eCurSitMode = m;
}

void GameBuilder::closeSituationMode()
{
//	sio << "Set situation <<" << pCurSit->getName() << ">> mode " << eCurSitMode << " to " << *pCurRoot << sio.nl;
	pCurSit->setAst( pCurRoot, eCurSitMode );
	pCurRoot = pCurNode = NULL;
}

void GameBuilder::endSituation()
{
	pGame->hSituation.insert( pCurSit->getName(), pCurSit );
	//sio << "Situation ended." << sio.nl;
}

void GameBuilder::addNode( AstNode::Type iType )
{
	stackMod( iType );
	switch( iType&AstNode::tTypeMask )
	{
		case AstNode::tBranch:
			pCurNode = (AstBranch *)pCurNode->addNode( new AstBranch( iType ) );
			break;

		case AstNode::tLeaf:
			pCurNode->addNode( new AstLeaf( iType ) );
			break;
	}
}

void GameBuilder::closeNode()
{
	pCurNode = pCurNode->getParent();
}

void GameBuilder::addLiteral( const Variable &v )
{
	setLiteral( v );
	if( pCurNode )
	{
		stackMod( AstNode::tLeafLiteral );
		pCurNode->addNode( new AstLeafLiteral( v ) );
	}
}

void GameBuilder::addVarRef( const Bu::String &sName, ScopeId sid )
{
	if( pCurNode )
	{
		stackMod( AstNode::tVarName );
		pCurNode->addNode( new AstLeafLiteral( AstNode::tVarName, Variable::newVariableName( sName, sid ) ) );
	}
}

void GameBuilder::addFuncCall( const Bu::String &sName )
{
	if( pCurNode )
	{
		stackMod( AstNode::tFuncCall );
		pCurNode->addNode( new AstLeafLiteral( AstNode::tFuncCall, sName ) );
	}
}

void GameBuilder::stackMod( AstNode::Type iType )
{
	switch( iType )
	{
		case AstNode::tNot:
		case AstNode::tNegate:
		case AstNode::tSwap:
		case AstNode::tDeref:
		case AstNode::tExists:
		case AstNode::tDelete:
			break;

		case AstNode::tComp:
		case AstNode::tCompGt:
		case AstNode::tCompLt:
		case AstNode::tCompGtEq:
		case AstNode::tCompLtEq:
		case AstNode::tStore:
		case AstNode::tAnd:
		case AstNode::tOr:
		case AstNode::tPlus:
		case AstNode::tMinus:
		case AstNode::tDivide:
		case AstNode::tMultiply:
		case AstNode::tPlusStore:
		case AstNode::tMinusStore:
		case AstNode::tDivideStore:
		case AstNode::tMultiplyStore:
		case AstNode::tIn:
		case AstNode::tGoto:
		case AstNode::tStoreRev:
		case AstNode::tReturn: // return doesn't pop anything, but it counts as it.
		case AstNode::tAppend:
		case AstNode::tPop:
		case AstNode::tIndex:
			iStackHeight--;
			break;
		
		case AstNode::tInsert:
			iStackHeight -= 2;
			break;

		case AstNode::tLeafLiteral:
		case AstNode::tVarName:
		case AstNode::tLiteral:
		case AstNode::tFuncCall:
			iStackHeight++;
			break;

		case AstNode::tBranch:
			break;

		case AstNode::tScope:
			break;

		case AstNode::tIf:
			iStackHeight--;
			break;

		case AstNode::tForEach:
			iStackHeight -= 2;
			break;

		case AstNode::tWhile:
			iStackHeight -= 1;
			break;

	}
}

void GameBuilder::beginGlobal()
{
	bGlobal = true;
}

void GameBuilder::closeGlobal()
{
	bGlobal = false;
}

void GameBuilder::beginCommand( const Bu::String &sValue )
{
	if( !bGlobal && pCurSit->getInputType() != Situation::inputCommand )
		throw Bu::ExceptionBase("Command in non-command situation.");
	pCurNode = pCurRoot = new AstBranch( AstNode::tScope );
	pCurCmd = new Command();
	pCurCmd->addLiteral( sValue );
}

void GameBuilder::addCommandLiteral( const Bu::String &sValue )
{
	pCurCmd->addLiteral( sValue );
}

void GameBuilder::addCommandParam( const Bu::String &sValue )
{
	pCurCmd->addParam( sValue );
}

void GameBuilder::endCommandParams()
{
	Bu::StringList lParams = pCurCmd->getParamList();

	for( Bu::StringList::iterator i = lParams.begin(); i; i++ )
	{
		addVarRef( *i, sidLocal );
		addNode( AstNode::tStoreRev );
	}
}

void GameBuilder::closeCommand()
{
	pCurCmd->setAst( pCurRoot );
	pCurRoot = pCurNode = NULL;
	//pCurCmd->print();
	if( bGlobal )
	{
		pGame->csGlobal.addCommand( pCurCmd );
	}
	else
	{
		pCurSit->csLocal.addCommand( pCurCmd );
	}
	pCurCmd = NULL;
	iStackHeight = 0;
}

void GameBuilder::beginOption( const Bu::String &sValue )
{
	if( pCurSit->getInputType() != Situation::inputOption )
		throw Bu::ExceptionBase("Option in non-option situation.");
	pCurNode = pCurRoot = new AstBranch( AstNode::tScope );
	pCurCmd = new Command();
	pCurCmd->addLiteral( sValue );
}

void GameBuilder::closeOption()
{
	pCurCmd->setAst( pCurRoot );
	pCurRoot = pCurNode = NULL;
	//pCurCmd->print();
	pCurSit->csLocal.addCommand( pCurCmd );
	pCurCmd = NULL;
	iStackHeight = 0;
}