/*
 *  This file is part of the KDE libraries
 *  Copyright (C) 2001 Thiago Macieira <thiago.macieira@kdemail.net>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library 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
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this library; see the file COPYING.LIB.  If not, write to
 *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *  Boston, MA 02110-1301, USA.
 */

#include "config.h"

#include <string.h>

#include <qptrlist.h>
#include <qcstring.h>
#include "kbufferedio.h"

/**
 * @section impldetails Implementation Details
 *
 * The KBufferedIO class has two purposes: first, it defines an API on how
 * that classes providing buffered I/O should provide. Next, it implements on
 * top of that API a generic buffering, that should suffice for most cases.
 *
 * The buffering implemented consists of two separate buffer areas, one for
 * the input (or read) buffer, and one for the output (or write) buffer. Each
 * of those buffers is implemented through a QList of QByteArrays instead of
 * simply QByteArrays. The idea is that, instead of having one large, contiguous
 * buffer area, we have several small ones. Even though this could be seen as
 * a waste of memory, it makes our life easier, because we can just append a new
 * QByteArray to the list and not have to worry with copying the rest of the
 * buffer, should we need to expand.
 *
 * This way, we have the capability of unlimited buffering, which can grow to
 * the extent of available memory.
 *
 * For each buffer, we provide three kinds of functions, available as protected
 * members: consume, feed and size. The size functions calculate the current
 * size of the buffer, by adding each individual QByteArray size. The feed
 * functions are used by the I/O functions that receive data from somewhere,
 * i.e., from the system, in the case of the input buffer, and from the user,
 * in the case of the output buffer. These two functions are used to give
 * the buffers more data. And the consume functions are used by the functions
 * that send out data (to the system, for the write buffer, and to the user,
 * for the read buffer).
 *
 * Note that for your own implementation, you can have your readBlock function
 * merely call consumeReadBuffer, similarly to peekBlock. As for
 * the writeBlock function, you'd call feedWriteBuffer.
 *
 * Now, the function receiving data from the system will need to simply call
 * feedReadBuffer, much in the same way of unreadBlock. The tricky part is
 * for the output function. We do not provide a member function that copies
 * data from the output buffer into another buffer for sending. We believe that
 * would be a waste of resources and CPU time, since you'd have to allocate
 * that buffer, copy data into it and then call the OS, which will likely just
 * copy data out of it.
 *
 * Instead, we found it better to leave it to you to access outBuf member
 * variable directly and use the buffers there. Should you want to copy that
 * into a larger buffer before sending, that's up to you.
 *
 * Both buffers work in the same way: they're an "array" of buffers, each
 * concatenated to the other. All data in all buffers is valid data, except
 * for the first QByteArray, whose valid data starts at inBufIndex/outBufIndex
 * bytes from the start. That is, the data starts in the first QByteArray buffer
 * that many bytes from the start and goes on contiguously until the last
 * QByteArray. This has been decided like that because we didn't want to
 * create a new QByteArray of the remaining bytes in the first buffer, after
 * a consume operation, because that could take some time. It is faster
 * this way, although not really easy.
 *
 * If you want to take a look at an implementation of a buffered I/O class,
 * refer to KExtendedSocket's source code.
 */

// constructor
KBufferedIO::KBufferedIO() :
  inBufIndex(0), outBufIndex(0)
{
  inBuf.setAutoDelete(true);
  outBuf.setAutoDelete(true);
}

// destructor
KBufferedIO::~KBufferedIO()
{
}

// sets the buffer sizes
// this implementation doesn't support setting the buffer sizes
// if any parameter is different than -1 or -2, fail
bool KBufferedIO::setBufferSize(int rsize, int wsize /* = -2 */)
{
  if (wsize != -2 && wsize != -1)
    return false;
  if (rsize != -2 && rsize != -1)
    return false;

  return true;
}

int KBufferedIO::bytesAvailable() const
{
  return readBufferSize();
}

int KBufferedIO::bytesToWrite() const
{
  return writeBufferSize();
}

