// Example 8-1: The basic_networkbuf class template
#ifndef NETSTREAM_H
#define NETSTREAM_H

// This demonstration does not actually perform network I/O.
// It is merely an example of how to write a custom streambuf.

#include <fstream>
#include <ios>
#include <locale>
#include <streambuf>
#include <string>

template<typename charT, typename traits = ::std::char_traits<char> >
class basic_networkbuf : public ::std::basic_streambuf<charT, traits> {
public:
  typedef charT                     char_type;
  typedef traits                    traits_type;
  typedef typename traits::int_type int_type;
  typedef typename traits::pos_type pos_type;
  typedef typename traits::off_type off_type;

  basic_networkbuf();
  virtual ~basic_networkbuf();

  bool is_connected();
  basic_networkbuf* connect(const char* hostname, int port, ::std::ios_base::openmode mode);
  basic_networkbuf* disconnect();

protected:
  virtual ::std::streamsize showmanyc();
  virtual int_type underflow();
  virtual int_type overflow(int_type c = traits::eof());

  virtual basic_networkbuf* setbuf(char_type* buf, ::std::streamsize size);
  virtual int sync();
  virtual void imbue(const ::std::locale&);

  virtual pos_type seekoff(off_type offset, ::std::ios_base::seekdir dir,
                           ::std::ios_base::openmode);
  virtual pos_type seekpos(pos_type sp, ::std::ios_base::openmode);

private:
  char_type* buffer_;
  ::std::streamsize size_;
  bool ownbuf_;
  static const int DEFAULT_BUFSIZ = 1024;
  ::std::basic_fstream<charT, traits> f;
};

template<typename charT, typename traits>
basic_networkbuf<charT,traits>::basic_networkbuf()
: buffer_(new char_type[DEFAULT_BUFSIZ]), size_(DEFAULT_BUFSIZ), ownbuf_(true)
{
  this->setg(buffer_, buffer_ + size_, buffer_ + size_ - 1);
  this->setp(buffer_, buffer_ + size_);
}

template<typename charT, typename traits>
basic_networkbuf<charT,traits>::~basic_networkbuf()
{
  if (ownbuf_)
    delete[] buffer_;
}

template<typename charT, typename traits>
basic_networkbuf<charT,traits>* basic_networkbuf<charT,traits>::setbuf(char_type* buf, ::std::streamsize size)
{
  if (ownbuf_) {
    ownbuf_ = false;
    delete [] buffer_;
  }
  buffer_ = buf;
  size_ = size;
  this->setg(buffer_, buffer_ + size_, buffer_ + size_);
  this->setp(buffer_, buffer_ + size_ - 1);
  return this;
}

template<typename charT, typename traits>
bool basic_networkbuf<charT,traits>::is_connected()
{
  return f.is_open();
}

template<typename charT, typename traits>
basic_networkbuf<charT,traits>* basic_networkbuf<charT,traits>::connect(const char* hostname, int, ::std::ios_base::openmode mode)
{
  f.open(hostname, mode);
  if (f.good())
    return this;
  else
    return 0;
}

template<typename charT, typename traits>
basic_networkbuf<charT,traits>* basic_networkbuf<charT,traits>::disconnect()
{
  f.close();
  if (f.good())
    return this;
  else
    return 0;
}
template<typename charT, typename traits>
typename basic_networkbuf<charT,traits>::pos_type
basic_networkbuf<charT,traits>::seekoff(off_type offset, ::std::ios_base::seekdir dir, ::std::ios_base::openmode)
{
  if (offset == 0 && dir == ::std::ios_base::cur)
    return f.tellg();
  else
    return static_cast<pos_type>(-1);
}

template<typename charT, typename traits>
typename basic_networkbuf<charT,traits>::pos_type
basic_networkbuf<charT,traits>::seekpos(pos_type sp, ::std::ios_base::openmode)
{
  return static_cast<pos_type>(-1);
}

template<typename charT, typename traits>
::std::streamsize basic_networkbuf<charT,traits>::showmanyc()
{
  return this->egptr() - this->gptr();
}

template<typename charT, typename traits>
typename basic_networkbuf<charT,traits>::int_type
basic_networkbuf<charT,traits>::underflow()
{
  f.read(buffer_, size_);
  this->setg(buffer_, buffer_, buffer_ + f.gcount());
  if (this->egptr() == this->gptr())
    return traits::eof();
  else
    return traits::to_int_type(*this->gptr());
}

