#include <text.h>
#include <fstream.h>
#include <ctype.h>

static char mytmpstr[257];

class _HotSpot : public TObject
{
public:
  //      TArray _spots;  // tokenstrings
  char _bg, _fg;

  _HotSpot (char fg, char bg)
  {
    _fg = fg;
    _bg = bg;
  }
  virtual ~ _HotSpot ()
  {
  }
};

void TTextfile::set_hotspots (char fg, char bg)
{
  _HotSpot *hp = new _HotSpot (fg, bg);
  _hotspots.add (hp);
}

style TTextfile::_trans_style (char ch)
{
  switch (ch)
  {
  case 'r':
    return normal;
    break;
  case 'i':
    return italic;
    break;
  case 'b':
    return bold;
    break;
  case 'u':
    return underlined;
    break;
  case 'o':
    return overstrike;
    break;
  case 'k':
    return smallcaps;
    break;
  default:
    return normal;
    break;
  }
}

void TTextfile::_read_page (long n)
{
  switch (_direction)
  {
  case down:
    _page_start = n;
    break;
  case up:
    _page_start = n + _page_size;
    break;
  case updown:
    _page_start = n - (_page_size / 2l);
    break;
  }

  if (_page_start < 0l)
    _page_start = 0l;
  if ((_page_start + _page_size) > _lines)
    _page_end = _lines - 1;
  else
    _page_end = _page_start + _page_size - 1;

  // zap hotspots
  _spots.destroy ();

  long l = 0l;
  fseek (_index, _page_start * (long) sizeof (long), SEEK_SET);
  if (_page_start != 0l)
    fread (&l, sizeof (long), 1, _index);
  fseek (_instr, l, SEEK_SET);

  for (long i = _page_start; i <= _page_end; i++)
  {
    if (feof (_instr))
      break;
    fgets (mytmpstr, sizeof (mytmpstr), _instr);
    mytmpstr[strlen (mytmpstr) - 1] = '\0';
    TString & ts = (TString &) _page[(int) (i - _page_start)];
    ts = mytmpstr;
    TString hcol (6);
    // find hotspots and compile list                           

    int len = 0;
    const char *cp;
    read_line (i, 0, FALSE);
    while (cp = piece ())
    {
      for (int z = 0; z < _hotspots.items (); z++)
      {
        _HotSpot & hs = (_HotSpot &) _hotspots[z];
        if (hs._fg == get_foreground () && hs._bg == get_background ())
        {
          TToken_string *tts = new TToken_string (50);
          tts->add (i); // line number                        

          tts->add (len);
          tts->add (len + (int) strlen (cp));
          tts->add (cp);
          tts->add (z);
          _spots.add (tts);
          break;
        }
      }
      len += strlen (cp);
    }
  }
}

void TTextfile::read_line (long n, long pos, bool pg)
{
  CHECK (_isopen, "Attempt operation on closed file");
  CHECKD (n >= 0 && n < _lines, "Line not present", n);

  if (pg && !_in_page (n))
    _read_page (n);

  TString *tp = (TString *) _page.objptr (int (n - _page_start));
  if (tp == NULL)
    return;

  const char *sp = (const char *) (*tp);
  _item = 0;
  _line = "";
  int ndx = 0, p = 0;
  bool first = TRUE;
  _cur_line = n;
  char ch;

  int col = ((int) 'w' << 8) | (int) 'n';
  long stl = (long) col << 16;

  while (ch = *sp++)
  {
    if (ch == '@' || (ch == '$' && *(sp) == '['))
    {
      if (!first && p >= pos)
      {
        _styles[_item++] = stl;
        mytmpstr[ndx] = '\0';
        _line.add (mytmpstr);
        ndx = 0;
      }
      while (ch && (ch == '@' || (ch == '$' && *sp == '[')))
      {
        if (ch == '@')  // font style change ? 

        {
          style sss = _trans_style (*sp++);
          if (sss == normal)
            stl = (long) col << 16;
          else
            stl |= (long) sss;
        }
        else if (ch == '$' && *sp == '[')               // color change

        {
          ++sp;         // eat '['

          col = *sp++;
          ++sp;         // eat ','                        

          col |= ((int) (*sp++) << 8);
          ++sp;         // eat ']'

          stl = (stl & 0x0000ffff) | ((long) col << 16);
        }
        ch = *sp++;
      }                 // while

    }
    if (ch && p >= pos)
    {
      first = FALSE;
      mytmpstr[ndx++] = ch;
    }
    p++;
  }
  _styles[_item++] = stl;
  mytmpstr[ndx] = '\0';
  _line.add(mytmpstr);
  _item = 0;
}

