/*******************************************************************

Copyright (c) 2004 Open Mobile Alliance, Ltd.  All Rights Reserved.

This software is made available under the usage and distribution terms 
covered by the Common Public License 1.0 and by using this software you
are bound by the terms and conditions of the same.

EXCEPT AS EXPRESSLY SET FORTH IN THE COMMON PUBLIC LICENSE,
THIS PROGRAM IS PROVIDED ON AN "AS IS" BASIS, 
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, 
ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, 
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
   
Each recipient is solely responsible for determining the 
appropriateness of using and distributing this program and assumes 
all risks associated with its exercise of rights under the Common 
Public License, including but not limited to the risks and 
costs of program errors, compliance with applicable laws, 
damage to or loss of data, programs or equipment, and 
unavailability or interruption of operations.

********************************************************************/
/* Note, debugging output only occurs in this module if DEBUGALL is used. */
#include <buffer.h>
#include <iConstants.h>
#include <stdlib.h>
#include <stdio.h>

#define  BUFFER_SIZE 1024

/*
** Private, common read.
*/
int iobxBufCommonRead( ObxBuffer *buf, void *data, int len, int peek );

/*
** Alloc a new buffer of specified length.
** Returns a newly allocated ObxBuffer or NULL on error.
** NOTE: len is ignored for now
*/
ObxBuffer   *iobxBufNew( int len ) {
	ObxBuffer      *buf;
#ifdef DEBUGALL
   OBXDBGFLOW(("iobxBufNew() entry, len=%d\n", len));
#endif
	
	if ( !(buf = (ObxBuffer *)malloc( sizeof( ObxBuffer ) )) ) {
      return NULL;
   }

#ifdef DEBUGALL
   OBXDBGBUF(("iobxBufNew() malloc, addr=0x%08x, len=%d.\n", buf, sizeof(ObxBuffer) ));
#endif
   buf->buffers = NULL;
	return iobxBufReset( buf );
}

/*
** Free all internal contents and the 'buf' itself.
** Pointer should not be used after calling.
*/
void        iobxBufFree( ObxBuffer *buf ) {
#ifdef DEBUGALL
   OBXDBGFLOW(("iobxBufFree() entry, buf=0x%08x\n", buf));
#endif
	if ( buf ) {
      iobxListFree( buf->buffers );
      free( buf );
   }
}

/*
** Clear internal state, allows buffer to be reused.
** Returns the reset ObxBuffer or NULL on error.
*/
ObxBuffer      *iobxBufReset( ObxBuffer *buf ) {
	void *managedbuf;
#ifdef DEBUGALL
   OBXDBGFLOW(("iobxBufReset() entry, buf=0x%08x\n", buf));
#endif
   if ( !buf ) {
      return NULL;
   }
   if ( buf ) {
      if ( buf->buffers ) {
         iobxListReset( buf->buffers );
      } else {
         if ( !(buf->buffers = iobxListNew()) ) {
            return NULL;
         }
      }
      if ( ( managedbuf = (void *)malloc( BUFFER_SIZE )) ) {
#ifdef DEBUGALL
         OBXDBGBUF(("iobxBufReset() malloc, addr=0x%08x, len=%d.\n", managedbuf, BUFFER_SIZE ));
#endif
         if ( (iobxListAppend( buf->buffers, managedbuf )) ) {
            buf->writeOffset = buf->readOffset = 0;
            buf->writeNode = buf->readNode = buf->buffers->head;
         } else {
            free( managedbuf );
            iobxListFree( buf->buffers );
            return NULL;
         }
      } else {
         iobxListFree( buf->buffers );
         return NULL;
      }
   }
	return buf;
}