template<typename charT, typename traits>
typename basic_networkbuf<charT,traits>::int_type
basic_networkbuf<charT,traits>::overflow(int_type c)
{
  if (c != traits::eof()) {
    *(this->pptr()) = c;
    this->pbump(1);
  }
  f.write(this->pbase(), this->pptr() - this->pbase());
  this->setp(buffer_, buffer_ + size_ - 1);
  return traits::not_eof(c);
}

template<typename charT, typename traits>
int basic_networkbuf<charT,traits>::sync()
{
  overflow(traits::eof());
  return f.sync();
}

template<typename charT, typename traits>
void basic_networkbuf<charT,traits>::imbue(const ::std::locale&)
{}

template<typename charT, typename traits = ::std::char_traits<charT> >
class basic_netstream : public ::std::basic_iostream<charT,traits>
{
public:
  typedef charT                     char_type;
  typedef traits                    traits_type;
  typedef typename traits::int_type int_type;
  typedef typename traits::pos_type pos_type;
  typedef typename traits::off_type off_type;

  basic_netstream(const char* hostname, int port) : ::std::basic_iostream<charT,traits>(0)
  {
    this->init(&sb);
    sb.connect(hostname, port, ::std::ios_base::out | ::std::ios_base::in);
  }
  basic_netstream(): ::std::basic_iostream<charT,traits>(0) { this->init(&sb); }
  ~basic_netstream() { if (is_connected()) disconnect(); }

  void connect(const char* hostname, int port)   { sb.connect(hostname, port, ::std::ios_base::out | ::std::ios_base::in); }
  void disconnect()                              { sb.pubsync(); sb.disconnect(); }
  bool is_connected()                            { return sb.is_connected(); }
private:
  basic_networkbuf<char_type, traits_type> sb;
};

template<typename charT, typename traits = ::std::char_traits<charT> >
class basic_onetstream : public ::std::basic_ostream<charT,traits>
{
public:
  typedef charT                     char_type;
  typedef traits                    traits_type;
  typedef typename traits::int_type int_type;
  typedef typename traits::pos_type pos_type;
  typedef typename traits::off_type off_type;

  basic_onetstream(const char* hostname, int port) : ::std::basic_ostream<charT,traits>(0)
  {
    this->init(&sb);
    sb.connect(hostname, port, ::std::ios_base::out);
  }
  basic_onetstream(): ::std::basic_ostream<charT,traits>(0) { this->init(&sb); }
  ~basic_onetstream() { if (is_connected()) disconnect(); }

  void connect(const char* hostname, int port)   { sb.connect(hostname, port, ::std::ios_base::out); }
  void disconnect()                              { sb.pubsync(); sb.disconnect(); }
  bool is_connected()                            { return sb.is_connected(); }
private:
  basic_networkbuf<char_type, traits_type> sb;
};

template<typename charT, typename traits = ::std::char_traits<charT> >
class basic_inetstream : public ::std::basic_istream<charT,traits>
{
public:
  typedef charT                     char_type;
  typedef traits                    traits_type;
  typedef typename traits::int_type int_type;
  typedef typename traits::pos_type pos_type;
  typedef typename traits::off_type off_type;

  basic_inetstream(const char* hostname, int port) : ::std::basic_istream<charT,traits>(0)
  {
    this->init(&sb);
    sb.connect(hostname, port, ::std::ios_base::in);
  }
  basic_inetstream(): ::std::basic_istream<charT,traits>(0) { this->init(&sb); }
  ~basic_inetstream() { if (is_connected()) disconnect(); }

  void connect(const char* hostname, int port)   { sb.connect(hostname, port, ::std::ios_base::in); }
  void disconnect()                              { sb.disconnect(); }
  bool is_connected()                            { return sb.is_connected(); }
private:
  basic_networkbuf<char_type, traits_type> sb;
};

typedef basic_netstream<char> netstream;
typedef basic_netstream<wchar_t> wnetstream;
typedef basic_inetstream<char> inetstream;
typedef basic_inetstream<wchar_t> winetstream;
typedef basic_onetstream<char> onetstream;
typedef basic_onetstream<wchar_t> wonetstream;

#endif // NETSTREAM_H
