#include <genmap.h>
#include <genfiles.h>
#include <strstream.h>
#include "gfiles.h"

class LinesSeq : public GenSeq {
  GenSeq *base;
public:
  LinesSeq(GenSeq *b) { base = b; }
  virtual int sizeof_file() const;
  virtual void open(GenFile*, OpenFlags flags=0);
  virtual size_t length();
  Root *index(index_t i);
};

Root *LinesSeq::index(index_t i)
{
  if (i < 0)
    i += length() + 1;
  size_t cur_lineno = 0;
  ITERATOR(iter, base);
  int line_non_empty = 0;
  while (cur_lineno < i)
    {
      Root* v = iter.next();
      if (v == Missing)
	return Missing;
      if (v == CCharToChar('\n'))
	cur_lineno++;
    }
  Root* v = iter.next();
  if (v == Missing)
    return Missing;
  ostrstream dest;
  long save_print_readable = print_readable;
  print_readable = 0;
  for (;;)
    {
      if (v == Missing || v == CCharToChar('\n'))
	break;
      dest << *v;
      v = iter.next();
    }
  print_readable = save_print_readable;
  StringC *str = NewString(dest.pcount(), dest.str());
  dest.freeze(0);
  return str;
}

size_t LinesSeq::length()
{
  size_t len = 0;
  ITERATOR(iter, base);
  int line_non_empty = 0;
  for (;;) {
    Root* v = iter.next();
    if (v == Missing)
      return len + line_non_empty;
    if (v == CCharToChar('\n'))
      len++, line_non_empty = 0;
    else
      line_non_empty = 1;
  }
}

class LinesFile : public GenFile {
  LinesSeq *seq;
  long pos;
public:
  GenFile* base_file() { return (GenFile*)(this+1); }
  LinesFile(LinesSeq *q) { seq = q; pos = 0; }
  void destroy();
  Root *next();
  Root* key();
};

LinesSeq::sizeof_file() const {
  return base->sizeof_file() + sizeof(LinesFile);
}

void LinesSeq::open(GenFile* file, OpenFlags flags)
{
  LinesFile *lfile = (LinesFile*)file;
  CONSTRUCT(lfile, LinesFile, (this));
  base->open(lfile->base_file(), flags);
}

Root* LinesFile::key() { return MakeFixInt(pos); }

Root *LinesFile::next()
{
  if (pos == -1)
    return Missing;
  Root* v = base_file()->next();
  if (v == Missing)
    {
      pos = -1;
      return Missing;
    }
  ostrstream dest;
  long save_print_readable = print_readable;
  print_readable = 0;
  for (;;)
    {
      if (v == Missing || v == CCharToChar('\n'))
	break;
      dest << *v;
      v = base_file()->next();
    }
  pos++;
  print_readable = save_print_readable;
  StringC *str = NewString(dest.pcount(), dest.str());
  dest.freeze(0);
  return str;
}

void LinesFile::destroy()
{
  base_file()->destroy();
}

Root*
MakeLines(Root *seq)
{
  GenSeq* q = Coerce2Sequence(seq);
  return new LinesSeq(q);
}