// This function will scan the read buffer for a '\n'
bool KBufferedIO::canReadLine() const
{
  if (bytesAvailable() == 0)
    return false;		// no new line in here

  QByteArray* buf;

  // scan each QByteArray for the occurrence of '\n'
  QPtrList<QByteArray> &buflist = ((KBufferedIO*)this)->inBuf;
  buf = buflist.first();
  char *p = buf->data() + inBufIndex;
  int n = buf->size() - inBufIndex;
  while (buf != NULL)
    {
      while (n--)
	if (*p++ == '\n')
	  return true;
      buf = buflist.next();
      if (buf != NULL)
	{
	  p = buf->data();
	  n = buf->size();
	}
    }

  return false;			// no new line found
}

// unreads the current data
// that is, writes into the read buffer, at the beginning
int KBufferedIO::unreadBlock(const char *data, uint len)
{
  return feedReadBuffer(len, data, true);
}

//
// protected member functions
//

unsigned KBufferedIO::consumeReadBuffer(unsigned nbytes, char *destbuffer, bool discard)
{
  {
    register unsigned u = readBufferSize();
    if (nbytes > u)
      nbytes = u;		// we can't consume more than there is
  }

  QByteArray *buf;
  unsigned copied = 0;
  unsigned index = inBufIndex;

  buf = inBuf.first();
  while (nbytes && buf)
    {
      // should we copy it all?
      unsigned to_copy = buf->size() - index;
      if (to_copy > nbytes)
	to_copy = nbytes;

      if (destbuffer)
	memcpy(destbuffer + copied, buf->data() + index, to_copy);
      nbytes -= to_copy;
      copied += to_copy;

      if (buf->size() - index > to_copy)
	{
	  index += to_copy;
	  break;	// we aren't copying everything, that means that's
			// all the user wants
	}
      else
	{
	  index = 0;
	  if (discard)
	    {
	      inBuf.remove();
	      buf = inBuf.first();
	    }
	  else
	    buf = inBuf.next();
	}
    }

  if (discard)
    inBufIndex = index;

  return copied;
}

void KBufferedIO::consumeWriteBuffer(unsigned nbytes)
{
  QByteArray *buf = outBuf.first();
  if (buf == NULL)
    return;			// nothing to consume

  if (nbytes < buf->size() - outBufIndex)
    // we want to consume less than there is in the first buffer
    outBufIndex += nbytes;
  else
    {
      nbytes -= buf->size() - outBufIndex;
      outBufIndex = 0;
      outBuf.remove();

      while ((buf = outBuf.current()) != NULL)
	if (buf->size() <= nbytes)
	  {
	    nbytes -= buf->size();
	    outBuf.remove();
	  }
	else
	  {
	    outBufIndex = nbytes;
	    break;
	  }
    }
}

unsigned KBufferedIO::feedReadBuffer(unsigned nbytes, const char *buffer, bool atBeginning)
{
  if (nbytes == 0)
    return 0;

  QByteArray *a = new QByteArray(nbytes);
  a->duplicate(buffer, nbytes);

  if (atBeginning)
    inBuf.prepend(a);
  else
    inBuf.append(a);

  return nbytes;
}

unsigned KBufferedIO::feedWriteBuffer(unsigned nbytes, const char *buffer)
{
  if (nbytes == 0)
    return 0;

  QByteArray *a = new QByteArray(nbytes);
  a->duplicate(buffer, nbytes);
  outBuf.append(a);
  return nbytes;
}

unsigned KBufferedIO::readBufferSize() const
{
  unsigned count = 0;
  QByteArray *buf = ((KBufferedIO*)this)->inBuf.first();
  while (buf != NULL)
    {
      count += buf->size();
      buf = ((KBufferedIO*)this)->inBuf.next();
    }

  return count - inBufIndex;
}

unsigned KBufferedIO::writeBufferSize() const
{
  unsigned count = 0;
  QByteArray *buf = ((KBufferedIO*)this)->outBuf.first();
  while (buf != NULL)
    {
      count += buf->size();
      buf = (const_cast<KBufferedIO*>(this))->outBuf.next();
    }

  return count - outBufIndex;
}

void KBufferedIO::virtual_hook( int id, void* data )
{ KAsyncIO::virtual_hook( id, data ); }

#include "kbufferedio.moc"
