// Filename: streamWrapper.cxx
// Created by:  drose (11Nov08)
//
////////////////////////////////////////////////////////////////////
//
// 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."
//
////////////////////////////////////////////////////////////////////

#include "streamWrapper.h"

////////////////////////////////////////////////////////////////////
//     Function: IStreamWrapper::Destructor
//       Access: Published
//  Description: 
////////////////////////////////////////////////////////////////////
IStreamWrapper::
~IStreamWrapper() {
  if (_owns_pointer) {
    // For some reason--compiler bug in gcc 3.2?--explicitly deleting
    // the stream pointer does not call the appropriate global delete
    // function; instead apparently calling the system delete
    // function.  So we call the delete function by hand instead.
#if !defined(WIN32_VC) && !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW)
    _istream->~istream();
    (*global_operator_delete)(_istream);
#else
    delete _istream;
#endif
  }
}

////////////////////////////////////////////////////////////////////
//     Function: IStreamWrapper::read
//       Access: Public
//  Description: Atomically reads a number of bytes from the stream,
//               without error detection.  If fewer bytes than
//               requested are read, quietly fills the remaining bytes
//               with 0.
////////////////////////////////////////////////////////////////////
void IStreamWrapper::
read(char *buffer, streamsize num_bytes) {
  acquire();
  _istream->clear();
  _istream->read(buffer, num_bytes);
  streamsize read_bytes = _istream->gcount();
  while (read_bytes < num_bytes) {
    // Fewer bytes than expected were read.  Maybe more will be
    // coming later.
    release();
    thread_yield();
    acquire();

    _istream->read(buffer + read_bytes, num_bytes - read_bytes);
    streamsize this_read_bytes = _istream->gcount();
    assert(this_read_bytes <= num_bytes - read_bytes);
    read_bytes += this_read_bytes;

    if (this_read_bytes == 0) {
      // No, don't expect any more.
      memset(buffer + read_bytes, 0, num_bytes - read_bytes);
      break;
    }
  }
  assert(read_bytes <= num_bytes);
  release();
}

////////////////////////////////////////////////////////////////////
//     Function: IStreamWrapper::read
//       Access: Public
//  Description: Atomically reads a number of bytes from the stream.
//               Returns the number of bytes actually read.
////////////////////////////////////////////////////////////////////
void IStreamWrapper::
read(char *buffer, streamsize num_bytes, streamsize &read_bytes) {
  acquire();
  _istream->clear();
  _istream->read(buffer, num_bytes);
  read_bytes = _istream->gcount();
  assert(read_bytes <= num_bytes);
  release();
}

////////////////////////////////////////////////////////////////////
//     Function: IStreamWrapper::read
//       Access: Public
//  Description: Atomically reads a number of bytes from the stream.
//               Returns the number of bytes actually read, and
//               whether an eof condition was detected by the
//               operation.
////////////////////////////////////////////////////////////////////
void IStreamWrapper::
read(char *buffer, streamsize num_bytes, streamsize &read_bytes, bool &eof) {
  acquire();
  _istream->clear();
  _istream->read(buffer, num_bytes);
  read_bytes = _istream->gcount();
  assert(read_bytes <= num_bytes);
  eof = _istream->eof() || _istream->fail();
  release();
}

////////////////////////////////////////////////////////////////////
//     Function: IStreamWrapper::seek_read
//       Access: Public
//  Description: Atomically seeks to a particular offset from the
//               beginning of the file, and reads a number of bytes
//               from the stream.  Returns the number of bytes
//               actually read, and whether an eof condition was
//               detected by the operation.
////////////////////////////////////////////////////////////////////
void IStreamWrapper::
seek_read(streamsize pos, char *buffer, streamsize num_bytes, 
          streamsize &read_bytes, bool &eof) {
  acquire();
  _istream->clear();
  _istream->seekg(pos);
  _istream->read(buffer, num_bytes);
  read_bytes = _istream->gcount();
  assert(read_bytes <= num_bytes);
  eof = _istream->eof() || _istream->fail();
  release();
}

////////////////////////////////////////////////////////////////////
//     Function: IStreamWrapper::seek_gpos_eof
//       Access: Public
//  Description: Atomically seeks to EOF and returns the gpos there;
//               that is, returns the file size.  Note that the EOF
//               might have been moved in another thread by the time
//               this method returns.
////////////////////////////////////////////////////////////////////
streamsize IStreamWrapper::
seek_gpos_eof() {
  streamsize pos;
  acquire();
  _istream->seekg(0, ios::end);
  pos = _istream->tellg();
  release();

  return pos;
}

