// Filename: socketStream.I
// Created by:  drose (15Oct02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University.  All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license.  You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////
//     Function: SSReader::receive_datagram
//       Access: Published
//  Description: Receives a datagram over the socket by expecting a
//               little-endian 16-bit byte count as a prefix.  If the
//               socket stream is non-blocking, may return false if
//               the data is not available; otherwise, returns false
//               only if the socket closes.
////////////////////////////////////////////////////////////////////
INLINE bool SSReader::
receive_datagram(Datagram &dg) {
#ifdef SIMULATE_NETWORK_DELAY
  if (_delay_active) {
    while (do_receive_datagram(dg)) {
      delay_datagram(dg);
    }
    return get_delayed(dg);
  }

  // Pick up any datagrams that might have been leftover in the queue
  // when we disabled the delay.
  if (get_delayed(dg)) {
    return true;
  }
#endif  // SIMULATE_NETWORK_DELAY

  return do_receive_datagram(dg);
}

////////////////////////////////////////////////////////////////////
//     Function: SSReader::set_tcp_header_size
//       Access: Published
//  Description: Sets the header size for datagrams.  At the present,
//               legal values for this are 0, 2, or 4; this specifies
//               the number of bytes to use encode the datagram length
//               at the start of each TCP datagram.  Sender and
//               receiver must independently agree on this.
////////////////////////////////////////////////////////////////////
INLINE void SSReader::
set_tcp_header_size(int tcp_header_size) {
  nassertv(tcp_header_size == 0 || tcp_header_size == 2 || tcp_header_size == 4);
  _tcp_header_size = tcp_header_size;
}

////////////////////////////////////////////////////////////////////
//     Function: SSReader::get_tcp_header_size
//       Access: Published
//  Description: Returns the header size for datagrams.  See
//               set_tcp_header_size().
////////////////////////////////////////////////////////////////////
INLINE int SSReader::
get_tcp_header_size() const {
  return _tcp_header_size;
}

////////////////////////////////////////////////////////////////////
//     Function: SSWriter::set_collect_tcp
//       Access: Published
//  Description: Enables or disables "collect-tcp" mode.  In this
//               mode, individual TCP packets are not sent
//               immediately, but rather they are collected together
//               and accumulated to be sent periodically as one larger
//               TCP packet.  This cuts down on overhead from the
//               TCP/IP protocol, especially if many small packets
//               need to be sent on the same connection, but it
//               introduces additional latency (since packets must be
//               held before they can be sent).
//
//               See set_collect_tcp_interval() to specify the
//               interval of time for which to hold packets before
//               sending them.
//
//               If you enable this mode, you may also need to
//               periodically call consider_flush() to flush the queue
//               if no packets have been sent recently.
////////////////////////////////////////////////////////////////////
INLINE void SSWriter::
set_collect_tcp(bool collect_tcp) {
  _collect_tcp = collect_tcp;
}

////////////////////////////////////////////////////////////////////
//     Function: SSWriter::get_collect_tcp
//       Access: Published
//  Description: Returns the current setting of "collect-tcp" mode.
//               See set_collect_tcp().
////////////////////////////////////////////////////////////////////
INLINE bool SSWriter::
get_collect_tcp() const {
  return _collect_tcp;
}

////////////////////////////////////////////////////////////////////
//     Function: SSWriter::set_collect_tcp_interval
//       Access: Published
//  Description: Specifies the interval in time, in seconds, for which
//               to hold TCP packets before sending all of the
//               recently received packets at once.  This only has
//               meaning if "collect-tcp" mode is enabled; see
//               set_collect_tcp().
////////////////////////////////////////////////////////////////////
INLINE void SSWriter::
set_collect_tcp_interval(double interval) {
  _collect_tcp_interval = interval;
}

////////////////////////////////////////////////////////////////////
//     Function: SSWriter::get_collect_tcp_interval
//       Access: Published
//  Description: Returns the interval in time, in seconds, for which
//               to hold TCP packets before sending all of the
//               recently received packets at once.  This only has
//               meaning if "collect-tcp" mode is enabled; see
//               set_collect_tcp().
////////////////////////////////////////////////////////////////////
INLINE double SSWriter::
get_collect_tcp_interval() const {
  return _collect_tcp_interval;
}

