/* Copyright (c) 1997-2006
   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_SOCKETSTREAM_H
#define _POLYMAKE_SOCKETSTREAM_H "$Project: polymake $$Id: socketstream.h,v 1.30.2.1 2006/03/14 22:47:01 gawrilow Exp $"

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

#include <iostream>
#include <netinet/in.h>
#include <sys/poll.h>

#if defined(__GNUC__) || defined(__COMO__)

# define PM_SET_BUF_GET_CUR(buf,g,e) (buf)->_M_in_cur=(g), (buf)->_M_in_end=(e)
# define PM_SET_BUF_GET_END(buf,e) (buf)->_M_in_end=(e)
# define PM_SET_BUF_GET_END_OFF(buf,n) (buf)->_M_in_end=(buf)->_M_in_cur + (n)
# define PM_SET_BUF_GET_CUR_END(buf) (buf)->_M_in_cur=(buf)->_M_in_end;

#else

# define PM_SET_BUF_GET_CUR(buf,g,e) (buf)->setg((buf)->eback(), (g), (e))
# define PM_SET_BUF_GET_END(buf,e) (buf)->setg((buf)->eback(), (buf)->gptr(), e)
# define PM_SET_BUF_GET_END_OFF(buf,n) (buf)->setg((buf)->eback(), (buf)->gptr(), (buf)->gptr()+(n))
# define PM_SET_BUF_GET_CUR_END(buf) (buf)->gbump((buf)->egptr()-(buf)->gptr())

#endif

namespace std {

class server_socketbuf;
class socketstream;

class streambuf_with_input_width : public streambuf {
protected:
   char *input_limit;
   streambuf_with_input_width() : input_limit(0) { }

public:
   streambuf_with_input_width(const std::string& src)
      : input_limit(0)
   {
      char *text=const_cast<char*>(src.c_str());
      setg(text, text, text+src.size());
   }

   bool set_input_width(int w);
   void reset_input_width(bool slurfed);

   void rewind(int w)
   {
      if (gptr()-w >= eback())
	 gbump(-w);
   }

   virtual int lines() { return 0; } // to be overloaded
};

class socketbuf : public streambuf_with_input_width {
public:
   socketbuf(int fd) : _fd(fd), _sfd(-1) { init(); }
   socketbuf(in_addr_t addr, int port, int timeout, int retries);
   socketbuf(const char* hostname, int port, int timeout, int retries);
   ~socketbuf();

   void fill(pollfd& pfd) { pfd.fd=_fd; }

   void echo(int n)
   {
      sputn(gptr(), n);
      gbump(n);
   }

   void set_congestible(bool s);

protected:
   socketbuf() { }
   void init();
   void connect(sockaddr_in& sa, int timeout, int retries);

   int try_out(const char* start, int size);
   int sync();
   int_type overflow(int_type c=traits_type::eof());
   int_type underflow();
   streamsize showmanyc();
   int_type pbackfail(int_type c=traits_type::eof());

   int _fd, _sfd, bufsize;
   pollfd my_poll;

   friend class server_socketbuf;
   friend class socketstream;
};

class server_socketbuf : public socketbuf {
public:
   server_socketbuf(int port);
   server_socketbuf(const char* fd_arg);

protected:
   int sync();
   int_type overflow(int_type c=traits_type::eof());
   int_type underflow();
   streamsize showmanyc() { return 0; }
   int_type pbackfail(int_type) { return traits_type::eof(); }

private:
   static socketbuf* start(server_socketbuf*);
};

class istream_with_input_width : public istream {
public:
   istream_with_input_width(const std::string& src)
      : istream(new streambuf_with_input_width(src)) { }

   ~istream_with_input_width() { delete istream::rdbuf(); }

   struct set_w {
      int w;
      mutable istream *s;

      explicit set_w(int w_arg)
	 : w(w_arg), s(0) { }

      void apply(istream& s_arg) const
      {
	 if (static_cast<streambuf_with_input_width*>(s_arg.rdbuf())->set_input_width(w))
	    s=&s_arg;
	 else
	    s_arg.setstate(ios::failbit|ios::eofbit);
      }

      ~set_w()
      {
	 if (s) {
	    static_cast<streambuf_with_input_width*>(s->rdbuf())->reset_input_width(s->good());
	    s->clear(s->rdstate() & ~eofbit);
	 }
      }
   };
};

inline istream_with_input_width::set_w setinputwidth(int w)
{
   return istream_with_input_width::set_w(w);
}

class procstream : public iostream {
protected:
   procstream(socketbuf *buf) : iostream(buf) { }
public:
   socketbuf* rdbuf() const { return static_cast<socketbuf*>(iostream::rdbuf()); }

   void close() { exceptions(goodbit); delete iostream::rdbuf(0); }
   ~procstream() { delete iostream::rdbuf(); }
};

class socketstream : public procstream {
public:
   socketstream(int port=0)
      : procstream(new server_socketbuf(port)) { }

   socketstream(const char* fd_arg)
      : procstream(new server_socketbuf(fd_arg)) { }

   socketstream(const char* hostname, int port, int timeout=0, int retries=0)
      : procstream(new socketbuf(hostname,port,timeout,retries)) { }

   socketstream(in_addr_t addr, int port, int timeout=0, int retries=0)
      : procstream(new socketbuf(addr,port,timeout,retries)) { }

   int port() const;
};


inline
istream& operator>> (istream_with_input_width& in, const istream_with_input_width::set_w& sw)
{
   sw.apply(in);
   return in;
}

inline
iostream& operator>> (procstream& in, const istream_with_input_width::set_w& sw)
{
   sw.apply(in);
   return in;
}

} // end namespace std

#endif // _POLYMAKE_SOCKETSTREAM_H

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