/***************************************************************************
                          kbuffercursor.cpp  -  description
                             -------------------
    begin                : Don Mai 29 2003
    copyright            : (C) 2003 by Friedrich W. H. Kossebau
    email                : Friedrich.W.H@Kossebau.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library General Public           *
 *   License version 2 as published by the Free Software Foundation.       *
 *                                                                         *
 ***************************************************************************/


//#include <kdebug.h>

// lib specific
#include "kbufferlayout.h"
#include "kbuffercursor.h"

using namespace KHE;

TDEBufferCursor::TDEBufferCursor( const TDEBufferLayout *L )
 : Layout( L ),
   Index( 0 ),
   Coord( 0, 0 ),
   Behind( false ),
   AppendPosEnabled( false )
{
}

TDEBufferCursor::~TDEBufferCursor()
{
}


bool TDEBufferCursor::operator==( const TDEBufferCursor &C ) const
{
  return Index == C.Index && Behind == C.Behind ;
}


void TDEBufferCursor::setAppendPosEnabled( bool APE )
{
  if( AppendPosEnabled == APE )
    return;

  AppendPosEnabled = APE;
  // reposition Cursor
  int Length = Layout->length();
  // behind buffer end, not end of line and not empty?
  if( realIndex() >= Length && Coord.pos() < Layout->noOfBytesPerLine()-1 && Length > 0 )
  {
    if( AppendPosEnabled )
    {
      ++Index;
      Coord.goRight();
      Behind = false;
    }
    else
    {
      --Index;
      Coord.goLeft();
      Behind = true;
    }
  }
}


void TDEBufferCursor::gotoPreviousByte()
{
  if( Behind )
    Behind = false;
  else if( Index > 0 )
  {
    --Index;
    Coord.goCLeft( Layout->noOfBytesPerLine()-1 );
  }
}


void TDEBufferCursor::gotoPreviousByte( int D )
{
  if( Behind )
  {
    --D;
    Behind = false;
  }
  if( D > Index )
  {
    if( Index == 0 )
      return;
    gotoStart();
  }
  gotoIndex( Index - D );
}


void TDEBufferCursor::gotoNextByte()
{
  int Length = Layout->length();

  if( Index < Length )
  {
    if( Index == Length-1 )
      stepToEnd();
    else
    {
      ++Index;
      Coord.goCRight( Layout->noOfBytesPerLine()-1 );
      Behind = false;
    }
  }
}


void TDEBufferCursor::gotoNextByte( int D ) // TODO: think about consistency with gotoNextByte!!!
{
  if( Behind )
  {
    ++D;
    Behind = false;
  }
  // would we end behind the end?
  if( Index+D >= Layout->length() )
    gotoEnd();
  else
    gotoIndex( Index + D );
}


void TDEBufferCursor::gotoNextByteInLine()
{
  int Length = Layout->length();

  if( Index < Length )
  {
    if( Index == Length-1 )
      stepToEnd();
    else
    {
      ++Index;

      if( Coord.pos() < Layout->noOfBytesPerLine()-1 )
        Coord.goRight();
      else
        Behind = true;
    }
  }
}


void TDEBufferCursor::gotoUp()
{
  // can we even go up?
  if( Coord.isBelow(Layout->startLine()) )
  {
    Coord.goUp();
    if( Coord.isPriorInLineThan(Layout->start()) )
    {
      Index = 0;
      Coord.setPos( Layout->startPos() );
      Behind = false;
    }
    else
    {
      Index -= Layout->noOfBytesPerLine();
      if( Behind && !atLineEnd() )
      {
        ++Index;
        Coord.goRight();
        Behind = false;
      }
    }
  }
}


void TDEBufferCursor::gotoDown()
{
  if( Coord.isAbove(Layout->finalLine()) )
  {
    Coord.goDown();
    // behind End?
    if( Coord.isLaterInLineThan(Layout->final()) )
      gotoEnd();
    else
      Index += Layout->noOfBytesPerLine();
  }
}


void TDEBufferCursor::gotoLineStart()
{
  int OldIndex = Index;
  Index = Layout->indexAtLineStart( Coord.line() );
  Coord.goLeft( OldIndex-Index );
  Behind = false;
}


