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

 This is a 'contentfilter' 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.
 
 mothods:
  // 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 );
  long      LinePos         ( void ) ;
  ChrOffset LineEnd         ( void ) ;
  ChrOffset NextLine        ( void ) ;
  ChrOffset PrevLine        ( void ) ;
  
  // 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


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

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


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

  Initialize the thing.

************************************/
ContentFilter::ContentFilter( void ) {

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

}

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

  Clean up.

************************************/
ContentFilter::~ContentFilter() {

}

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

  Set the accumulator

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

    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 ContentFilter::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 ContentFilter::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 ContentFilter::SelectionStart( long newstart = -1 ) {

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

 return blockStart;

}

long ContentFilter::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 ContentFilter::SelectionEnd( long newend = -1) {

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

 return blockEnd;

} 

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

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

   Return the size of the selected
     block.

******************************/
long ContentFilter::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 * ContentFilter::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 ContentFilter::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


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

Report the cursor position in a line that the cursor 
  is currently on.

  notes:
  
   - error handling would be a good thing...
   
   \nlsfjdllkfdldsflkjasfjk\n
    ^        ^          
    0        9

***********************************************/
long    ContentFilter::LinePos    ( void ) {
 
  // this is slow, but using the new-legal methods
  
  char buffer[2] = "";
  ChrOffset position;
  ChrOffset retval = 0;
  
  position = blockStart; // start at current position
  
  accumulator->ReadN ( position, buffer, 1 ); // read "1" character
  
  // position >= 0 causes the beggining of file to report the same as \n
  
  while (( buffer[0] != '\n' ) && ( position >= 0 )) { // count back to linefeed or BOF
    retval ++;
    position --;
    accumulator->ReadN ( position, buffer, 1 );
  } // end of while
  
 return( retval );
 
}

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

  Return the position of the last character 
    of the current line. This is measured 
    from blockstart.
    
    \nlsafdlsfjlkfhksg\nfksfiewoiutrkjnsciou\n
         ^           ^
       start      result

*******************************************/
ChrOffset ContentFilter::LineEnd( void ) {

 ChrOffset position;
 int found = 0; 
 ChrOffset dataEnd;
 char buffer[2] = "";

 dataEnd  = accumulator->GetSize();
 position = blockStart + 1;  // first character dosn't count

 while (found == 0) { 
 
   if (position > dataEnd)     // return EOF
     return ( position - 1);
 
   accumulator->ReadN ( position, buffer, 1 ); // read a byte
 
   if ( buffer[0] == '\n' ) 
     found = 1;
   else 
     position++;            // next byte
 
 } // end while

 return ( position - 1) ;

}

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

  Return the position of the first character 
    of the next line.
    
    \nlsafdlsfjlkfhksg\nfksfiewoiutrkjnsciou\n
         ^              ^
       start         result

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

ChrOffset ContentFilter::NextLine( void ) {

 ChrOffset position;
 int found = 0; 
 ChrOffset dataEnd;
 char buffer[2] = "";

 dataEnd  = accumulator->GetSize();
 position = blockStart + 1;  // first character dosn't count

 while (found == 0) { 
 
   if (position > dataEnd) // die if EOF
     return -1;
 
   accumulator->ReadN ( position, buffer, 1 ); // read a byte
 
   if ( buffer[0] == '\n') 
     found = 1;

     position++;            // next byte
 
 } // end while

 return position ;

}

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

  Return the position of the first character 
    of the previous line.
    
    \nlsafdlsfjlkfhksg\nfksfiewoiutrkjnsciou\n
      ^                             ^
     result                        start               

    asdaf\nlsafdlsfjlkfhksg\nfksfiewoiutrkjnsciou\n
    ^             ^
   result         start
   
    \nlsafdlsfjlkfhksg\nfksfiewoiutrkjnsciou\n
    ^             ^  
    result      start

  works by stopping on 2n'd \n or BOF
  
*******************************************/

ChrOffset ContentFilter::PrevLine( void ) {
 // this is slow, but using the new-legal methods
  
  char buffer[2] = "";
  ChrOffset position;
  char lineCount = 0;
  
  position = blockStart + 1; // start at current position (with compensation)
  
  do {
 
    position --;

    accumulator->ReadN ( position, buffer, 1 );
    
    if (buffer[0] == '\n') {
      lineCount++;
    }
    
   } while (( lineCount < 2 ) && ( position != 0 ));  // count back to linefeed or BOF
  
   if (lineCount == 2) { // if we stoped because we found a normal begginig, 
      position++;        //  adjust to first character of that line.
   }
  
 return( position );

}