////////////////////////////////////////////////////////////////////
//     Function: SSWriter::set_tcp_header_size
//       Access: Published
//  Description: Sets the header size for datagrams.  At the present,
//               legal values for this are 0, 2, or 4; this specifies
//               the number of bytes to use encode the datagram length
//               at the start of each TCP datagram.  Sender and
//               receiver must independently agree on this.
////////////////////////////////////////////////////////////////////
INLINE void SSWriter::
set_tcp_header_size(int tcp_header_size) {
  nassertv(tcp_header_size == 0 || tcp_header_size == 2 || tcp_header_size == 4);
  _tcp_header_size = tcp_header_size;
}

////////////////////////////////////////////////////////////////////
//     Function: SSWriter::get_tcp_header_size
//       Access: Published
//  Description: Returns the header size for datagrams.  See
//               set_tcp_header_size().
////////////////////////////////////////////////////////////////////
INLINE int SSWriter::
get_tcp_header_size() const {
  return _tcp_header_size;
}

////////////////////////////////////////////////////////////////////
//     Function: SSWriter::consider_flush
//       Access: Published
//  Description: Sends the most recently queued data if enough time
//               has elapsed.  This only has meaning if
//               set_collect_tcp() has been set to true.
////////////////////////////////////////////////////////////////////
INLINE bool SSWriter::
consider_flush() {
  if (!_collect_tcp) {
    return flush();

  } else {
    double elapsed = 
      TrueClock::get_global_ptr()->get_short_time() - _queued_data_start;
    // If the elapsed time is negative, someone must have reset the
    // clock back, so just go ahead and flush.
    if (elapsed < 0.0 || elapsed >= _collect_tcp_interval) {
      return flush();
    }
  }

  return true;
}

////////////////////////////////////////////////////////////////////
//     Function: SSWriter::flush
//       Access: Published
//  Description: Sends the most recently queued data now.  This only
//               has meaning if set_collect_tcp() has been set to
//               true.
////////////////////////////////////////////////////////////////////
INLINE bool SSWriter::
flush() {
  _ostream->flush();
  _queued_data_start = TrueClock::get_global_ptr()->get_short_time();
  return !is_closed();
}

////////////////////////////////////////////////////////////////////
//     Function: ISocketStream::Constructor
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
INLINE ISocketStream::
ISocketStream(streambuf *buf) : istream(buf), SSReader(this) {
  _channel = NULL;
}

////////////////////////////////////////////////////////////////////
//     Function: OSocketStream::Constructor
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
INLINE OSocketStream::
OSocketStream(streambuf *buf) : ostream(buf), SSWriter(this) {
}

////////////////////////////////////////////////////////////////////
//     Function: OSocketStream::flush
//       Access: Published
//  Description: Sends the most recently queued data now.  This only
//               has meaning if set_collect_tcp() has been set to
//               true.
////////////////////////////////////////////////////////////////////
INLINE bool OSocketStream::
flush() {
  return SSWriter::flush();
}

////////////////////////////////////////////////////////////////////
//     Function: SocketStream::Constructor
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
INLINE SocketStream::
SocketStream(streambuf *buf) : iostream(buf), SSReader(this), SSWriter(this) {
}

////////////////////////////////////////////////////////////////////
//     Function: SocketStream::set_tcp_header_size
//       Access: Published
//  Description: Sets the header size for datagrams.  At the present,
//               legal values for this are 0, 2, or 4; this specifies
//               the number of bytes to use encode the datagram length
//               at the start of each TCP datagram.  Sender and
//               receiver must independently agree on this.
////////////////////////////////////////////////////////////////////
INLINE void SocketStream::
set_tcp_header_size(int tcp_header_size) {
  SSReader::set_tcp_header_size(tcp_header_size);
  SSWriter::set_tcp_header_size(tcp_header_size);
}

////////////////////////////////////////////////////////////////////
//     Function: SocketStream::get_tcp_header_size
//       Access: Published
//  Description: Returns the header size for datagrams.  See
//               set_tcp_header_size().
////////////////////////////////////////////////////////////////////
INLINE int SocketStream::
get_tcp_header_size() const {
  return SSReader::get_tcp_header_size();
}

////////////////////////////////////////////////////////////////////
//     Function: SocketStream::flush
//       Access: Published
//  Description: Sends the most recently queued data now.  This only
//               has meaning if set_collect_tcp() has been set to
//               true.
////////////////////////////////////////////////////////////////////
INLINE bool SocketStream::
flush() {
  return SSWriter::flush();
}