void TDEBufferCursor::gotoLineEnd()
{
  if( Index < Layout->length() )
  {
    int OldIndex = Index;
    Index = Layout->indexAtLineEnd( Coord.line() );
    Coord.goRight( Index-OldIndex );

    stepToEnd();
  }
}


void TDEBufferCursor::gotoStart()
{
  Index = 0;
  Coord = Layout->start();
  Behind = false;
}


void TDEBufferCursor::gotoEnd()
{
  int Length = Layout->length();
  if( Length > 0 )
  {
    Index = Length-1;
    Coord = Layout->final();

    stepToEnd();
  }
  else
    gotoStart();
}


void TDEBufferCursor::gotoCIndex( int i )
{
  if( Layout->length() > 0 )
  {
    Index = Layout->correctIndex( i );
    Coord = Layout->coordOfIndex( Index );
    if( i > Index )
      stepToEnd();
    else
      Behind = false;
  }
  else
    gotoStart();
}


void TDEBufferCursor::gotoCCoord( const TDEBufferCoord &C )
{
  if( Layout->length() > 0 )
  {
    Coord = Layout->correctCoord( C );
    Index = Layout->indexAtCoord( Coord );
    if( C > Coord )
      stepToEnd();
    else
      Behind = false;
  }
  else
    gotoStart();
}


void TDEBufferCursor::stepToEnd()
{
  if( AppendPosEnabled && (Coord.pos() < Layout->noOfBytesPerLine()-1) )
  {
    ++Index;
    Coord.goRight();
    Behind = false;
  }
  else
    Behind = true;
}


void TDEBufferCursor::gotoIndex( int i )
{
  Index = i;
  Coord = Layout->coordOfIndex( Index );
  Behind = false;
}


void TDEBufferCursor::gotoRealIndex()
{
  if( Behind )
  {
    ++Index;
    Coord = Layout->coordOfIndex( Index );
    Behind = false;
  }
}


void TDEBufferCursor::gotoCoord( const TDEBufferCoord &C )
{
  Index = Layout->indexAtCoord( C );
  Coord = C;
  Behind = false;
}


void TDEBufferCursor::updateCoord()
{
  Coord = Layout->coordOfIndex( Index );
}

// page down should be: one page minus one line
// -> if in the very first line page down will put the cursor on the same page into the last line
void TDEBufferCursor::gotoPageUp()
{
  int NoOfLinesPerPage = Layout->noOfLinesPerPage();
  int NewIndex = Index - NoOfLinesPerPage * Layout->noOfBytesPerLine();
  if( NewIndex < 0 )
    gotoStart();
  else
  {
    Index = NewIndex;
    Coord.goUp( NoOfLinesPerPage );
    if( Behind && !atLineEnd() )
    {
      ++Index;
      Coord.goRight();
      Behind = false;
    }
  }
}


void TDEBufferCursor::gotoPageDown()
{
  int NoOfLinesPerPage = Layout->noOfLinesPerPage();
  int NewIndex = Index + NoOfLinesPerPage * Layout->noOfBytesPerLine();
  if( NewIndex >= Layout->length() )
    gotoEnd();
  else
  {
    Index = NewIndex;
    Coord.goDown( NoOfLinesPerPage );
  }
}


int TDEBufferCursor::validIndex()       const { return Index < Layout->length() ? Index : -1; }
int TDEBufferCursor::indexAtLineStart() const { return Layout->indexAtLineStart( Coord.line() ); }
int TDEBufferCursor::indexAtLineEnd()   const { return Layout->indexAtLineEnd( Coord.line() ); }


bool TDEBufferCursor::atStart()     const { return Index == 0; }
bool TDEBufferCursor::atEnd()       const { return Index == Layout->length() - 1; }
bool TDEBufferCursor::atAppendPos() const { return realIndex() >= Layout->length(); }


bool TDEBufferCursor::atLineStart() const { return Layout->atLineStart( Coord ); }
bool TDEBufferCursor::atLineEnd()   const { return Layout->atLineEnd( Coord ); }