////////////////////////////////////////////////////////////////////
//     Function: OStreamWrapper::Destructor
//       Access: Published
//  Description: 
////////////////////////////////////////////////////////////////////
OStreamWrapper::
~OStreamWrapper() {
  if (_owns_pointer) {
    // For some reason--compiler bug in gcc 3.2?--explicitly deleting
    // the stream pointer does not call the appropriate global delete
    // function; instead apparently calling the system delete
    // function.  So we call the delete function by hand instead.
#if !defined(WIN32_VC) && !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW)
    _ostream->~ostream();
    (*global_operator_delete)(_ostream);
#else
    delete _ostream;
#endif
  }
}

////////////////////////////////////////////////////////////////////
//     Function: OStreamWrapper::write
//       Access: Public
//  Description: Atomically writes a number of bytes to the stream,
//               without error detection.
////////////////////////////////////////////////////////////////////
void OStreamWrapper::
write(const char *buffer, streamsize num_bytes) {
  acquire();
  _ostream->write(buffer, num_bytes);
  release();
}

////////////////////////////////////////////////////////////////////
//     Function: OStreamWrapper::read
//       Access: Public
//  Description: Atomically writes a number of bytes to the stream.
//               Returns whether a failure condition was detected by
//               the operation.
////////////////////////////////////////////////////////////////////
void OStreamWrapper::
write(const char *buffer, streamsize num_bytes, bool &fail) {
  acquire();
  _ostream->clear();
  _ostream->write(buffer, num_bytes);
  fail = _ostream->fail();
  release();
}

////////////////////////////////////////////////////////////////////
//     Function: OStreamWrapper::seek_write
//       Access: Public
//  Description: Atomically seeks to a particular offset from the
//               beginning of the file, and writes a number of bytes
//               to the stream.  Returns whether a failure condition
//               was detected by the operation.
////////////////////////////////////////////////////////////////////
void OStreamWrapper::
seek_write(streamsize pos, const char *buffer, streamsize num_bytes, 
           bool &fail) {
  acquire();
  _ostream->clear();
  _ostream->seekp(pos);

#ifdef WIN32_VC
  if (_ostream->fail() && _stringstream_hack && pos == 0) {
    // Ignore an unsuccessful attempt to seekp(0) if
    // _stringstream_hack is true.
    _ostream->clear();
  }
#endif // WIN32_VC

  _ostream->write(buffer, num_bytes);
  fail = _ostream->fail();
  release();
}

////////////////////////////////////////////////////////////////////
//     Function: OStreamWrapper::seek_eof_write
//       Access: Public
//  Description: Atomically seeks to the end of the file, and writes a
//               number of bytes to the stream.  Returns whether a
//               failure condition was detected by the operation.
////////////////////////////////////////////////////////////////////
void OStreamWrapper::
seek_eof_write(const char *buffer, streamsize num_bytes, bool &fail) {
  acquire();
  _ostream->clear();
  _ostream->seekp(0, ios::end);

#ifdef WIN32_VC
  if (_ostream->fail() && _stringstream_hack) {
    // Ignore an unsuccessful attempt to seekp(0) if
    // _stringstream_hack is true.
    _ostream->clear();
  }
#endif // WIN32_VC

  _ostream->write(buffer, num_bytes);
  fail = _ostream->fail();
  release();
}

////////////////////////////////////////////////////////////////////
//     Function: OStreamWrapper::seek_ppos_eof
//       Access: Public
//  Description: Atomically seeks to EOF and returns the ppos there;
//               that is, returns the file size.  Note that the EOF
//               might have been moved in another thread by the time
//               this method returns.
////////////////////////////////////////////////////////////////////
streamsize OStreamWrapper::
seek_ppos_eof() {
  streamsize pos;
  acquire();
  _ostream->seekp(0, ios::end);

#ifdef WIN32_VC
  if (_ostream->fail() && _stringstream_hack) {
    // Ignore an unsuccessful attempt to seekp(0) if
    // _stringstream_hack is true.
    _ostream->clear();
    release();
    return 0;
  }
#endif // WIN32_VC

  pos = _ostream->tellp();
  release();

  return pos;
}

////////////////////////////////////////////////////////////////////
//     Function: StreamWrapper::Destructor
//       Access: Published
//  Description: 
////////////////////////////////////////////////////////////////////
StreamWrapper::
~StreamWrapper() {
  if (_owns_pointer) {
    // For some reason--compiler bug in gcc 3.2?--explicitly deleting
    // the stream pointer does not call the appropriate global delete
    // function; instead apparently calling the system delete
    // function.  So we call the delete function by hand instead.
#if !defined(WIN32_VC) && !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW)
    _iostream->~iostream();
    (*global_operator_delete)(_iostream);
#else
    delete _iostream;
#endif
  }
}
