/* Copyright (c) 1997-2004
   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.
*/

#ifndef _POLYMAKE_POLYBUF_H
#define _POLYMAKE_POLYBUF_H "$Project: polymake $$Id: Polybuf.h,v 1.3 2004/09/20 14:19:07 gawrilow Exp $"

#if defined(__GNUC__) && __GNUC_MINOR__<3 && !defined(__APPLE__) && !defined(__COMO__)
#pragma interface
#endif

#include <socketstream.h>
#include <sys/types.h>
#include <Poly.h>
#include <cctype>

namespace pm {
namespace client {

class streambuf : public std::socketbuf {
protected:
   std::string name;
   int last_nl, lines;
   char *end_of_seek;

public:
   streambuf(int fd) : std::socketbuf(fd),
		       last_nl(0), lines(0), end_of_seek(0) { }

   typedef std::char_traits<char> ctraits;
   typedef ctraits::int_type int_type;

   void start_input();
   void clear_input();
   void finish_output();
   bool alive();
   void check_exception();

   int fd() const { return _fd; }

   int find_char_forward(char c, int offset=0);
   // ??? int find_string_forward(const char *c, int len, int offset=0);
   int seek_forward(int offset);
   int count_char(char c) const;
   int skip_ws();
   int next_ws(int offset=0, bool report_eof=true);
   int next_non_ws(int offset=0);
   int matching_brace (char opening, char closing, int offset=0);
   void skip_all();
   int_type underflow();
   int get_string(std::string&, char delim);

   void send_cmd(int id, const char *cmd, bool immed=true);
   void send_cmd(int id, const char *cmd, const char *arg, bool immed=true);
   void send_cmd(int id, const char *cmd, const char *arg, va_list rest);

   friend class Object;
   friend class streambuf_holder;
   friend class Object::istream_germ;
   friend class istream_common;
};

inline
streambuf::int_type streambuf::seek_forward (int offset)
{
   const int_type eof_char=ctraits::eof();
   if (gptr()+offset >= egptr() && underflow()==eof_char)
      return eof_char;
   return gptr()[offset];
}

inline
int streambuf::next_ws(int offset, bool report_eof)
{
   const int_type eof_char=ctraits::eof();
   for (int_type c; (c=seek_forward(offset))!=eof_char; ++offset)
      if (isspace(c)) return offset;
   return report_eof ? -1 : offset;
}

inline
int streambuf::next_non_ws(int offset)
{
   const int_type eof_char=ctraits::eof();
   for (int_type c; (c=seek_forward(offset))!=eof_char; ++offset)
      if (!isspace(c)) return offset;
   return -1;
}

inline
int streambuf::skip_ws()
{
   int i=next_non_ws();
   if (i<0) {
      skip_all();
      return -1;
   } else {
      gbump(i);
      return 0;
   }
}

inline
void streambuf::skip_all()
{
   gbump(egptr()-gptr());
}

inline
int streambuf::find_char_forward (char c, int offset)
{
   const int_type eof_char=ctraits::eof();
   if (seek_forward(offset) != eof_char) {
      do {
	 if (char *found=(char*)memchr(gptr()+offset, c, egptr()-(gptr()+offset)))
	    return found-gptr();
	 offset=egptr()-gptr();
      } while (underflow()!=eof_char);
   }
   return -1;
}

inline
int streambuf::count_char (char c) const
{
   char *start=gptr(), *end=egptr();
   int cnt=0;
   while ((start=(char*)memchr(start, c, end-start)) != 0) {
      ++cnt; ++start;
   }
   return cnt;
}

/* ???
inline
int streambuf::find_string_forward (const char *c, int len, int offset)
{
   const int_type eof_char=ctraits::eof();
   while ((offset=find_char_forward(c[0],offset)) >= 0  &&
          seek_forward(offset+len-1) != eof_char) {
      if (!memcmp(gptr()+offset, c, len)) return offset;
      ++offset;
   }
   return -1;
}
??? */

class streambuf_holder {
   friend class Object;

   static streambuf* get() { return inst()->buf; }

   int new_object(const char *object_arg, bool exists, const char *options);
   void delete_object(int id);

   ~streambuf_holder();
   static streambuf_holder* inst();

   bool client_mode() const { return server==0; }
protected:
   streambuf_holder() : buf(0), server(0), object_cnt(-1), server_options(0) { }

   void set_options(const char*);

   streambuf *buf;
   pid_t server;
   int object_cnt;
   const char* server_options;
};
} } // end namespace pm::client

#endif // _POLYMAKE_POLYBUF_H

// Local Variables:
// mode:C++
// End:
