// ---------------------------------------------------------------------------
// - Cursor.cpp                                                              -
// - standard object library - character buffer class implementation         -
// ---------------------------------------------------------------------------
// - This program is free software;  you can redistribute it  and/or  modify -
// - it provided that this copyright notice is kept intact.                  -
// -                                                                         -
// - This program  is  distributed in  the hope  that it will be useful, but -
// - without  any  warranty;  without  even   the   implied    warranty   of -
// - merchantability or fitness for a particular purpose.  In no event shall -
// - the copyright holder be liable for any  direct, indirect, incidental or -
// - special damages arising in any way out of the use of this software.     -
// ---------------------------------------------------------------------------
// - copyright (c) 1999-2006 amaury darsch                                   -
// ---------------------------------------------------------------------------

#include "Ascii.hpp"
#include "Cursor.hpp"
#include "Unicode.hpp"

namespace afnix {

  // -------------------------------------------------------------------------
  // - class section                                                         -
  // -------------------------------------------------------------------------

  // create a new cursor

  Cursor::Cursor (void) {
    d_cursor = 0;
    d_insert = true;
    d_prtlen = 0;
  }

  // create a new cursor with a predefined size

  Cursor::Cursor (const long size) : Strbuf (size) {
    d_cursor = 0;
    d_insert = true;
    d_prtlen = 0;
  }

  // create a new cursor and initialize it with a c string

  Cursor::Cursor (const char* value) : Strbuf (value) {
    d_cursor = d_length;
    d_insert = true;
    d_prtlen = 0;
  }

  // create a new cursor and initialize it with a string

  Cursor::Cursor (const String& value) : Strbuf (value) {
    d_cursor = d_length;
    d_insert = true;
    d_prtlen = 0;
  }
  
  // return the class name

  String Cursor::repr (void) const {
    return "Cursor";
  }

  // reset this cursor
  
  void Cursor::reset (void) {
    wrlock ();
    Strbuf::reset ();
    d_cursor = 0;
    d_prompt = "";
    d_prtlen = 0;
    unlock ();
  }

  // clear this cursor but do not change the prompt
  
  void Cursor::clear (void) {
    wrlock ();
    d_length = 0;
    d_cursor = 0;
    unlock ();
  }

  // return the relative cursor position
  
  long Cursor::getrel (void) const {
    rdlock ();
    long result = d_cursor;
    unlock ();
    return result;
  }

  // return the absolute cursor position
  
  long Cursor::getabs (void) const {
    rdlock ();
    long result = d_cursor + d_prtlen;
    unlock ();
    return result;
  }

  // return the remaining distance

  long Cursor::getrem (void) const {
    rdlock ();
    long result = d_length - d_cursor;
    unlock ();
    return result;
  }

  // return true if the cursor is at bol

  bool Cursor::isbol (void) const {
    rdlock ();
    bool result = (d_cursor == 0);
    unlock ();
    return result;
  }

  // return true if the cursor is at eol

  bool Cursor::iseol (void) const {
    rdlock ();
    bool result = (d_cursor == d_length);
    unlock ();
    return result;
  }

  // return true if the cursor is in the last position
  
  bool Cursor::islst (void) const {
    rdlock ();
    // this test works only with some characters
    if (d_length == 0) {
      unlock ();
      return false;
    }
    // must be at the last character
    bool result = (d_cursor == (d_length - 1));
    unlock ();
    return result;
  }

  // return true if the character can be deleted at cursor position

  bool Cursor::isdel (void) const {
    rdlock ();
    if ((d_length == 0) || (d_cursor == d_length)) {
      unlock ();
      return false;
    }
    unlock ();
    return true;
  }

  // set the cursor prompt 

  void Cursor::setprt (const String& prt) {
    wrlock ();
    reset ();
    d_prompt = prt;
    d_prtlen = prt.length ();
    unlock ();
  }

  // get the cursor prompt

  String Cursor::getprt (void) const {
    rdlock ();
    String result = d_prompt;
    unlock();
    return result;
  }