const char *TTextfile::line(long j, long pos)
{
  if (_cur_line != j)
    read_line (j);
  *mytmpstr = '\0';
  _line.restart ();
  for (int i = 0; i < _line.items (); i++)
    strcat (mytmpstr, (const char *) _line.get ());
  return strlen (mytmpstr) > (word) pos ? &(mytmpstr[pos]) : "";
}

const char *TTextfile::line_formatted(long j)
{
  if (_cur_line != j)
    read_line (j);
  TString* tp = (TString*)_page.objptr(int(j-_page_start));
  strcpy(mytmpstr, (const char*)(*tp));
  return mytmpstr;
}

long TTextfile::get_attribute (int pos)
{
  long stl = 0;
  if (pos == -1)
  {
    CHECK (_item > 0, "must execute piece() before style()!");
    stl = _styles[_item - 1];
  }
  else
  {
    int x = 0, nd = 0;
    const char *c;
    _line.restart ();
    while (c = _line.get ())
    {
      x += strlen (c);
      stl = _styles[nd++];
      if ((x - 1) >= pos)
        break;
    }
  }
  return stl;
}

int TTextfile::get_style (int pos)
{
  long x = get_attribute (pos);
  return (int) (x & 0x0000ffff);
}

char TTextfile::get_background (int pos)
{
  long x = get_attribute (pos);
  return (char) (x >> 24);
}

char TTextfile::get_foreground (int pos)
{
  long x = get_attribute (pos);
  return (char) ((x >> 16) & 0x000000ff);
}

const char *TTextfile::piece ()
{
  if (_item >= _line.items ())
    return NULL;
  return strcpy (mytmpstr, (const char *) _line.get (_item++));
}

const char *TTextfile::word_at (long x, long y)
{
  CHECK (_isopen, "Attempt operation on closed file");
  TString s (line (y));
  int x2 = 0;

  if (x < s.len ())
  {
    while (isspace (s[(int) x]))
    {
      if (x == (s.len () - 1) && y < (_lines - 1l))
      {
        s = line (++y);
        x = 0l;
      }
      else if (x < (s.len () - 1))
        x++;
      else
        break;
    }
    while (isalnum (s[(int) x]))
      mytmpstr[x2++] = s[(int) x++];
  }
  mytmpstr[x2] = '\0';
  return mytmpstr;
}

bool TTextfile::append (const char *l)
{
  CHECK (_isopen, "Attempt operation on closed file");

  if (!_accept)
    return FALSE;

  fseek (_instr, 0l, SEEK_END);
  fseek (_index, 0l, SEEK_END);
  long cpos = ftell (_instr);
  fprintf (_instr, "%s\n", l);
  fwrite (&cpos, sizeof (long), 1, _index);
  if (ferror (_index) || ferror (_instr))
  {
    error_box ("Errore di scrittura file temporaneo: scrittura interrotta");
    freeze ();
  }
  fflush (_index);
  fflush (_instr);

  _lines++;
  _dirty = TRUE;

  if ((_lines) < (_page_start + _page_size))
  {
    TString *ll = new TString (l);
    _page.add (ll);
    _page_end++;

    int len = 0;
    const char *cp;
    read_line (_lines - 1);
    while (cp = piece ())
    {
      for (int z = 0; z < _hotspots.items (); z++)
      {
        _HotSpot & hs = (_HotSpot &) _hotspots[z];
        if (hs._fg == get_foreground () && hs._bg == get_background ())
        {
          TToken_string *tts = new TToken_string (50);
          tts->add (_lines - 1l);       // line number
          // 

          tts->add (len);
          tts->add (len + (int) strlen (cp));
          tts->add (cp);
          tts->add (z);
          _spots.add (tts);
          break;
        }
      }
      len += strlen (cp);
    }
    return TRUE;
  }
  return FALSE;
}