/*
** Appends bytes from passed 'data' to the end of the
** buffer.  (copies data from 'data' into buffer).
** Returns 0 on success, !0 on error.
*/
int iobxBufWrite( ObxBuffer *buf, void *data, int len ) {
   int   length;
   int   thisCopy = 0;
   void  *source = data;
	void  *managedbuf;

#ifdef DEBUGALL
   OBXDBGFLOW(("iobxBufWrite() entry, buf=0x%08x\tlen=%d\n", buf, len));
   OBXDBGMEM(("iobxBufWrite()", data, len));
#endif

   if ( !buf ) {
      return -1;
   }
   length = len;
   while ( length > 0 ) {
      thisCopy = min( BUFFER_SIZE - buf->writeOffset, length );
      memcpy( (char *)buf->writeNode->data+buf->writeOffset, source, thisCopy );

      buf->writeOffset += thisCopy;
      (char *)source += thisCopy;
      length -= thisCopy;

      if ( length > 0 ) {
         // Need more space
         if ( ( managedbuf = (void *)malloc( BUFFER_SIZE )) ) {
#ifdef DEBUGALL
            OBXDBGBUF(("iobxBufWrite() malloc, addr=0x%08x, len=%d.\n", managedbuf, BUFFER_SIZE ));
#endif
            if ( (iobxListAppend( buf->buffers, managedbuf )) ) {
               buf->writeNode = buf->buffers->tail;
               buf->writeOffset = 0;
            } else {
               free( managedbuf );
               iobxListFree( buf->buffers );
               return -1;
            }
         } else {
            iobxListFree( buf->buffers );
            return -1;
         }
      }
   }
	return 0;
}

/*
**
*/
int iobxBufRead( ObxBuffer *buf, void *data, int length ) {
#ifdef DEBUGALL
   OBXDBGFLOW(("iobxBufRead() entry, buf=0x%08x\tlen=%d\n", buf, length));
#endif
   return iobxBufCommonRead( buf, data, length, 0 );
}

/*
**
*/
int iobxBufPeek( ObxBuffer *buf, void *data, int length ) {
#ifdef DEBUGALL
   OBXDBGFLOW(("iobxBufPeek() entry, buf=0x%08x\tlen=%d\n", buf, length));
#endif
   return iobxBufCommonRead( buf, data, length, 1 );
}

/*
** Populates 'data' with 'len' bytes from the buffer.  Returns
** the actual number of bytes placed into 'data'.  This value can
** be less than 'len' if no additional bytes were available.
** Handles, peek or no peek.
*/
int iobxBufCommonRead( ObxBuffer *buf, void *data, int length, int peek ) {
   int                  read = 0;
   int                  empty = 0;
   int                  thisCopy = 0;
   void                 *target = data;
   struct iobxListNode  *nodePntr = NULL;

   int                  origReadOffset = buf->readOffset;
   struct iobxListNode  *origReadNode = buf->readNode;

#ifdef DEBUGALL
   OBXDBGFLOW(("iobxBufCommonRead() entry, buf=0x%08x\tdata=0x%08x\tlen=%d\n", buf, data, length));
#endif

   if ( !buf ) {
      return 0;
   }
   while ( !empty && read < length ) {
      thisCopy = min( BUFFER_SIZE - buf->readOffset, length-read );

      memcpy( target, (char *)buf->readNode->data+buf->readOffset, thisCopy );

      (char *)target += thisCopy;
      buf->readOffset += thisCopy;
      read += thisCopy;

      /*
      ** Done?
      */
      if ( read < length  ) {
         /*
         ** Need to advance to next buffer, or we're 'emtpy'.
         ** Messing with the list directly isn't quite kosher.. but it's quicker.
         */
         if ( buf->readNode->next ) {
            /*
            ** There is another buffer, thus, more data.
            */
            nodePntr = buf->readNode;
            buf->readNode = buf->readNode->next;

            if ( !peek ) {
               /*
               ** Free 'read data' buffer
               */
               if ( nodePntr->data ) {
                  free( nodePntr->data );
               }
               free( nodePntr );
            }

            buf->readNode->prev = NULL;
            buf->buffers->head = buf->readNode;

            buf->readOffset = 0;
         } else {
            empty = 1;
         }
      }
   }

   if ( peek ) {
      buf->readOffset = origReadOffset;
      buf->readNode = origReadNode;
   }

   return read;
}


/*
** Amount of unread bytes in buffer.
*/
int iobxBufSize( ObxBuffer *buf ) {
   int size = 0;
   struct iobxListNode  *nodePntr;

#ifdef DEBUGALL
   OBXDBGFLOW(("iobxBufSize() entry, buf=0x%08x\n", buf));
#endif

   if ( !buf ) {
      return 0;
   }
   nodePntr = buf->readNode;
   while ( nodePntr ) {
      if ( nodePntr == buf->readNode ) {
         if ( nodePntr == buf->writeNode ) {
            size += (buf->writeOffset - buf->readOffset);
         } else {
            size += (BUFFER_SIZE - buf->readOffset);
         }
      } else {
         if ( nodePntr == buf->writeNode ) {
            size += buf->writeOffset;
         } else {
            size += BUFFER_SIZE;
         }
      }
      nodePntr = nodePntr->next;
   }
   return size;
}


