//<copyright>
// 
// Copyright (c) 1994,95
// Institute for Information Processing and Computer Supported New Media (IICM),
// Graz University of Technology, Austria.
// 
//</copyright>

//<file>
//
// File:        lineeditor.C - implementation of class LineEditor
//
// Created:     10 Mar 94   Michael Pichler
//
// Changed:     12 Jan 95   Michael Pichler
//
//
//</file>


#include "lineeditor.h"

#include <hyperg/OS/memory.h>
#include <hyperg/utils/str.h>

#include <string.h>
#include <ctype.h>
#include <iostream.h>


// the LineEditor manages a dynamic character array of size
// bufsize_, which may store strings of length up to bufsize_-1;
// when strings become longer, bufsize is incremented by a fixed amount;
// on construation and when changing the string, bufsize_ is set
// to the minimum size strlen+1 to avoid waste of memory


LineEditor::LineEditor (const char* str)
{
  str_ = 0;
  setString (str);
}


LineEditor::~LineEditor ()
{
  delete str_;
}


void LineEditor::setString (const char* str)
{
  // replace str_ by new string str
  delete str_;
  if (str && *str)
  {
    RString rstr(str);
    rstr.subst("\t", "        ");   // replace tabs with 8 spaces
    len_ = rstr.length();
    bufsize_ = len_ + 1;
    str_ = new char [bufsize_];
    strcpy (str_, rstr.string());
  }
  else
  {
    len_ = 0;
    bufsize_ = 1;
    str_ = new char [bufsize_];
    *str_ = '\0';
  }
}


void LineEditor::insertChar (char c, int pos)
{
  if (pos < 0 || pos > len_)  // pos == len_ means append
    return;

  len_++;

  if (len_ > bufsize_-1)  // enlarge buffer
  {
    bufsize_ += 16;
    char* newstr = new char [bufsize_];
    strcpy (newstr, str_);
    delete str_;
    str_ = newstr;
  }

  char* src = str_ + pos;
  char h;  // save old character
  while (h = *src, *src++ = c, c = h);  // shift back, including '\0', '=' intended
  *src = '\0';
} // insertChar


void LineEditor::insertSubstring (const char* str, int slen, int pos)
{
  if (pos < 0 || pos > len_ || slen <= 0)  // pos == len_ means append
    return;

  int n = len_ - pos + 1;  // no. of chars (including '\0') to shift right

//cerr << "adding substring length " << slen << " to " << len_ << "; new length: " << len_ + slen << endl;
//cerr << "insert substring: <";  cerr.write ((const char*) str, slen);  cerr << ">" << endl;

  len_ += slen;  // new total length

  if (len_ > bufsize_-1)  // enlarge buffer
  {
    bufsize_ = len_ + 16;  // at least len_ + 1
    char* newstr = new char [bufsize_];
    strcpy (newstr, str_);
    delete str_;
    str_ = newstr;
  }

  char* dst = str_ + pos;

  Memory::copy (dst, dst + slen, n);
  // shift to right, overlapping will be handled correct

  strncpy (dst, str, slen);

} // insertSubstring


void LineEditor::overwrite (char c, int pos)
{
  if (pos >= 0 && pos < len_)
    str_ [pos] = c;
}


void LineEditor::deleteChar (int pos)
{
  if (pos >= 0 && pos < len_)
  {
    len_--;
    char* dst = str_ + pos;
    char* src = dst + 1;
    while (*dst++ = *src++);  // move to front, including '\0', '=' intented
  }
}


void LineEditor::deleteSubstring (int from, int behind)
{
  if (behind < from)
  { int temp = behind;
    behind = from;
    from = temp;
  }
  // from <= behind

  if (from >= 0 && behind <= len_ && from < behind)
  {
    len_ -= behind - from;
    char* dst = str_ + from;
    char* src = str_ + behind;
    while (*dst++ = *src++);  // move to front, including '\0', '=' intented
  }
}


// wordLeft
// find word on left side

int LineEditor::wordLeft (int pos) const
{
  const char* str = str_;

  // scan over space
  while (pos && isspace (str [pos-1]))
    pos--;

  // scan over word characters
  while (pos && !isspace (str [pos-1]))
    pos--;

  return pos;
}


int LineEditor::wordRight (int pos) const
{
  const char* str = str_;

  // scan over word characters
  while (pos < len_ && !isspace (str [pos]))
    pos++;

  // scan over space
  while (pos < len_ && isspace (str [pos]))
    pos++;

  return pos;
}


// findWord
// find word boundings
// "word": continous string of non-spaces
// range of result: 0 <= start <= pos <= end <= length

void LineEditor::findWord (int pos, int& start, int& end) const
{
  const char* str = str_;

  start = pos;  // word is also found when pos is right after the word
  while (start && !isspace (str [start - 1]))
    start--;

  end = pos;
  while (end < len_ && !isspace (str [end]))
    end++;
}