void TTextfile::close ()
{
  CHECK (_isopen, "Attempt operation on closed file");
  fclose (_instr);
  fclose (_index);
  _instr = _index = NULL;
  _isopen = FALSE;
}

void TTextfile::print ()
{
  CHECK (_isopen, "Attempt operation on closed file");
  warning_box ("Funzione non ancora implementata");
  // TBI istanzia una printer inibendo la scelta di video
  // add all lines (maybe new method: print_txt)
  // print
}

bool TTextfile::write (const char *path, TPoint * from, TPoint * to)
{
  bool ok = FALSE;
  FILE *fp;
  if ((fp = fopen (path, "w")) != NULL)
  {
    ok = TRUE;
    TString256 s;
    long starty = from == NULL ? 0l : from->y;
    int startx = from == NULL ? 0 : (int) from->x;
    long endy = to == NULL ? _lines - 1l : to->y;
    int endx = to == NULL ? -1 : (int) to->x;
    for (long j = starty; j <= endy; j++)
    {
      s = line (j);
      if (j == endy && endx == -1)
        endx = s.len ();

      if (j == starty && j == endy)
        s = s.sub (startx, endx);
      else if (j == starty)
        s = s.mid (startx);
      else if (j == endy)
        s = s.left (endx);

      fprintf (fp, "%s\n", (const char *) s);
    }
    fclose (fp);
  }
  else
    warning_box ("Impossibile scrivere il file %s; scrittura fallita", path);
  return ok;
}

void TTextfile::destroy ()
{
  CHECK (_istemp, "destroy() chiamata su testo permanente!");
  if (_page.items () > 0)
  {
    if (_index)
      fclose (_index);
    if (_instr)
      fclose (_instr);
    remove ((const char *) _filename);
    remove ((const char *) _indname);
    _page_start = _lines = 0l;
    _page_end = _cur_line = -1l;
    _accept = TRUE;
    _instr = fopen (_filename, "a+");
    _indname.temp ();
    _index = fopen (_indname, "w+b");
    if (_index == NULL || _instr == NULL)
    {
      error_box ("Impossibile aprire files temporanei");
      freeze ();
    }
    _isopen = TRUE;
    _page.destroy ();
    _spots.destroy ();
  }
}

TTextfile ::TTextfile (const char *file, int pagesize, direction preferred):
_page_size (pagesize), _page (pagesize), _filename (file), _lines (0l),
_index (NULL), _page_start (0l), _page_end (-1l), _direction (preferred),
_dirty (FALSE), _istemp (FALSE), _item (0), _line (256), _cur_line (-1),
_hotspots (4), _accept (TRUE)
{
  // open file & build index
  if (file == NULL || *file <= ' ')
  {
    _filename.temp ();
    _istemp = TRUE;
  }

  _instr = fopen (_filename, "a+");
  _indname.temp ();
  _index = fopen (_indname, "w+b");

  if (_index == NULL || _instr == NULL)
  {
    yesnofatal_box ("Impossibile aprire files temporanei");
    freeze ();
  }
  if (file != NULL)
    while (!feof (_instr))
    {
      const long l = ftell (_instr);
      fwrite (&l, sizeof (long), 1, _index);
      if (ferror (_index) || ferror (_instr))
      {
        error_box ("Errore di scrittura file temporaneo: scrittura interrotta");
        freeze ();
      }
      fgets (mytmpstr, sizeof (mytmpstr), _instr);
      _lines++;
    }
  _isopen = TRUE;
}

TTextfile::~TTextfile ()
{
  if (_index)
    fclose (_index);
  if (_instr)
    fclose (_instr);
  if (_istemp)
    remove ((const char *) _filename);
  remove ((const char *) _indname);
}
