/* Copyright (c) 1997-2005
   Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Berlin, Germany)
   http://www.math.tu-berlin.de/polymake,  mailto:polymake@math.tu-berlin.de

   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, or (at your option) any
   later version: http://www.gnu.org/licenses/gpl.txt.

   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.
*/

#ident "$Project: polymake $$Id: PlainParser.cc,v 1.2 2005/07/28 19:20:00 gawrilow Exp $"

#include <CharBuffer.h>
#include <PlainParser.h>
#include <Rational.h>
#include <socketstream.h>

namespace pm {

int CharBuffer::matching_brace (std::streambuf *_buf, char opening, char closing, int offset)
{
   CharBuffer *buf=static_cast<CharBuffer*>(_buf);
   int level=1;
   int i_op=find_char_forward(buf,opening,offset), i_cl=find_char_forward(buf,closing,offset);
   while (i_cl>=0)
      if (i_op<0 || i_cl<i_op) {
	 if (--level==0) break;
	 i_cl=find_char_forward(buf,closing,i_cl+1);
      } else {
	 ++level;
	 i_op=find_char_forward(buf,opening,i_op+1);
      }
   return i_cl;
}

int CharBuffer::get_string(std::streambuf *_buf, std::string& s, char delim)
{
   CharBuffer *buf=static_cast<CharBuffer*>(_buf);
   int end=-1;
   if (delim!=0) {
      end=find_char_forward(buf,delim);
   } else if (skip_ws(buf) >= 0) {
      end=next_ws(buf,0,false);
   }
   if (end>=0) {
      s.assign(buf->gptr(),end);
      buf->gbump(end+(delim!=0));
   }
   return end;
}

int PlainParserCommon::count_words()
{
   std::streambuf *mybuf=is->rdbuf();
   if (CharBuffer::skip_ws(mybuf)<0) return 0;
   int cnt=0, offset=0;
   do {
      offset=CharBuffer::next_ws(mybuf,offset+1);
      ++cnt;
   } while (offset>0  &&
	    CharBuffer::seek_forward(mybuf,offset) != '\n'  && 
	    (offset=CharBuffer::next_non_ws(mybuf,offset+1)) > 0);
   return cnt;
}

int PlainParserCommon::count_lines()
{
   return CharBuffer::count_lines(is->rdbuf());
}

int PlainParserCommon::count_all_lines()
{
   return dynamic_cast<std::streambuf_with_input_width*>(is->rdbuf())->lines();
}

int PlainParserCommon::count_braced(char opening, char closing)
{
   std::streambuf *mybuf=is->rdbuf();
   if (CharBuffer::skip_ws(mybuf)<0) return 0;
   int cnt=0, offset=0;
   do {
      if (CharBuffer::get_ptr(mybuf)[offset] != opening) {
	 is->setstate(is->failbit);
	 return 0;
      }
      offset=CharBuffer::matching_brace(mybuf, opening, closing, offset+1);
      if (offset<0) {
	 is->setstate(is->failbit);
	 return 0;
      }
      ++cnt;
   } while ((offset=CharBuffer::next_non_ws(mybuf,offset+1)) > 0);
   return cnt;
}

int PlainParserCommon::count_leading(char c)
{
   std::streambuf *mybuf=is->rdbuf();
   int cnt=0, offset=-1;
   for (;;) {
      if ((offset=CharBuffer::next_non_ws(mybuf,offset+1)) < 0) {
	 is->setstate(is->failbit);
	 return 0;
      }
      if (CharBuffer::get_ptr(mybuf)[offset] != c) break;
      ++cnt;
   }
   return cnt;
}

bool PlainParserCommon::at_end()
{
   return CharBuffer::skip_ws(is->rdbuf())<0;
}

void PlainParserCommon::skip_item()
{
   std::streambuf *mybuf=is->rdbuf();
   if (CharBuffer::skip_ws(mybuf)<0) return;

   int offset=0;
   switch (mybuf->sbumpc()) {
   case '<':
      offset=CharBuffer::matching_brace(mybuf, '<', '>');  break;
   case '(':
      offset=CharBuffer::matching_brace(mybuf, '(', ')');  break;
   case '{':
      offset=CharBuffer::matching_brace(mybuf, '{', '}');  break;
   default:
      offset=CharBuffer::next_ws(mybuf,0,false);
   }
   if (offset<0)
      CharBuffer::skip_all(mybuf);
   else
      CharBuffer::get_bump(mybuf,offset+1);
}

void PlainParserCommon::skip_rest()
{
   std::streambuf *mybuf=is->rdbuf();
   CharBuffer::skip_all(mybuf);
   is->setstate(std::ios::eofbit);
}

char* PlainParserCommon::set_temp_range(char opening, char closing)
{
   std::streambuf *mybuf=is->rdbuf();

   if (CharBuffer::skip_ws(mybuf)<0) {
      is->setstate(closing == '\n' ? is->eofbit : is->failbit | is->eofbit);
      return 0;
   }

   int offset;
   if (closing == '\n') {
      offset=CharBuffer::find_char_forward(mybuf,'\n');
      if (offset<0) return 0;
      ++offset;
   } else {
      if (*CharBuffer::get_ptr(mybuf) != opening) {
	 is->setstate(is->failbit);
	 return 0;
      }
      CharBuffer::get_bump(mybuf,1);
      offset=CharBuffer::matching_brace(mybuf, opening, closing);
      if (offset<0) {
	 is->setstate(is->failbit);
	 return 0;
      }
   }

   return set_input_range(offset);
}

char* PlainParserCommon::set_input_range(int offset)
{
   std::streambuf_with_input_width *mybuf=static_cast<std::streambuf_with_input_width*>(is->rdbuf());
   char *egptr=CharBuffer::end_get_ptr(mybuf);
   if (CharBuffer::get_input_limit(mybuf)) {
      CharBuffer::set_end_get_ptr(mybuf, CharBuffer::get_ptr(mybuf)+offset);
   } else {
      mybuf->set_input_width(offset);
   }
   return egptr;
}

void PlainParserCommon::restore_input_range(char *egptr)
{
   std::streambuf_with_input_width *mybuf=static_cast<std::streambuf_with_input_width*>(is->rdbuf());
   if (egptr==CharBuffer::get_input_limit(mybuf)) {
      mybuf->reset_input_width(false);
   } else {
      CharBuffer::set_end_get_ptr(mybuf, egptr);
   }
}

void PlainParserCommon::discard_range(char closing)
{
   std::streambuf *mybuf=is->rdbuf();
   if (is->eof())
      is->clear();
   else if (CharBuffer::skip_ws(mybuf) >= 0 ||
	    CharBuffer::get_ptr(mybuf) != CharBuffer::end_get_ptr(mybuf))
      is->setstate(is->failbit);
   if (is->good() && closing != '\n')
      CharBuffer::get_bump(mybuf,1);
}

void PlainParserCommon::skip_temp_range(char *egptr)
{
   std::streambuf_with_input_width *mybuf=static_cast<std::streambuf_with_input_width*>(is->rdbuf());
   char *next=CharBuffer::end_get_ptr(mybuf)+1;
   if (egptr==CharBuffer::get_input_limit(mybuf)) {
      mybuf->reset_input_width(false);
      CharBuffer::get_bump(mybuf, next-CharBuffer::get_ptr(mybuf));
   } else {
      CharBuffer::set_get_and_end_ptr(mybuf, next, egptr);
   }
}

char* PlainParserCommon::save_read_pos()
{
   return CharBuffer::get_ptr(is->rdbuf());
}

void PlainParserCommon::restore_read_pos(char *pos)
{
   std::streambuf_with_input_width *mybuf=static_cast<std::streambuf_with_input_width*>(is->rdbuf());
   mybuf->rewind(CharBuffer::get_ptr(mybuf)-pos);
}

void PlainParserCommon::get_scalar(Rational& x)
{
   static std::string text;
   if (*is >> text) {
      if (text.find_first_of(".eE") != std::string::npos) {
	 x=atof(text.c_str());
      } else {
	 x.set(text.c_str());
      }
   }
}

void PlainParserCommon::get_scalar(double& x)
{
   static std::string text;
   if (*is >> text) {
      if (text.find('/') != std::string::npos) {
	 x=Rational(text.c_str());
      } else {
	 char *end;
	 x=strtod(text.c_str(),&end);
	 if (*end) is->setstate(is->failbit);
      }
   }
}

void PlainParserCommon::get_string (std::string& s, char delim)
{
   if (CharBuffer::get_string(is->rdbuf(),s,delim) < 0)
      is->setstate(is->eofbit | is->failbit);
}

} // end namespace pm
