#include <ctype.h>
#include <stdlib.h>
#include <utility.h>

#include <scanner.h>

HIDDEN const char* strlwr (const char* str)
{
  for (char* s = __tmp_string; *str; s++, str++) *s = tolower(*str);
  *s = '\0';
  return __tmp_string;
}

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

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

TScanner::TScanner(const char* filename)
: _pushed(FALSE), _line(0),
  _token(128), _key(2), _buffer(1024*16)
{         
  setbuf((char*)(const char*)_buffer, _buffer.size());
  open(filename);
  if (bad()) 
    fatal_box("Impossibile aprire %s", filename);
}

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 = "";
    getline(__tmp_string, sizeof(__tmp_string), eol);
    _line++;
    _token <<  __tmp_string;
    _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_string, sizeof(__tmp_string), match(c));
      _token = __tmp_string;
    }
    else
    {
      error_box("Stringa non trovata: riga ignorata");
      _token.cut(0);
    }
  }

  _pushed = FALSE;
  return _token;
}

void TScanner::rectangle(RCT& rect)
{
  rect.left = integer();
  rect.top = integer();
  rect.right  = integer();
  rect.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;
}

bool TScanner::paragraph(const char* name)
{
  TString p;
  p << '[' << name << ']';

  seekg(0L);

  while (line().not_empty())
    if (token() == p) return TRUE;

  return FALSE;
}