#include <xvt.h>

#include <scanner.h>

inline bool string_start(char c)
{ return c == '"' || c == '\'' || c == '{'; }

inline char match(char c)
{ return (c == '{') ? '}' : c; }

TScanner::TScanner(const char* filename)
: _token(128), _key(2),
  _tmp(1024*8), _pushed(FALSE), _line(0)
{         
#ifdef WIN32
  open(filename, ios::in | ios::nocreate, filebuf::sh_read);
#else
  open(filename, ios::in);
#endif
  if (fail()) 
  {
    DIRECTORY dir; xvt_fsys_get_dir(&dir);
    TFilename curdir; xvt_fsys_convert_dir_to_str(&dir, curdir.get_buffer(), curdir.size());

    TString msg;
    msg << "Impossibile leggere il file '" << filename << "'\n"
        << "Directory corrente '" << curdir << "'\n";
    if (xvt_fsys_file_exists(filename))    
      msg << "Il file esite ma NON e' leggibile!";
    else
      msg << "Il file NON esite!";
    fatal_box(msg);
  }
}

TScanner::~TScanner()
{
}

const TString& TScanner::pop()
{
  if (!_pushed) do
  {
    _token.read_from((ifstream&) *this);
    if (_token[0] == '/' && _token[1] == '/')
    {
      line();
      _token.cut(0);
    }
  } while (_token.empty() && good() && !eof());

  _pushed = FALSE;
  _token.upper();
  _key = _token.left(2);

  return _token;
}

// @doc EXTERNAL

// @mfunc Ritorna la linea letta dal file
//
// @rdesc Ritorna una stringa contenente la linea letta dal file
TString& TScanner::line(
  char eol)  //@parm Carattere fino al quale leggere la riga (default EOL)
{
  do
  {
    if (!_pushed) _token.cut(0);
    getline(_tmp.get_buffer(), _tmp.size(), eol);
    _line++;
    _token <<  _tmp;
    _token.trim();
  } while (_token.empty() && good() && !eof());

  _pushed = FALSE;

  return _token;
}

const TString& TScanner::string()
{
  if (!_pushed)
  {
    char c;
    while (isspace(c = get()));
    if (string_start(c))
    {
      getline(_tmp.get_buffer(), _tmp.size(), match(c));
      _token = _tmp;
    }
    else
    {
      error_box("Stringa non trovata: riga ignorata");
      _token.cut(0);
    }
  }

  _pushed = FALSE;
  return _token;
}

void TScanner::rectangle(short& left, short& top, short& right, short& bottom)
{
  left = integer();
  top = integer();
  right  = integer();
  bottom = integer();
}

int TScanner::integer()
{
  int i = atoi(pop());
  if (i == 0 && !isdigit(_token[0])) push();
  return i;
}

double TScanner::number()
{
  double d = atof(pop());
  if (d == 0.0 && !isdigit(_token[0])) push();
  return d;
}


const TString& TScanner::operand()
{
  if (!_pushed)
  {
    char c;
    while (isspace(c = get()));
    putback(c);
    if (string_start(c))
      return string();
    else
      return pop();
  }

  _pushed = FALSE;
  return _token;
}

void TScanner::push(const char* s)
{
  CHECK(!_pushed, "You are pushing back two values to the parser");

  if (s != NULL) _token = s;
  _pushed = TRUE;
}

// Certified 100%
// @doc EXTERNAL
// @mfunc Va al paragrafo <p name>
// @rdesc Ritorna TRUE se il paragrafo esiste
bool TScanner::paragraph(const char* name)
{
  TString256 p = name;
  if (p[0] != '[')
  {
    p.insert("[", 0);
    p << ']';
  }  

  clear();// resetta eof
  seekg(0L);
  while (line().not_empty())
  {
    if (token() == p) 
      return TRUE;
  }

  return FALSE;
}