/*************************************************************************************************
** Project:     FSMGenerator
** Package:     FSMGenerator
** Module:      FSMGenerator::parser
** File:        parser.cpp
** Date:        2002.08.01
** Author:      Pavel Bekkerman (chpavel@tx.technion.ac.il)
** Copyright:   Copyright (C) 2002 Pavel Bekkerman
** License:
**              This program is free software; you can redistribute it and/or
**              modify it under the terms of the GNU General Public License as
**              published by the Free Software Foundation; either version 2 of
**              the License, or (at your option) any later version.
**
**              This program is distributed in the hope that it will be useful,
**              but WITHOUT ANY WARRANTY; without even the implied warranty of
**              MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**              GNU General Public License for more details.
**
**              You should have received a copy of the GNU General Public License
**              along with this program; if not, write to the Free Software
**              Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
**
** Contact:     Pavel Bekkerman (chpavel@tx.technion.ac.il)
*************************************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include "../FSMCommon.h"
#include "../scanner/FSMScanner.h"
#include "../dbmanager/FSMDBManager.h"

extern FILE *yyin;
extern FILE *yyout;

YYSTYPE	yylval;

// Sections
#define SECTION_NAME		0
#define SECTION_STATES          1
#define SECTION_EVENTS          2
#define SECTION_TRANSITIONS     3
#define SECTION_START           4
#define SECTION_FINAL           5
#define SECTION_CALLBACKS       6
#define SECTION_HOOKS		7
#define SECTIONS		8
#define SECTION_UNDEFINED       SECTIONS // MUST be max

namespace FSMGenerator
{

static char *g_strSections[SECTIONS] = { "[name]", "[states]", "[events]", "[transitions]", "[start]", "[final]", "[callbacks]", "[hooks]" };
// end

static int g_fSectionNameAppeared;
static int g_fSectionStatesAppeared;
static int g_fSectionEventsAppeared;
static int g_fSectionTransitionsAppeared;
static int g_fSectionStartAppeared;
static int g_fSectionFinalAppeared;
static int g_fSectionCallbacksAppeared;
static int g_fSectionHooksAppeared;

/////////////////////////////////////////////////////////////////////////////////////
// Functions
/////////////////////////////////////////////////////////////////////////////////////

// Parsing: File
int _parse( FSMDBManager *pFSMDBManager );

// Parsing: Section
int _parse_section( FSMDBManager *pFSMDBManager, char *strSection );
int _parse_section_name( FSMDBManager *pFSMDBManager );
int _parse_section_states( FSMDBManager *pFSMDBManager );
int _parse_section_events( FSMDBManager *pFSMDBManager );
int _parse_section_transitions( FSMDBManager *pFSMDBManager );
int _parse_section_start( FSMDBManager *pFSMDBManager );
int _parse_section_final( FSMDBManager *pFSMDBManager );
int _parse_section_callbacks( FSMDBManager *pFSMDBManager );
int _parse_section_hooks( FSMDBManager *pFSMDBManager );

// Helper:
int _identify_section( char *strSection );

// end

//
// parse the strConfigurationFileName and return the filled DB
//
FSMDB* parse( const char *strConfigurationFileName )
{
  int 	return_code;
  FILE	*pfIn, *pfOut;
  FSMDB			*pFSMDB;
  FSMDBManager	*pFSMDBManager;
	
	//
	// Initialize: open files, create database
	//
	pfOut = fopen( "temp.tmp", "w" );
        if( pfOut == NULL )
        {
                fprintf( stderr, "Error: Unable to open file: %s\n", "temp.tmp" );
                return NULL;
        }
	yyout = pfOut;

	pfIn = fopen( strConfigurationFileName, "r" );
	if( pfIn == NULL )
	{
		fprintf( stderr, "Error: Unable to open file: %s\n", strConfigurationFileName );
		return NULL;
	}
	yyin = pfIn;

	pFSMDB = new FSMDB( "" );
	if( pFSMDB == NULL )
	{
		return NULL;
	}

	pFSMDBManager = new FSMDBManager( *pFSMDB );
	if( pFSMDBManager == NULL )
	{
		return NULL;
	}

	//
	// Parse: read tokens one-by-one, fill the DB.
	//
	return_code = _parse( pFSMDBManager ); 
	if( return_code != ERROR_SUCCESS )
	{
		fprintf( stderr, "Error: Failed parsing file: %s\n", strConfigurationFileName );
		delete pFSMDBManager;
		return( NULL );
	}

	delete( pFSMDBManager );
	
	//
	// Finalize: close files.
	//
	fclose( pfIn );
	fclose( pfOut );

  return pFSMDB;
}

int _parse( FSMDBManager *pFSMDBManager )
{
  int return_code;

	return_code = _parse_section( pFSMDBManager, NULL );

  return( return_code );
}

int _parse_section( FSMDBManager *pFSMDBManager, char *strSection )
{
  int return_code;
  int nSectionID;

	if( strSection != NULL ) // valid section has started
	{
		nSectionID = _identify_section( strSection );
		switch( nSectionID )
		{
			case SECTION_NAME:
				return_code = _parse_section_name( pFSMDBManager );
			break;
			case SECTION_STATES:
				return_code = _parse_section_states( pFSMDBManager );
			break;
			case SECTION_EVENTS:
				return_code = _parse_section_events( pFSMDBManager );
			break;
			case SECTION_TRANSITIONS:
				return_code = _parse_section_transitions( pFSMDBManager );
			break;
			case SECTION_START:
				return_code = _parse_section_start( pFSMDBManager );
			break;
			case SECTION_FINAL:
				return_code = _parse_section_final( pFSMDBManager );
			break;
			case SECTION_CALLBACKS:
				return_code = _parse_section_callbacks( pFSMDBManager );
			break;
			case SECTION_HOOKS:
				return_code = _parse_section_hooks( pFSMDBManager );
			break;
			case SECTION_UNDEFINED:
				return_code = ERROR_SECTION_UNDEFINED;
			break;

			default:
				return( ERROR_FAILURE );
		}
	}
	else // seek for the first section to start
	{
		g_fSectionNameAppeared = 0;
		g_fSectionStatesAppeared = 0;
		g_fSectionEventsAppeared = 0;
		g_fSectionTransitionsAppeared = 0;
		g_fSectionStartAppeared = 0;
		g_fSectionFinalAppeared = 0;
		g_fSectionCallbacksAppeared = 0;

		while( (return_code = yylex()) != ERROR_EOF )
		{
			if( return_code != ERROR_SUCCESS )
			{
				return( ERROR_FAILURE );
			}
			
			if( yylval.id == TOKEN_SECTION )
			{
				return_code = _parse_section( pFSMDBManager, yylval.text );
				return( return_code );
			}
			else if( yylval.id == TOKEN_COMMENT )
			{
				// nothing to do
			}
			else
			{
				fprintf( stderr, "Error: Wrong syntax: line %d\n", yylval.line );
				return( ERROR_FAILURE );
			}
		}

		if( g_fSectionNameAppeared == 0 )
		{
			fprintf( stderr, "Warning: Section %s doesn't appear in file\n", g_strSections[SECTION_NAME] );
			// PB: non-critical error ?!
		}

		if( g_fSectionStatesAppeared == 0 )
		{
			fprintf( stderr, "Warning: Section %s doesn't appear in file\n", g_strSections[SECTION_STATES] );
			// PB: non-critical error ?!
		}

		if( g_fSectionEventsAppeared == 0 )
		{
			fprintf( stderr, "Warning: Section %s doesn't appear in file\n", g_strSections[SECTION_EVENTS] );
			// PB: non-critical error ?!
		}

		if( g_fSectionTransitionsAppeared == 0 )
		{
			fprintf( stderr, "Warning: Section %s doesn't appear in file\n", g_strSections[SECTION_TRANSITIONS] );
			// PB: non-critical error ?!
		}

		if( g_fSectionStartAppeared == 0 )
		{
			fprintf( stderr, "Warning: Section %s doesn't appear in file\n", g_strSections[SECTION_START] );
			// PB: non-critical error ?!
		}

		if( g_fSectionFinalAppeared == 0 )
		{
			fprintf( stderr, "Warning: Section %s doesn't appear in file\n", g_strSections[SECTION_FINAL] );
			// PB: non-critical error ?!
		}

		if( g_fSectionCallbacksAppeared == 0 )
		{
			fprintf( stderr, "Warning: Section %s doesn't appear in file\n", g_strSections[SECTION_CALLBACKS] );
			// PB: non-critical error ?!
		}

		if( g_fSectionHooksAppeared == 0 )
		{
			fprintf( stderr, "Warning: Section %s doesn't appear in file\n", g_strSections[SECTION_HOOKS] );
			// PB: non-critical error ?!
		}

		return_code = ERROR_SUCCESS;
	}

  return( return_code );
}

int _parse_section_name( FSMDBManager *pFSMDBManager )
{
  int return_code;
  int fNameAppeared;
  char strName[STRING_MAX_LENGTH];

	if( g_fSectionNameAppeared == 1 )
	{
		fprintf( stderr, "Error: Section %s must appeare ONLY once: line %d\n", g_strSections[SECTION_NAME], yylval.line );
		return( ERROR_FAILURE );
	}
	g_fSectionNameAppeared = 1;

	fNameAppeared = 0;
	while( (return_code = yylex()) != ERROR_EOF )
	{
		if( return_code != ERROR_SUCCESS )
		{
			return( ERROR_FAILURE );
		}
		
		switch( yylval.id ) // token id
		{
			case TOKEN_SECTION:
				if( fNameAppeared == 0 )
				{
					fprintf( stderr, "Warning: No FSM name is given in section %s: line %d\n", g_strSections[SECTION_NAME], yylval.line );
					// PB: non-critical error ?!
				}
				return_code = _parse_section( pFSMDBManager, yylval.text );
				return( return_code );
			break;

			case TOKEN_NAME:
				if( fNameAppeared == 0 )
				{
					fNameAppeared = 1;
					strcpy( strName, yylval.text );
					try
					{
						pFSMDBManager->NameSet( strName );
					}
					catch( DBException &e )
					{
						fprintf( stderr, "Error: In section %s, line %d: %s\n", g_strSections[SECTION_NAME], yylval.line, e.ErrorInfo().c_str() );
						return( ERROR_FAILURE );
					}
					printf( "OUTPUT: name: %s\n", strName );
				}
				else
				{
					fprintf( stderr, "Error: In section %s the FSM name MUST be alphanumeric ONLY: line %d\n", g_strSections[SECTION_NAME], yylval.line );
					return( ERROR_FAILURE );
				}
			break;

			case TOKEN_COMMENT:
				// nothing to do
			break;
			
			default:
				fprintf( stderr, "Error: Wrong syntax in section %s: line %d, %s\n", g_strSections[SECTION_NAME], yylval.line, yylval.text );
				return( ERROR_FAILURE );
		}
	}

	if( fNameAppeared == 0 )
	{
		fprintf( stderr, "Warning: No FSM name is given in section %s: line %d\n", g_strSections[SECTION_NAME], yylval.line );
		// PB: non-critical error ?!
	}

  return( ERROR_SUCCESS );
}

int _parse_section_states( FSMDBManager *pFSMDBManager )
{
  int return_code;
  int nState; // "0"<s>"1"<;>"0"
  char strState[STRING_MAX_LENGTH];
  int fStatesAppeared;

	if( g_fSectionStatesAppeared == 1 )
	{
		fprintf( stderr, "Error: Section %s must appeare ONLY once: line %d\n", g_strSections[SECTION_STATES], yylval.line );
		return( ERROR_FAILURE );
	}
	g_fSectionStatesAppeared = 1;

	fStatesAppeared = 0;
	nState = 0;
	while( (return_code = yylex()) != ERROR_EOF )
	{
		if( return_code != ERROR_SUCCESS )
		{
			return( ERROR_FAILURE );
		}
		
		if( yylval.id == TOKEN_COMMENT )
		{
			// nothing to do
		}
		else
		{
			switch( nState ) 
			{
			case 0:
				if( yylval.id == TOKEN_SECTION )
				{
					if( fStatesAppeared == 0 )
					{
						fprintf( stderr, "Warning: No states appeared in section %s: line %d\n", g_strSections[SECTION_STATES], yylval.line );
						// PB: non-critical error ?!
					}
					return_code = _parse_section( pFSMDBManager, yylval.text );
					return( return_code );
				}
				else if( yylval.id == TOKEN_NAME )
				{
					fStatesAppeared = 1;
					strcpy( strState, yylval.text );
					nState = 1;
				}
				else if( yylval.id == TOKEN_SEMICOLLON )
				{
					nState = 0;
				}
				else
				{
					fprintf( stderr, "Error: Wrong syntax in section %s: line %d, %s\n", g_strSections[SECTION_STATES], yylval.line, yylval.text );
					return( ERROR_FAILURE );
				}
			break;

			case 1:
				if( yylval.id == TOKEN_SEMICOLLON )
				{
					try
					{
						pFSMDBManager->StateInsert( strState );
					}
					catch( DBException &e )
					{
						fprintf( stderr, "Error: In section %s, line %d: %s\n", g_strSections[SECTION_STATES], yylval.line, e.ErrorInfo().c_str() );
						return( ERROR_FAILURE );
					}
					printf( "OUTPUT: state: %s\n", strState );
					nState = 0;
				}
				else
				{
					fprintf( stderr, "Error: Wrong syntax in section %s: line %d, %s\n", g_strSections[SECTION_STATES], yylval.line, yylval.text );
					return( ERROR_FAILURE );
				}
			break;

			default:
				fprintf( stderr, "Error: Unknown error\n" );
				return( ERROR_FAILURE );
			}
		}
	}

	if( fStatesAppeared == 0 )
	{
		fprintf( stderr, "Warning: No states appeared in section %s: line %d\n", g_strSections[SECTION_STATES], yylval.line );
		// PB: non-critical error ?!
	}

  return( ERROR_SUCCESS );
}

int _parse_section_events( FSMDBManager *pFSMDBManager )
{
  int return_code;
  int nState; // "0"<e>"1"<;>"0"
  char strEvent[STRING_MAX_LENGTH];
  int fEventsAppeared;

	if( g_fSectionEventsAppeared == 1 )
	{
		fprintf( stderr, "Error: Section %s must appeare ONLY once: line %d\n", g_strSections[SECTION_EVENTS], yylval.line );
		return( ERROR_FAILURE );
	}
	g_fSectionEventsAppeared = 1;

	fEventsAppeared = 0;
	nState = 0;
	while( (return_code = yylex()) != ERROR_EOF )
	{
		if( return_code != ERROR_SUCCESS )
		{
			return( ERROR_FAILURE );
		}
		
		if( yylval.id == TOKEN_COMMENT )
		{
			// nothing to do
		}
		else
		{
			switch( nState ) 
			{
			case 0:
				if( yylval.id == TOKEN_SECTION )
				{
					if( fEventsAppeared == 0 )
					{
						fprintf( stderr, "Warning: No events appeared in section %s: line %d\n", g_strSections[SECTION_EVENTS], yylval.line );
						// PB: non-critical error ?!
					}
					return_code = _parse_section( pFSMDBManager, yylval.text );
					return( return_code );
				}
				else if( yylval.id == TOKEN_NAME )
				{
					fEventsAppeared = 1;
					strcpy( strEvent, yylval.text );
					nState = 1;
				}
				else if( yylval.id == TOKEN_SEMICOLLON )
				{
				}
				else
				{
					fprintf( stderr, "Error: Wrong syntax in section %s: line %d, %s\n", g_strSections[SECTION_EVENTS], yylval.line, yylval.text );
					return( ERROR_FAILURE );
				}
			break;

			case 1:
				if( yylval.id == TOKEN_SEMICOLLON )
				{
					try
					{
						pFSMDBManager->EventInsert( strEvent );
					}
					catch( DBException &e )
					{
						fprintf( stderr, "Error: In section %s, line %d: %s\n", g_strSections[SECTION_EVENTS], yylval.line, e.ErrorInfo().c_str() );
						return( ERROR_FAILURE );
					}
					printf( "OUTPUT: event: %s\n", strEvent );
					nState = 0;
				}
				else
				{
					fprintf( stderr, "Error: Wrong syntax in section %s: line %d, %s\n", g_strSections[SECTION_EVENTS], yylval.line, yylval.text );
					return( ERROR_FAILURE );
				}
			break;

			default:
				fprintf( stderr, "Error: Unknown error\n" );
				return( ERROR_FAILURE );
			}
		}
	}

	if( fEventsAppeared == 0 )
	{
		fprintf( stderr, "Warning: No events appeared in section %s: line %d\n", g_strSections[SECTION_EVENTS], yylval.line );
		// PB: non-critical error ?!
	}

  return( ERROR_SUCCESS );
}

int _parse_section_transitions( FSMDBManager *pFSMDBManager )
{
  int return_code;
  int fTransitionsAppeared;
  int nState; // "0"<t>"1"<:>"2"<s1>"3"<,>"4"<e>"5"<->>"6"<s2>"7"<;>"0"
  char strTransition[STRING_MAX_LENGTH];
  char strState1[STRING_MAX_LENGTH];
  char strEvent[STRING_MAX_LENGTH];
  char strState2[STRING_MAX_LENGTH];

	if( g_fSectionStatesAppeared == 0 || g_fSectionEventsAppeared == 0 )
	{
		fprintf( stderr, "Error: Section %s must appeare strictly AFTER the sections %s and %s: line %d\n", g_strSections[SECTION_TRANSITIONS], g_strSections[SECTION_STATES], g_strSections[SECTION_EVENTS], yylval.line );
		return( ERROR_FAILURE );
	}

	if( g_fSectionTransitionsAppeared == 1 )
	{
		fprintf( stderr, "Error: Section %s must appeare ONLY once: line %d\n", g_strSections[SECTION_TRANSITIONS], yylval.line );
		return( ERROR_FAILURE );
	}
	g_fSectionTransitionsAppeared = 1;

	fTransitionsAppeared = 0;
	nState = 0;
	while( (return_code = yylex()) != ERROR_EOF )
	{
		if( return_code != ERROR_SUCCESS )
		{
			return( ERROR_FAILURE );
		}
			
		if( yylval.id == TOKEN_COMMENT )
		{
			// nothing to do
		}
		else
		{
			switch( nState )
			{
				case 0:
					if( yylval.id == TOKEN_SECTION )
					{
						if( fTransitionsAppeared == 0 )
						{
							fprintf( stderr, "Warning: No transitions appeared in section %s: line %d\n", g_strSections[SECTION_TRANSITIONS], yylval.line );
							// PB: non-critical error ?!
						}
						return_code = _parse_section( pFSMDBManager, yylval.text );
						return( return_code );
					}
					else if( yylval.id == TOKEN_NAME )
					{
						fTransitionsAppeared = 1;
						strcpy( strTransition, yylval.text );
						nState = 1;
					}
					else if( yylval.id == TOKEN_SEMICOLLON )
					{
						nState = 0;
					}
					else
					{
						fprintf( stderr, "Error: Wrong syntax in section %s: line %d, %s\n", g_strSections[SECTION_TRANSITIONS], yylval.line ,yylval.text );
						return( ERROR_FAILURE );
					}
				break;

				case 1:
					if( yylval.id == TOKEN_COLLON )
					{
						nState = 2;
					}
					else
					{
						fprintf( stderr, "Error: Wrong syntax in section %s: line %d, %s\n", g_strSections[SECTION_TRANSITIONS], yylval.line ,yylval.text );
						return( ERROR_FAILURE );
					}
				break;

				case 2:
					if( yylval.id == TOKEN_NAME )
					{
						strcpy( strState1, yylval.text );
						nState = 3;
					}
					else
					{
						fprintf( stderr, "Error: Wrong syntax in section %s: line %d, %s\n", g_strSections[SECTION_TRANSITIONS], yylval.line ,yylval.text );
						return( ERROR_FAILURE );
					}
				break;

				case 3:
					if( yylval.id == TOKEN_COMMA )
					{
						nState = 4;
					}
					else
					{
						fprintf( stderr, "Error: Wrong syntax in section %s: line %d, %s\n", g_strSections[SECTION_TRANSITIONS], yylval.line ,yylval.text );
						return( ERROR_FAILURE );
					}
				break;

				case 4:
					if( yylval.id == TOKEN_NAME )
					{
						strcpy( strEvent, yylval.text );
						nState = 5;
					}
					else
					{
						fprintf( stderr, "Error: Wrong syntax in section %s: line %d, %s\n", g_strSections[SECTION_TRANSITIONS], yylval.line ,yylval.text );
						return( ERROR_FAILURE );
					}
				break;

				case 5:
					if( yylval.id == TOKEN_ARROW )
					{
						nState = 6;
					}
					else
					{
						fprintf( stderr, "Error: Wrong syntax in section %s: line %d, %s\n", g_strSections[SECTION_TRANSITIONS], yylval.line ,yylval.text );
						return( ERROR_FAILURE );
					}
				break;

				case 6:
					if( yylval.id == TOKEN_NAME )
					{
						strcpy( strState2, yylval.text );
						nState = 7;
					}
					else
					{
						fprintf( stderr, "Error: Wrong syntax in section %s: line %d, %s\n", g_strSections[SECTION_TRANSITIONS], yylval.line ,yylval.text );
						return( ERROR_FAILURE );
					}
				break;

				case 7:
					if( yylval.id == TOKEN_SEMICOLLON )
					{
						try
						{
							pFSMDBManager->TransInsert( strTransition, strState1, strEvent, strState2 );
						}
						catch( DBException &e )
						{
							fprintf( stderr, "Error: In section %s, line %d: %s\n", g_strSections[SECTION_TRANSITIONS], yylval.line, e.ErrorInfo().c_str() );
							return( ERROR_FAILURE );
						}
						printf( "OUTPUT: transition: %s: %s, %s -> %s\n", strTransition, strState1, strEvent, strState2 );
						nState = 0;
					}
					else
					{
						fprintf( stderr, "Error: Wrong syntax in section %s: line %d, %s\n", g_strSections[SECTION_TRANSITIONS], yylval.line ,yylval.text );
						return( ERROR_FAILURE );
					}
				break;

				default:
					fprintf( stderr, "Error: Unknown error\n" );
					return( ERROR_FAILURE );
			}
		}
	}

	if( fTransitionsAppeared == 0 )
	{
		fprintf( stderr, "Warning: No transitions appeared in section %s: line %d\n", g_strSections[SECTION_TRANSITIONS], yylval.line );
		// PB: non-critical error ?!
	}

  return( ERROR_SUCCESS );
}

int _parse_section_start( FSMDBManager *pFSMDBManager )
{
  int return_code;
  int nState;
  char strStartState[STRING_MAX_LENGTH];
  int fStartStateAppeared;

	if( g_fSectionStatesAppeared == 0 )
	{
		fprintf( stderr, "Error: Section %s must appeare strictly AFTER the section %s: line %d\n", g_strSections[SECTION_START], g_strSections[SECTION_STATES], yylval.line );
		return( ERROR_FAILURE );
	}

	if( g_fSectionStartAppeared == 1 )
	{
		fprintf( stderr, "Error: Section %s must appeare ONLY once: line %d\n", g_strSections[SECTION_START], yylval.line );
		return( ERROR_FAILURE );
	}
	g_fSectionStartAppeared = 1;

	fStartStateAppeared = 0;
	nState = 0;
	while( (return_code = yylex()) != ERROR_EOF )
	{
		if( return_code != ERROR_SUCCESS )
		{
			return( ERROR_FAILURE );
		}
		
		if( yylval.id == TOKEN_COMMENT )
		{
			// nothing to do
		}
		else
		{
			switch( nState ) 
			{
			case 0:
				if( yylval.id == TOKEN_SECTION )
				{
					if( fStartStateAppeared == 0 )
					{
						fprintf( stderr, "Warning: No final state appeared in section %s: line %d\n", g_strSections[SECTION_START], yylval.line );
						// PB: non-critical error ?!
					}
					return_code = _parse_section( pFSMDBManager, yylval.text );
					return( return_code );
				}
				else if( yylval.id == TOKEN_NAME )
				{
					if( fStartStateAppeared == 0 )
					{
						fStartStateAppeared = 1;
						strcpy( strStartState, yylval.text );
						nState = 1;
					}
					else
					{
						fprintf( stderr, "Error: Only one state can be defined as a start state in section %s: line %d, %s\n", g_strSections[SECTION_START], yylval.line, yylval.text );
						return( ERROR_FAILURE );
					}
				}
				else if( yylval.id == TOKEN_SEMICOLLON )
				{
				}
				else
				{
					fprintf( stderr, "Error: Wrong syntax in section %s: line %d, %s\n", g_strSections[SECTION_START], yylval.line, yylval.text );
					return( ERROR_FAILURE );
				}
			break;

			case 1:
				if( yylval.id == TOKEN_SEMICOLLON )
				{
					try
					{
						pFSMDBManager->StartSet( strStartState );
					}
					catch( DBException &e )
					{
						fprintf( stderr, "Error: In section %s, line %d: %s\n", g_strSections[SECTION_START], yylval.line, e.ErrorInfo().c_str() );
						return( ERROR_FAILURE );
					}
					printf( "OUTPUT: start: %s\n", strStartState );
					nState = 0;
				}
				else
				{
					fprintf( stderr, "Error: Wrong syntax in section %s: line %d, %s\n", g_strSections[SECTION_START], yylval.line, yylval.text );
					return( ERROR_FAILURE );
				}
			break;

			default:
				fprintf( stderr, "Error: Unknown error\n" );
				return( ERROR_FAILURE );
			}
		}
	}

	if( fStartStateAppeared == 0 )
	{
		fprintf( stderr, "Warning: No final state appeared in section %s: line %d\n", g_strSections[SECTION_START], yylval.line );
		// PB: non-critical error ?!
	}

  return( ERROR_SUCCESS );
}

int _parse_section_final( FSMDBManager *pFSMDBManager )
{
  int return_code;
  int nState;
  char strFinalState[STRING_MAX_LENGTH];
  int fFinalStateAppeared;

	if( g_fSectionStatesAppeared == 0 )
	{
		fprintf( stderr, "Error: Section %s must appeare strictly AFTER the section %s: line %d\n", g_strSections[SECTION_FINAL], g_strSections[SECTION_STATES], yylval.line );
		return( ERROR_FAILURE );
	}

	if( g_fSectionFinalAppeared == 1 )
	{
		fprintf( stderr, "Error: Section %s must appeare ONLY once: line %d\n", g_strSections[SECTION_FINAL], yylval.line );
		return( ERROR_FAILURE );
	}
	g_fSectionFinalAppeared = 1;

	fFinalStateAppeared = 0;
	nState = 0;
	while( (return_code = yylex()) != ERROR_EOF )
	{
		if( return_code != ERROR_SUCCESS )
		{
			return( ERROR_FAILURE );
		}
		
		if( yylval.id == TOKEN_COMMENT )
		{
			// nothing to do
		}
		else
		{
			switch( nState ) 
			{
			case 0:
				if( yylval.id == TOKEN_SECTION )
				{
					if( fFinalStateAppeared == 0 )
					{
						fprintf( stderr, "Warning: No final state appeared in section %s: line %d\n", g_strSections[SECTION_FINAL], yylval.line );
						// PB: non-critical error ?!
					}
					return_code = _parse_section( pFSMDBManager, yylval.text );
					return( return_code );
				}
				else if( yylval.id == TOKEN_NAME )
				{
					fFinalStateAppeared = 1;
					strcpy( strFinalState, yylval.text );
					nState = 1;
				}
				else if( yylval.id == TOKEN_SEMICOLLON )
				{
				}
				else
				{
					fprintf( stderr, "Error: Wrong syntax in section %s: line %d, %s\n", g_strSections[SECTION_FINAL], yylval.line, yylval.text );
					return( ERROR_FAILURE );
				}
			break;

			case 1:
				if( yylval.id == TOKEN_SEMICOLLON )
				{
					try
					{
						pFSMDBManager->FinalInsert( strFinalState );
					}
					catch( DBException &e )
					{
						fprintf( stderr, "Error: In section %s, line %d: %s\n", g_strSections[SECTION_FINAL], yylval.line, e.ErrorInfo().c_str() );
						return( ERROR_FAILURE );
					}
					printf( "OUTPUT: final: %s\n", strFinalState );
					nState = 0;
				}
			break;

			default:
				fprintf( stderr, "Error: Unknown error\n" );
				return( ERROR_FAILURE );
			}
		}
	}

	if( fFinalStateAppeared == 0 )
	{
		fprintf( stderr, "Warning: No final state appeared in section %s: line %d\n", g_strSections[SECTION_FINAL], yylval.line );
		// PB: non-critical error ?!
	}

  return( ERROR_SUCCESS );
}

int _parse_section_callbacks( FSMDBManager *pFSMDBManager )
{
  int return_code;
  int fCallbacksAppeared;
  int fCollectingStructuredName;
  int nState; // "0"<c>"1"<:>"2"<f1>"3"<;>"0"
  char strCallback[STRING_MAX_LENGTH];
  char strFunction[STRING_MAX_LENGTH];
  char strTemp[STRING_MAX_LENGTH];

	if( g_fSectionCallbacksAppeared == 1 )
	{
		fprintf( stderr, "Error: Section %s must appeare ONLY once: line %d\n", g_strSections[SECTION_CALLBACKS], yylval.line );
		return( ERROR_FAILURE );
	}
	g_fSectionCallbacksAppeared = 1;

	fCallbacksAppeared = 0;
	nState = 0;
	while( (return_code = yylex()) != ERROR_EOF )
	{
		if( return_code != ERROR_SUCCESS )
		{
			return( ERROR_FAILURE );
		}
		
		if( yylval.id == TOKEN_COMMENT )
		{
			// nothing to do
		}
		else
		{
			switch( nState )
			{
				case 0:
					if( yylval.id == TOKEN_SECTION )
					{
						if( fCallbacksAppeared == 0 )
						{
							fprintf( stderr, "Warning: No callbacks appeared in section %s: line %d\n", g_strSections[SECTION_CALLBACKS], yylval.line );
							// PB: non-critical error ?!
						}
						return_code = _parse_section( pFSMDBManager, yylval.text );
						return( return_code );
					}
					else if( yylval.id == TOKEN_NAME )
					{
						fCallbacksAppeared = 1;
						strcpy( strCallback, yylval.text );
						nState = 1;
					}
					else if( yylval.id == TOKEN_SEMICOLLON )
					{
						nState = 0;
					}
					else
					{
						fprintf( stderr, "Error: Wrong syntax in section %s: line %d, %s\n", g_strSections[SECTION_CALLBACKS], yylval.line ,yylval.text );
						return( ERROR_FAILURE );
					}
				break;

				case 1:
					if( yylval.id == TOKEN_COLLON )
					{
						fCollectingStructuredName = 0;
						nState = 2;
					}
					else
					{
						fprintf( stderr, "Error: Wrong syntax in section %s: line %d, %s\n", g_strSections[SECTION_CALLBACKS], yylval.line ,yylval.text );
						return( ERROR_FAILURE );
					}
				break;

				case 2:
					if( yylval.id == TOKEN_NAME )
					{
						if( fCollectingStructuredName == 0 )
						{
							strcpy( strFunction, yylval.text );
						}
						else
						{
							strcpy( strTemp, strFunction );
							sprintf( strFunction, "%s.%s", strTemp, yylval.text );
						}
						nState = 3;
					}
					else
					{
						fprintf( stderr, "Error: Wrong syntax in section %s: line %d, %s\n", g_strSections[SECTION_CALLBACKS], yylval.line ,yylval.text );
						return( ERROR_FAILURE );
					}
				break;

				case 3:
					if( yylval.id == TOKEN_DOT )
					{
						fCollectingStructuredName = 1;
						nState = 2;
					}
					else if( yylval.id == TOKEN_SEMICOLLON )
					{
						try
						{
							pFSMDBManager->CallbackInsert( strCallback, strFunction );
						}
						catch( DBException &e )
						{
							fprintf( stderr, "Error: In section %s, line %d: %s\n", g_strSections[SECTION_CALLBACKS], yylval.line, e.ErrorInfo().c_str() );
							return( ERROR_FAILURE );
						}
						printf( "OUTPUT: callback: %s: %s\n", strCallback, strFunction );
						nState = 0;
					}
					else
					{
						fprintf( stderr, "Error: Wrong syntax in section %s: line %d, %s\n", g_strSections[SECTION_CALLBACKS], yylval.line ,yylval.text );
						return( ERROR_FAILURE );
					}
				break;

				default:
					fprintf( stderr, "Error: Unknown error\n" );
					return( ERROR_FAILURE );
			}
		}
	}

	if( fCallbacksAppeared == 0 )
	{
		fprintf( stderr, "Warning: No callbacks appeared in section %s: line %d\n", g_strSections[SECTION_CALLBACKS], yylval.line );
		// PB: non-critical error ?!
	}

  return( ERROR_SUCCESS );
}

int _parse_section_hooks( FSMDBManager *pFSMDBManager )
{
  int return_code;
  int fHooksAppeared;
  int nState; // "0"<i>"1"<:>"2"<c0>"3"<,>"4"<c1>"5"<;>"0"
  char strItem[STRING_MAX_LENGTH];
  char strCallback1[STRING_MAX_LENGTH];
  char strCallback2[STRING_MAX_LENGTH];

	if( g_fSectionStatesAppeared == 0 || g_fSectionEventsAppeared == 0 || g_fSectionTransitionsAppeared == 0 || g_fSectionCallbacksAppeared == 0)
	{
		fprintf( stderr, "Error: Section %s must appeare strictly AFTER the sections %s, %s, %s, %s: line %d\n", g_strSections[SECTION_HOOKS], g_strSections[SECTION_STATES], g_strSections[SECTION_EVENTS], g_strSections[SECTION_TRANSITIONS], g_strSections[SECTION_CALLBACKS], yylval.line );
		return( ERROR_FAILURE );
	}

	if( g_fSectionHooksAppeared == 1 )
	{
		fprintf( stderr, "Error: Section %s must appeare ONLY once: line %d\n", g_strSections[SECTION_HOOKS], yylval.line );
		return( ERROR_FAILURE );
	}
	g_fSectionHooksAppeared = 1;

	fHooksAppeared = 0;
	nState = 0;
	while( (return_code = yylex()) != ERROR_EOF )
	{
		if( return_code != ERROR_SUCCESS )
		{
			return( ERROR_FAILURE );
		}
		
		if( yylval.id == TOKEN_COMMENT )
		{
			// nothing to do
		}
		else
		{
			switch( nState )
			{
				case 0:
					if( yylval.id == TOKEN_SECTION )
					{
						if( fHooksAppeared == 0 )
						{
							fprintf( stderr, "Warning: No hooks appeared in section %s: line %d\n", g_strSections[SECTION_HOOKS], yylval.line );
							// PB: non-critical error ?!
						}
						return_code = _parse_section( pFSMDBManager, yylval.text );
						return( return_code );
					}
					else if( yylval.id == TOKEN_NAME )
					{
						fHooksAppeared = 1;
						strcpy( strItem, yylval.text );
						nState = 1;
					}
					else if( yylval.id == TOKEN_SEMICOLLON )
					{
						nState = 0;
					}
					else
					{
						fprintf( stderr, "Error: Wrong syntax in section %s: line %d, %s\n", g_strSections[SECTION_HOOKS], yylval.line ,yylval.text );
						return( ERROR_FAILURE );
					}
				break;

				case 1:
					if( yylval.id == TOKEN_COLLON )
					{
						nState = 2;
					}
					else
					{
						fprintf( stderr, "Error: Wrong syntax in section %s: line %d, %s\n", g_strSections[SECTION_HOOKS], yylval.line ,yylval.text );
						return( ERROR_FAILURE );
					}
				break;

				case 2:
					if( yylval.id == TOKEN_NAME )
					{
						strcpy( strCallback1, yylval.text );
						nState = 3;
					}
					else
					{
						fprintf( stderr, "Error: Wrong syntax in section %s: line %d, %s\n", g_strSections[SECTION_HOOKS], yylval.line ,yylval.text );
						return( ERROR_FAILURE );
					}
				break;

				case 3:
					if( yylval.id == TOKEN_COMMA )
					{
						nState = 4;
					}
					else if( yylval.id == TOKEN_SEMICOLLON )
					{
						try
						{
							pFSMDBManager->HookSet( strItem, strCallback1 );
						}
						catch( DBException &e )
						{
							fprintf( stderr, "Error: In section %s, line %d: %s\n", g_strSections[SECTION_HOOKS], yylval.line, e.ErrorInfo().c_str() );
							return( ERROR_FAILURE );
						}
						printf( "OUTPUT: hook: %s: %s\n", strItem, strCallback1 );
						nState = 0;
					}
					else
					{
						fprintf( stderr, "Error: Wrong syntax in section %s: line %d, %s\n", g_strSections[SECTION_HOOKS], yylval.line ,yylval.text );
						return( ERROR_FAILURE );
					}
				break;

				case 4:
					if( yylval.id == TOKEN_NAME )
					{
						strcpy( strCallback2, yylval.text );
						nState = 5;
					}
					else
					{
						fprintf( stderr, "Error: Wrong syntax in section %s: line %d, %s\n", g_strSections[SECTION_HOOKS], yylval.line ,yylval.text );
						return( ERROR_FAILURE );
					}
				break;

				case 5:
					if( yylval.id == TOKEN_SEMICOLLON )
					{
						try
						{
							pFSMDBManager->HookSet( strItem, strCallback1, strCallback2 );
						}
						catch( DBException &e )
						{
							fprintf( stderr, "Error: In section %s, line %d: %s\n", g_strSections[SECTION_HOOKS], yylval.line, e.ErrorInfo().c_str() );
							return( ERROR_FAILURE );
						}
						printf( "OUTPUT: hook: %s: %s, %s\n", strItem, strCallback1, strCallback2 );
						nState = 0;
					}
					else
					{
						fprintf( stderr, "Error: Wrong syntax in section %s: line %d, %s\n", g_strSections[SECTION_HOOKS], yylval.line ,yylval.text );
						return( ERROR_FAILURE );
					}
				break;

				default:
					fprintf( stderr, "Error: Unknown error\n" );
					return( ERROR_FAILURE );
			}
		}
	}

	if( fHooksAppeared == 0 )
	{
		fprintf( stderr, "Warning: No hooks appeared in section %s: line %d\n", g_strSections[SECTION_HOOKS], yylval.line );
		// PB: non-critical error ?!
	}

  return( ERROR_SUCCESS );
}

int _identify_section( char *strSection )
{
  int nSectionID;

	for( nSectionID = 0; nSectionID < SECTIONS; nSectionID++ )
		if( !strcmp( g_strSections[nSectionID], strSection ) )
			break;

  return( nSectionID );
}

}