  // get the prompt length

  long Cursor::prtlen (void) const {
    rdlock ();
    long result = d_prtlen;
    unlock();
    return result;
  }

  // add a unicode character in this cursor
  
  void Cursor::add (const t_quad value) {
    wrlock ();
    // check for overflow
    if (d_length == d_size) resize (d_size * 2);
    // if we are at the end - we simply add the character and move the cursor
    if (d_cursor == d_length) {
      p_buffer[d_length++] = value;
      d_cursor = d_length;
      unlock ();
      return;
    }
    // if we are in insert mode - we need to shift the buffer
    // prior the insertion else put the character in place
    if (d_insert == true) {
      for (long i = d_length-1; i >= d_cursor; i--) {
	p_buffer[i+1] = p_buffer[i];
      }
      p_buffer[d_cursor] =  value;
      d_length++;
      d_cursor++;
    } else {
      p_buffer[d_cursor] = value;
      d_cursor++;
    }
    unlock ();
  }

  // remove a character at the cursor (delete mode)
  
  bool Cursor::chdel (void) {
    wrlock ();
    // do nothing if empty or cursor is at end
    if ((d_length == 0) || (d_cursor == d_length)) {
      unlock ();
      return false;
    }
    // move all characters in position
    for (long i = d_cursor+1; i < d_length; i++) {
      p_buffer[i-1] = p_buffer[i];
    }
    // update length before return
    d_length--;
    unlock ();
    return true;
  }
  
  // return true if the cursor is moved one character left
  
  bool Cursor::movel (void) {
    wrlock ();
    // do nothing if empty or at the beginning
    if ((d_length == 0) || (d_cursor == 0)) {
      unlock ();
      return false;
    }
    // update cursor position
    d_cursor--;
    unlock ();
    return true;
  }
    // return true if the cursor is moved one character right
  
  bool Cursor::mover (void) {
    wrlock ();
    // do nothing if empty or at the end
    if ((d_length == 0) || (d_cursor == d_length)) {
      unlock ();
      return false;
    }
    // update cursor position
    d_cursor++;
    unlock ();
    return true;
  }
  
  // move the cursor to the beginning of the buffer
  
  long Cursor::setcb (void) {
    wrlock ();
    long result = d_cursor;
    d_cursor    = 0;
    unlock ();
    return result;
  }
  // move the cursor to the end of the buffer

  long Cursor::setce (void) {
    wrlock ();
    long result = d_length - d_cursor;
    d_cursor    = d_length;
    unlock ();
    return result;
  }

  // set the insert mode flag
  
  void Cursor::setim (const bool mode) {
    wrlock ();
    d_insert = mode;
    unlock ();
  }
  
  // return the full cursor line

  String Cursor::toline (void) const {
    rdlock ();
    try {
      String result = d_prompt + tostring ();
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return a substring from the cursor to the end

  String Cursor::substr (void) const {
    rdlock ();
    // create a temporary buffer to hold the characters
    long len = d_length - d_cursor;
    t_quad* buf = new t_quad[len+1];
    // build the result string
    try {
      for (long i = 0; i < len; i++) buf[i] = p_buffer[i+d_cursor];
      buf[len] = nilq;
      // create the result string and clean the buffer
      String result = buf;
      delete [] buf;
      unlock ();
      return result;
    } catch (...) {
      delete [] buf;
      unlock ();
      throw;
    }
  }

  // return a substring from the next cursor to the end

  String Cursor::delstr (void) const {
    rdlock ();
    if (d_cursor == d_length) {
      String result;
      unlock ();
      return result;
    }
    // create a temporary buffer to hold the characters
    long len = d_length - d_cursor - 1;
    t_quad* buf = new t_quad[len+1];
    // build the result string
    try {
      for (long i = 0; i < len; i++) buf[i] = p_buffer[i+d_cursor+1];
      buf[len] = nilq;
      String result = buf;
      delete [] buf;
      unlock ();
      return result;
    } catch (...) {
      delete [] buf;
      unlock ();
      throw;
    }
  }
}
