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

 This is a 'blockeditor' its job is to provide basic text buffer 
   manipulation functionality. Mainly, to replace a variable size 
   block of text with another (of optionally different size) 
 Other side effects are inserting and removing characters. 
 
   As it stands, this code is missing the ability to effect
    the screen colour of a selected region. It also isn't 
    designed to handle a multiuser environment where your 
    text may move on you.
 
 Dont use tab characters in this source
  Dont use tab characters in this source
   Dont use tab characters in this source
    Dont use tab characters in this source
     Dont use tab characters in this source
      Dont use tab characters in this source
       Dont use tab characters in this source
        Dont use tab characters in this source
         Dont use tab characters in this source
          Thanks.
 
 methods:
  // setup classes
  int       SetAccumulator  ( Accumulator * accum = NULL );
  long      SelectionStart  ( long newstart ) ;
  long      SelectionEnd    ( long newend   ) ;
  long      SelectionStart  ( void ) ;
  long      SelectionEnd    ( void ) ;
  long      SelectionSize   ( void ) ;
  
  // content search
  int       Seek            ( long offset, int whence );
  
  // content modify
  int       Putc            ( char inchar );
  int       Replace         ( char * buff ) ;
  
  // content retrieve 
  char *    Read            ( char * buff, int len );
 
 --================| change log ==============--
 
 Feb 17 2002  Dan Williams, Created. note: NOT CONCURRENT
 [bunch of stuff missing here]
 mar 10 2002  dan, Added prevline method.
 mar 10 2002  dan, Added range checking to seek.
 mar 10 2002  dan, added lineEnd
 nov 29 2002  dan, contentfilter becomes blockeditor, line refferences removed.
 Dec 13 2003: Fixed for gcc 3.3 dan 


notes:  there are problems if the selection start and end positions are
  backwards, e.g. start is after end.

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

#include <stdlib.h>
#include <stdio.h> // SEEK_*
#include <string.h> // strchr
#include "blockeditor.h"
#include "accumulator2.h"
#include "cestypes.h" // MIN


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

  Initialize the thing.

************************************/
BlockEditor::BlockEditor( void ) {

 blockStart = 0;
 blockEnd   = 0;
 accumulator= NULL;

}

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

  Clean up.

************************************/
BlockEditor::~BlockEditor() {

}

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

  Set the accumulator

*************************************/
//int BlockEditor::SetAccumulator ( Accumulator * accum = NULL ) {
int BlockEditor::SetAccumulator ( Accumulator * accum ) {

    accumulator = accum;
    return 0;

}

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

  Set the start and end of the selection 
    block to the requested position

  in:
    offset: how far from the requested position to go
    whence: forward from front, forward from current, or back from end.
    
  out:
    0 if it worked
    1 if it failed
    
  notes:
    whence should actually have values SEEK_NEXTLINE and
      SEEK_PREVLINE, but for some reason I'm hesitant to 
      do that because of them being defined in stdio.h...
  

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

int BlockEditor::Seek( long offset, int whence) {

 if (whence == SEEK_SET) {         // beggining

   if (offset >= 0) {
     if (offset > accumulator->GetSize()) {
       offset = accumulator->GetSize();
     }
   } else {
     offset = 0;
   }
   
 } else if (whence == SEEK_CUR) {  // current position

   if ((blockStart + offset) >= 0) {
     if ((blockStart + offset) <= accumulator->GetSize()) {
       offset = (blockStart + offset);
     } else {
       offset = accumulator->GetSize();
     }
   } else {
     offset = 0;
   }

 } else if (whence == SEEK_END) {  // end

   if ((accumulator->GetSize() - offset - 1) >= 0) {
     if ((accumulator->GetSize() - offset - 1) <= accumulator->GetSize()) {
       offset = (accumulator->GetSize() - offset - 1);
     } else {
       offset = accumulator->GetSize();
     }
   } else {
     offset = 0;
   }
 }

  blockStart = offset;
  blockEnd   = blockStart;

 return 0;

} 


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

 This is the equivilent of Replace, but 
   for a character input level.

  this should be able to return errors...

  this will insert the character, not overwrite,
  unless a block is selected.

*******************************************/
int BlockEditor::Putc(char inchar) {

 // The rediculon consists of a character, followed by an obsurdicon, followed by a zero.
 char rediculonBuffer[3];
 char obsurdiconBuffer[3];
 
 if (SelectionSize() > 1) { // inserting a character is defferent with no block
   blockEnd++;
 }
 
 accumulator->ReadN(  blockEnd, obsurdiconBuffer, 1 ); // we need an obsurdicon to create a radiculon
 
 rediculonBuffer[0] = inchar;              // it is verry important that a rediculon have character
 rediculonBuffer[1] = obsurdiconBuffer[0]; // an obsurdicon,
 rediculonBuffer[2] = '\0';                // and a partridge in a pair tree

  // character overwrites current block, with inchar and 
 Replace(rediculonBuffer);
 
 // cursor moves forward after each character, I mean radiculon,  written
 Seek( SEEK_CUR, 1);

 return 0;
} 


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

 set the start of the selection
 
    There should be a check here to
      make sure that newstart isn't 
      past the end of the buffer.
     
      
************************/ 
//long BlockEditor::SelectionStart( long newstart = -1 ) {
long BlockEditor::SelectionStart( long newstart) {

 if (newstart >= 0) {
     blockStart = newstart;
 }

 return blockStart;

}

long BlockEditor::SelectionStart(void) {
 return blockStart;
}

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

 set the end of the selection
 
    There should be a check here to
      make sure that newend isn't 
      past the end of the buffer.
      
************************/ 
//long BlockEditor::SelectionEnd( long newend = -1) {
long BlockEditor::SelectionEnd( long newend ) {

 if ((newend >= 0) && (newend > blockStart)) {
     blockEnd = newend;
 }

 return blockEnd;

} 

long BlockEditor::SelectionEnd( void ) {
 return blockEnd;
} 

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

   Return the size of the selected
     block.

******************************/
long BlockEditor::SelectionSize( void ) {

  return blockEnd - blockStart + 1;

} 


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

  Read the text contained in the selection.
    The buffer should be atleast as big as the 
    selection size.
  
  In:
    buff    :  The location of a buffer to store the result.
    len     :  The size of the buffer

  Out:
    Copy of the pointer to buff


*******************************************/
char * BlockEditor::Read( char * buff, int len ){
 
 accumulator->ReadN(  blockStart, buff, MIN( SelectionSize(), len ) );
 buff[SelectionSize()] = '\0';
  
 return buff;

} // ok for accum


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

 Replace the selected block with the specified 
   string.

  this should be able to return errors

**********************************************/
int BlockEditor::Replace( char * buff ) {

 long  srcsize;
 long destsize;
 //long diff;

  srcsize = strlen(buff);
 destsize = SelectionSize();
 //    diff = srcsize - destsize;

     //accumulator->AdjustSize ( blockStart, diff );
     //accumulator->WriteN     ( blockStart, buff, srcsize );

     accumulator->SetN(blockStart, destsize, buff, srcsize);

 // adjust for a new selectionsize

 blockEnd = blockStart;

   return 0;
} // ok for accum






