%option prefix="engine_ska_yy"
%{
#include <Engine/Base/CTString.h>
#include <Engine/Base/CTString.inl>
#include <Engine/Base/FileName.h>
#include <Engine/Base/ErrorReporting.h>

#include <Engine/Templates/DynamicStackArray.cpp>

#include "ParsingSmbs.h"
#include "smcPars.h"

extern CTFileName _fnmApplicationPath;

YYSTYPE engine_ska_yylval;

#ifdef __cplusplus
extern "C" { int engine_ska_yywrap(void); }
#endif

int engine_ska_yywrap(void)
{
  // no more buffers
  return 1;
};

// declarations for recursive SMC script parsing
struct BufferStackEntry {
  YY_BUFFER_STATE bse_bs;
  const char *bse_strName;
  const char *bse_strContents;
  int bse_iLineCt;
  BOOL bse_bParserEnd;
};

static BufferStackEntry _abseBufferStack[SMC_MAX_INCLUDE_LEVEL];
static int _ibsBufferStackTop = -1;

void SMCPushBuffer(const char *strName, const char *strBuffer, BOOL bParserEnd)
{
  _ibsBufferStackTop++;

  _abseBufferStack[_ibsBufferStackTop].bse_strContents = strdup(strBuffer);
  _abseBufferStack[_ibsBufferStackTop].bse_strName = strdup(strName);
  _abseBufferStack[_ibsBufferStackTop].bse_iLineCt = 1;
  _abseBufferStack[_ibsBufferStackTop].bse_bParserEnd = bParserEnd;

  _abseBufferStack[_ibsBufferStackTop].bse_bs = engine_ska_yy_scan_string((char*)(const char*)strBuffer);

  engine_ska_yy_switch_to_buffer(_abseBufferStack[_ibsBufferStackTop].bse_bs);
}
BOOL SMCPopBuffer(void)
{
  engine_ska_yy_delete_buffer( _abseBufferStack[_ibsBufferStackTop].bse_bs);
  free((void*)_abseBufferStack[_ibsBufferStackTop].bse_strName);
  free((void*)_abseBufferStack[_ibsBufferStackTop].bse_strContents);
  BOOL bParserEnd = _abseBufferStack[_ibsBufferStackTop].bse_bParserEnd;

  _ibsBufferStackTop--;

  if (_ibsBufferStackTop>=0) {
    engine_ska_yy_switch_to_buffer(_abseBufferStack[_ibsBufferStackTop].bse_bs);
  }
  return bParserEnd;
}
const char *SMCGetBufferName(void)
{
  return _abseBufferStack[_ibsBufferStackTop].bse_strName;
}
int SMCGetBufferLineNumber(void)
{
  return _abseBufferStack[_ibsBufferStackTop].bse_iLineCt;
}
int SMCGetBufferStackDepth(void)
{
  return _ibsBufferStackTop;
}
const char *SMCGetBufferContents(void)
{
  return _abseBufferStack[_ibsBufferStackTop].bse_strContents;
}
void SMCCountOneLine(void)
{
  _abseBufferStack[_ibsBufferStackTop].bse_iLineCt++;
}
%}

%x COMMENT
%x INCLUDE

DIGIT		[0-9]
HEXDIGIT [0-9A-Fa-f]
DOUBLEQUOTE	\"
STRINGCONTENT	([^\"]|(\\\"))
NONEXP_FLT  ({DIGIT}+"."{DIGIT}*)
EXP_FLT (({DIGIT}+("."({DIGIT}*)?)?)("E"|"e")("+"|"-")?{DIGIT}+)

%%

"#INCLUDE"               BEGIN(INCLUDE);
"SE_SMC"                { return(k_SE_SMC); }
"SE_END"                { return(k_SE_END); }
"TFNM"                  { return(k_TFNM);   }
"NAME"                  { return(k_NAME);   }
"MESH"                  { return(k_MESH);   }
"SKELETON"              { return(k_SKELETON);}
"ANIMSET"               { return(k_ANIMSET);}
"ANIMATION"             { return(K_ANIMATION);}
"TEXTURES"              { return(k_TEXTURES);}
"PARENTBONE"            { return(k_PARENTBONE);}
"OFFSET"                { return(k_OFFSET);}
"COLISION"              { return(k_COLISION);}
"ANIMSPEED"             { return(k_ANIMSPEED);}
"COLOR"                 { return(k_COLOR);}
"ALLFRAMESBBOX"         { return(k_ALLFRAMESBBOX);}

<INCLUDE>[ \t]*"\""      /* eat the whitespace */
<INCLUDE>[^"\""]*"\""   { /* got the include file name */

  if (SMCGetBufferStackDepth() >= SMC_MAX_INCLUDE_LEVEL) {
    ThrowF_t("File '%s' line %d\nIncludes nested too deeply '%s'",SMCGetBufferName(), SMCGetBufferLineNumber(),yytext);
  }
  char strFileName[256];
  strcpy(strFileName, yytext);
  strFileName[strlen(strFileName)-1] = 0;

  CTString strIncludeFile;
  try {
    strIncludeFile.Load_t(CTString(strFileName));
    SMCPushBuffer(strFileName, strIncludeFile, FALSE);

  } catch (const char *strError) {
    (void)strError;
    ThrowF_t("File '%s'\n Could not open '%s' (line %d)",SMCGetBufferName(), strFileName, SMCGetBufferLineNumber());
  }
  BEGIN(INITIAL);
}
<INCLUDE>.    {  /* something unrecognized inside include statement */
  BEGIN(INITIAL);
  ThrowF_t("File '%s'\n Wrong syntax for include statement",SMCGetBufferName());
}
<<EOF>> {
  if (SMCPopBuffer()) {
    yyterminate();
  }
}


 /* single character operators and punctuations */
";"|","|"{"|"}" {
  return(yytext[0]);}

 /* constants */

"-"?{DIGIT}+                  { engine_ska_yylval.i = atoi(yytext); return(c_int); }
"0x"{HEXDIGIT}+               { engine_ska_yylval.i = strtoul(yytext+2, NULL, 16); return(c_int);}
"-"?{NONEXP_FLT}("f"|"F")?    { engine_ska_yylval.f = (float) atof(yytext); return(c_float); }
"-"?{EXP_FLT}("f"|"F")?       { engine_ska_yylval.f = (float) atof(yytext); return(c_float); }
"\""{STRINGCONTENT}*"\""  { 
  char *strNew;
  // remove double-quotes
  yytext[strlen(yytext)-1] = 0;
  strNew = yytext+1;
  engine_ska_yylval.str = (const char*)strNew;
  return(c_string); 
}

 /* eat up comments */
"/*"          { BEGIN(COMMENT); }
<COMMENT>"* /" { BEGIN(INITIAL); }
<COMMENT>.    {}
"//"[^\n]*\n { SMCCountOneLine(); }

 /* eat up whitespace */
[ \t]+	 {
}
 /* eat up linefeeds and count lines in all conditions */
<*>\n	{
  SMCCountOneLine();;
}

 /* for all unrecognized characters */
. {
  // report an error
  ThrowF_t("File '%s'\n Unrecognized character '%c' (line %d)", SMCGetBufferName(), yytext[0], SMCGetBufferLineNumber());
  //ThrowF_t("Unrecognized character '%c' in line %d)", yytext[0], _yy_iLine );
}

%%

