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

This is a data accumulator for the CES system. I'd have used a 
 regular array, but I thought this would be more expandable
 should I make ANOTHER structure change later. 
Its still unknown if this will be the level that has the 
 concurency locks, cause I havn't even thought about
 how this will be threaded.
 
 One of the ideas here is to make this thing so that its internal 
   functionality can be completely overhauled without changing 
   the way that its called by other objects.

 This thing is supposed to store data, and provide for easy 
   manipulation of said data. This SHOULD be able to handle 
   binary data just fine.
   

Methods are sumarized as follows:
  long   GetSize( void );                how big is it?
  long   ReadN(  long position, char * buffer, long size ); read bytes
  long   SetN( long destStart, long destSize, char * src, long srcSize); insert/delete content. automatically resizes accumulator. 
 
  -- internal methods --
  int    ReSize( long newSize);              raw resize op.
  long   Insert( long position, long size);   position specific resize
  long   Remove( long position, long size);   position specific unresize

 Dont use tab characters in this code.
  Dont use tab characters in this code.
   Dont use tab characters in this code.
    Dont use tab characters in this code.
     Dont use tab characters in this code.
      Dont use tab characters in this code.
       Dont use tab characters in this code.
        Dont use tab characters in this code.
         Dont use tab characters in this code.
          thanks.
 
 Creation Dan, <Dec 28/2001>
 added resize. Dan <jan ??/2002>
 added insert and remove. Dan <feb 18/2002>
 added adjustsize. Dan <feb 19/2002>
 removed local class def. Dan <mar 5/2002>
 minor overhaul on size tracking. Dan, <mar 8/2002>
 major overhaul. removed several sizing classes in preference of 
    united content/resize method. remaining methods are private Dan. 
    attempted optimizations of insert / delete code <jul 5/2002>
 rewritten to use block allocation. moorcr0  <2002 july 06>
 added SerialNumber. Dan. <Nov 24 2002>

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

#include <stdlib.h>
#include <string.h>
#include "cestypes.h" // MIN
#include "accumulator2.h" // class definition

#include <stdio.h> //for printf

//#include <always_exclude.h>


/*****************************************
*
* Initilize contents
*  By default the buffer has one byte
*
*****************************************/
Accumulator::Accumulator ( void ) {
  blockCount   = 1;
  contents     = new char[BLOCK*blockCount];
  contentSize  = 1;
  SerialNumber = 0;
}

/****************************************
 *
 *  standard deconstructor
 *
 ***************************************/
Accumulator::~Accumulator ( void ) {
   delete[] contents;
}

/*****************************************
*
*  Get the serial number, this is used to 
*    know if the buffer changed while you 
*    weren't looking
* 
*****************************************/

ASerial_t Accumulator::GetSerialNumber( void ){
  return SerialNumber;
}


/**********************************************
*
*  Return the Current size of the accumulator,
*   result in bytes.
*
**********************************************/

// AOffset_t Accumulator::GetSize( void )

/********************************************************************
*
* Read N bytes of the accumulator. The read 
*  and write methods exist to enable 
*  this code to be concurrent.
*  
*  In:
*    position:  The First character to start reading from (included)
*    buffer  :  The location of a buffer to store the result.
*    size    :  The number of characters to read.
*
*  Out:
*    The Number of characters actually read. (This may 
*     come up short if the buffers end was reached.)
*
* notes:
*
*   - should this be able to handle negitive sizes?
*
*******************************************************************/
AOffset_t Accumulator::ReadN(  AOffset_t position, char * buffer, AOffset_t size ) {

  AOffset_t safeSize = MIN(position + size, GetSize()) - position; 
  
  //safety check.
  if (( position > GetSize() )|( position < 0 )) { 
    return 0;
  }   
  
  // I dont want to limit this to strings, hence memcpy
  memcpy(buffer, contents+position, safeSize);

  return safeSize;
}


/*************************************************************************
*
* Insert/delete content. automatically resizes accumulator.
*  
* In:
*   destStart: First position of the accumulators contents to replace.
*   destSize:  Number of accumulator "bytes" to be affected by "paste".
*   src:       Data to use for the contents of the "paste".
*   srcSize:   Size of src.
*
* Out:
*   The number of characters written, should match srcSize if there
*      were no problems
*
**************************************************************************/
AOffset_t Accumulator::SetN( AOffset_t destStart, AOffset_t destSize, char * src, AOffset_t srcSize ) 
{
   AOffset_t memDelta;
   //printf("accumaltor::setN Param start:%ld size:%ld src:%s srcSize%ld\n" , 
//destStart, destSize, src, srcSize);
   //sanity check, its 1:30 am, I must not be sane...
   if ( !WITHIN(0, GetSize(), destStart) ) {
     return 0;
   }  

   if ((destSize < 0) | (srcSize < 0)) {
     return 0;
   }
   //sanity check, you can only replace content up to the end
   //  of the buffer, but not past it

   destSize = MIN(GetSize()-destStart, destSize); 

   // sanity check, only YOU can prevent buffer overuns.

   memDelta = srcSize - destSize ;

   //printf("accumulator: delta size is %ld\n", memDelta);
 
   if(memDelta >0)
   {
      if ( contentSize + memDelta > blockCount *BLOCK)
      {
        //we need memory
        reSize(  ((contentSize + memDelta) /BLOCK)+1);
      }
      shift(destStart,memDelta);
   }
   else if (memDelta <0)
   {
      shift(destStart,memDelta);
      if(contentSize +memDelta < (blockCount/2 *BLOCK))
      {
         reSize(blockCount/2);
      }
   }
   //printf("accumulator: resize done, copying contents\n");
   //printf("accumulator: contents+destStart %x, src %s, srcSize %ld \n", 
   //(contents+destStart), src, srcSize);
   memcpy( contents+destStart, src, srcSize);    // write content
   SerialNumber++;
   return srcSize;
}


/****************************************************
 *
 *  Resize the accumulator
 * This will resize that array to specified size
 *  @param   newBlockSize  is the new buffer size in blocks
 *  @return  1 success    0 bad
 *
 ***************************************************/
CESError_t Accumulator::reSize(int64_t newBlockSize)
{
  char * temp = new char[newBlockSize * BLOCK];
  memcpy(temp, contents, contentSize);
  delete[] contents;
  contents   = temp;
  temp       = 0;   
  blockCount = newBlockSize;
  return NoError;
}


/*****************************************************
 * 
 *  Shift contents of the array, will need to be changed later
 *    since O(n) time here.
 *    
 *  IN: 
 *   offset: the first character to be effected
 *   delta:  the number of characters after offset to 
 *      be effected. a negitive number 'removes' delta,
 *      a positive number 'adds' delta
 *
 *  OUT:
 *   dosn't mean anything yet, ignore
 *
 *****************************************************/
CESError_t Accumulator::shift(AOffset_t offset, AOffset_t delta)
{
  if (delta ==0)
    return NoError;
  
  if (delta < 0)  { //contracting (shrink)
    char * src  = contents + offset + (- delta);
    char * dest = contents + offset;
    
    // fold back the post data
    for (; src <= (contents + GetSize()); src++) {
      *dest = *src;
      dest++;
    }
    
  } else  {  //expanding
    
    char * dest = contents + GetSize() + delta; 
    //ptr math -> to end of buffer
    char * src  = dest - delta;// delta back from end of buffer
    
    for (; src >= (contents + offset);src--)  {
        *dest = *src;
        *dest--;     
      }
  }
  
  contentSize+=delta;
  SerialNumber++;
  return NoError;

}







