#pragma implementation
#include <iostream.h>
#include "genfiles.h"
#include <undobind.h>
#include <exceptions.h>
#include <listprocs.h>
#include "gennum.h"
#include <nullbuf.h>
#include <std.h>
#include "mapping.h"
#include "gfunc.h"
extern "C" int fstat(int, struct stat*);

int GenFile::seek(Root* index)
{
    const Numeric *num = index->numeric();
    if (num == NULL) return -1;
    long i;
    if (!num->getlong(&i)) return -1;
    return seek(i, 0);
}

void GenFile::reset()
{
    if (seek(0, 0) < 0)
	Signal(new GenericCondition("GenFile::reset() failed!"));
}

Root *GenFile::key()
{
     Signal(new UnimplementedOp("GenFile::key", isA()));
     return NULL;
}
void GenFile::put(Root*)
{
     Signal(new UnimplementedOp("GenFile::put", isA()));
}

int GenFile::seek(void *arg, int kind)
{
#if 0
    int newIndex;
    if (kind == 0) newIndex = (int)arg;
    else if (kind == 1) newIndex = (int)current + (int)arg*stride;
    else newIndex = GenLength(mapping) + (int)arg*stride;
    if ((flags & FileUndoSeeks) &&
#ifdef STACK_GROWS_UP
       _Handler_ > handlerAtLastSeek) {
#else
       _Handler_ < handlerAtLastSeek) {
#endif
	 struct UndoSeek *undo = (struct UndoSeek*)
	     GetUndoCommand(sizeof(struct UndoSeek));
	 undo->kind = UndoSeekFlag;
	 undo->file = this;
	 undo->savedHandlerAtLastSeek = handlerAtLastSeek;
	 handlerAtLastSeek = _Handler_;
	 undo->savedIndex = current;
     }
    current = (void*)newIndex;
#endif
    return 1;
}

int GenFile::seek(long offset, int kind)
{
    return -1;
}

void GenFile::printon(ostream& outs) const
{
    outs.form("file(0x%x)", this);
}

#if 0
void *GenFile::fillBuffer()
{
    current = 0;
    readLimit = 0;
    return EOF_ptr;
}

struct Any GenFile::getAny()
{
    return MAKE_ANY(next(), NULL);
}
#endif

Root *GenFile::next()
{
    Signal(new UnimplementedOp("GenFile::next", isA()));
    return NULL;
}
Root *GenFile::peek_next()
{
    Root *value = next();
    if (value != Missing)
	putback(value);
    return value;
}
void GenFile::putback(Root *ob)
{
    Signal(new UnimplementedOp("GenFile::putback", isA()));
}
GenSeq *GenFile::get(size_t n)
{
    Root **vals = new RootPtr[n];
    int i = 0;
    for (; i < n; i++) {
	Root* val = next();
	if (val == Missing)
	    break;
	vals[i] = val;
    }
    Vector *vec = NewVector(i, vals);
    delete [] vals;
    return vec;
}
void GenFile::destroy() { }
GenFile::~GenFile() { destroy(); }

int GenFile::close() { return 0; }

#if 0
Root *Iterator::next()
{
    Root * v = cltn->index(index);
    if (v != Missing) index++;
    return v;
}
#endif

GenSeq *Iterator::get(size_t n)
{
#if 0
    fprintf(stderr, "SegFile::get(%d) -> ", n);
    seq->printon(stderr);
    fprintf(stderr, "->subseq(%d,%d)\n", curIndex, n);
    fflush(stderr);
#endif
    if (index >= 0) {
	GenSeq* seq = cltn->sequence();
	if (seq) {
	    GenSeq *ss = seq->subseq(index, index+n);
	    index += ss->length();
	    return ss;
	}
    }
    return GenFile::get(n);
}

#if 0
int Iterator::seek(Root* arg)
{
    const Numeric *num = arg->numeric();
    if (num == NULL) return -1;
    long i;
    if (!num->getlong(&i)) return -1;
    GenSeq *seq = cltn->sequence();
    if (i > cltn->length()) return -1;
    curIndex = i;
    return 0;
}
#endif

Root* SeqIterator::next()
{
    Root *t;
    if (state) t=state, state=NULL; // In case of putback.
    else t=((GenSeq*)cltn)->index(index);
    index++;
    return t;
}

int SeqIterator::seek(long offset, int kind)
{
    int cur_len = cltn->length();
    switch (kind) {
      case 0:
	break;
      case 1:
	offset += index;
	break;
      case 2:
	offset += cur_len;
	break;
      default:
	return -1;
    }
    if (offset > cur_len)
	return -1;
    index = offset;
    return index;
}

void SeqIterator::putback(Root* ob)
{
    state = ob;
    index--;
}

Root *SeqIterator::key()
{
    int k = seek(0, 1);
    if (k < 0)
	Signal(new GenericCondition("seek() failed in SeqIterator::key"));
    return (Root*)MakeFixInt(k);
}

void SeqIterator::put(Root *ob)
{
    ((GenSeq*)cltn)->set_at(index++, ob);
    state = NULL;
}

SubSeqIterator::SubSeqIterator(SubSeq *sseq)
{
    _count = 0;
    _start = sseq->start;
    if (sseq->start < 0)
	_start = base()->seek(_start, 2);
    else if (_start > 0)
	_start = base()->seek(_start, 0);
    if (_start < 0)
	Signal(new GenericCondition("Bad seek."));
    _limit = sseq->end;
    if (_limit != -1) {
	if (_limit < 0)
	    _limit = sseq->length() + sseq->end + 1;
	_limit -= _start;
    }
}

Root *SubSeqIterator::next()
{
    if (_limit != -1 && _count >= _limit)
	return Missing;
    Root *el = base()->next();
    if (el != Missing)
	_count++;
    return el;    
}

#if 0
RangeFile::RangeFile(GenRecur* r) : Iterator(r)
{
    bufIndex = FixInt::biggest();
}

Root* RangeFile::next()
{
    if (curIndex >= range()->len)
	return Missing;
    if (curIndex > bufIndex)
	range()->stepper(_buf(), _buf(), curIndex - bufIndex);
    else if (curIndex < bufIndex)
	range()->stepper(range()->first, _buf(), curIndex);
    bufIndex = curIndex++;
    return range()->elType->coerceToRoot(_buf());
}
#endif

class CharDummySeqType : public CharSeq {
  public:
    virtual size_t length() { return UnknownLength; }
};
CharDummySeqType CharDummySeq;

CharFile::CharFile(streambuf *s)
{
    cltn = &CharDummySeq;
    stream = s;
    if (s) state = ios::goodbit; else state = ios::badbit;
}

CharFile::CharFile(GenSeq &m, streambuf *s)
{
    cltn = &m;
    stream = s;
    if (s) state = ios::goodbit; else state = ios::badbit;
}

void CharFile::destroy()
{
  delete stream;
  stream = 0;
}

Root *CharFile::key()
{
    streampos pos = stream->seekoff(0, ios::cur);
    if (pos == EOF) {
	// ERROR!
    }
    return (Root*)MakeFixInt(pos);
}

Root *CharFile::next()
{
    if (stream == NULL)
	return Missing;
    int c = stream->sbumpc();
    if (c == EOF) return Missing;
    return &CharTable[(unsigned char)c];
}

GenSeq *CharFile::get(size_t n)
{
#ifndef BUFSIZ
#define BUFSIZ 1024
#endif
    size_t cur_size = n == InfiniteLength ? BUFSIZ : n;
    char *buf = (char*)malloc(cur_size);
    register char *ptr = buf, *lim = buf+cur_size;
    for (;;) {
	if (ptr >= lim) {
	    if (n != InfiniteLength)
		break;
	    int new_size = cur_size + BUFSIZ;
	    buf = (char*)realloc(buf, new_size);
	    ptr = buf + cur_size;
	    lim = buf + new_size;
	    cur_size = new_size;
	}
	int cc = stream->sgetn(ptr, lim - ptr);
	if (cc < 0) {perror("read failed");
	    return NULL;
	}
	if (cc == 0) break;
	ptr = ptr + cc;
    }
    size_t length = ptr - buf;
    if (ptr + 1 != lim)
	buf = (char*)realloc(buf, length + 1);
    buf[length] = '\0';
    return new String(buf, length);
}

int CharFile::seek(long offset, int kind)
{
    return stream->seekoff(offset, (_seek_dir)kind);
}

void CharFile::putback(Root *ob)
{
    if (!ob->isMemberOf(*Character::desc()))
	RaiseDomainError(NULL);
    stream->sputbackc(((Character*)ob)->string()[0]);
}

nullbuf nullsbuf;

int CharFile::close()
{
    int status = 0;
    if (stream == &nullsbuf)
	return 0;
#if !defined(_IO_IS_FILEBUF) && defined(_S_IS_FILEBUF)
#define _IO_IS_FILEBUF _S_IS_FILEBUF
#endif
#ifdef _IO_IS_FILEBUF
    if (stream->_flags & _IO_IS_FILEBUF) {
	status = ((filebuf*)stream)->close() == NULL ? EOF : 0;
	delete (filebuf*)stream;
    }
    else
#endif
	delete (streambuf*)stream;
    stream = &nullsbuf;
    return status;
}

MapFile::MapFile(const struct Mapping *m, int pos) : map(m)
{
    i = pos;
}

MapFile *MapOpen(struct Mapping *map, int pos)
 { return new MapFile(map, pos); }

int MapFile::seek(void *arg, int kind)
{
    return -1;
}

int MapFile::seek(Root *arg)
{
    abort();
    return 0;
}

#if 0
void *MapFile::fillBuffer()
{
    return EOF_ptr;
}
#endif
Root * MapFile::key()
{
    if (i >= map->size) return Missing;
    else return (Root*)((char*)map->argBuf + i * map->stride);
}

Root * MapFile::next()
{
    if (i >= map->size) return Missing;
    i++;
    return (Root*)((char*)map->valBuf + (i-1) * map->stride);
}

#if 0
void UnifyMaps (struct Mapping *map, struct Any other)
{
    MapFile file(map, 0);
    for (;;) {
      void *arg = file.key();
      if (arg == Missing) return;
      void *val = file.next();
      Unify(MAKE_ANY(val, NULL),
	    MappingLookupTrapEOF(other, MAKE_ANY(arg, NULL)));}
}
#endif

#undef stdin
#undef stdout
#undef stderr

#if 0
CharFile StdIn(cin.rdbuf());
CharFile StdOut(cout.rdbuf());
CharFile StdErr(cerr.rdbuf());
#else
CharFile _StdIn(cin.rdbuf());
CharFile _StdOut(cout.rdbuf());
CharFile _StdErr(cerr.rdbuf());
SimpleAssignable StdIn(&_StdIn);
SimpleAssignable StdOut(&_StdOut);
SimpleAssignable StdErr(&_StdErr);
#endif
INSERT_BUILTIN(stdin, StdIn);
INSERT_BUILTIN(stdout, StdOut);
INSERT_BUILTIN(stderr, StdErr);

CharFile *DefaultInFile()
{
    return Coerce2CharFile(StdIn.value()); // FIXME:  Duplicate checking?
}

CharFile *DefaultOutFile()
{
    return Coerce2CharFile(StdOut.value());
}

CharFile* Coerce2CharFile(Root* arg)
{
#if 1
    arg = arg->value();
#else
    LVariable *lvar = arg->lvariable();
    if (lvar) arg = lvar->value();
#endif
    CharFile* file = PTR_CAST(CharFile, arg);
    if (file == NULL)
	Signal(new CoercionFail(arg, CharFile::desc()));
    return file;
}

GenFile* Coerce2GenFile(Root* arg)
{
#if 1
    arg = arg->value();
#else
    LVariable *lvar = arg->lvariable();
    if (lvar) arg = lvar->value();
#endif
    GenFile* file = PTR_CAST(GenFile, arg);
    if (file == NULL)
	Signal(new CoercionFail(arg, GenFile::desc()));
    return file;
}

class ListIterator : public Iterator {
    // An Alist implements a linked list.  The complication is one
    // can create an Alist by doing a "cons" such the cdr is some
    // other kind of sequence.  In that case, the abstract sequence
    // is represented by the "linked part" (a chain of AList cells),
    // followed by the "tail" part (some other kind of sequence).
    // (In Lisp, the tail part can be &NilSequence, which means
    // the empty seqeence.  Otherwise, the tail part is usually
    // &NullSequence, but not always.)
    // If the iterator is currently in the linked part, then
    // rest_file and rest_seq are NULL, and index is the current index.
    // If the iterator is currently in the tail part, then rest_seq is
    // the tail part, rest_file is an iterator over rest_seq, and index
    // is the length of the linked part.
    AList *_first;
    AList *first() { return _first; }
    Root *current;
    Root *saved; // putback buffer
    long index;
    GenFile *rest_file;
    GenSeq *rest_seq;

  public:
    ListIterator(AList *list);
    virtual Root *next();
    virtual void putback(Root*);
    virtual int seek(long offset, int kind);
    ~ListIterator() { delete rest_file; }
};

ListIterator::ListIterator(AList *list)
{
    _first = list;
    current = list;
    index = 0;
    rest_file = NULL;
    rest_seq = NULL;
    saved = 0;
}

Root *ListIterator::next()
{
    if (saved) {
	Root *tmp = saved;
	saved = NULL;
	return tmp;
    }
    if (rest_file)
	return rest_file->next();
    Root *current_value = current->value();
    AList *list = PTR_CAST(AList, current_value);
    if (list) {
	current = list->cdr;
	index++;
	return list->car;
    }
    if (current_value == &NilSymbol || current_value == &NullSequence)
	return Missing;
    GenSeq *seq = current_value->sequence();
    if (seq == NULL)
	Signal(new CoercionFail(current_value, GenSeq::desc()));
    rest_seq = seq;
    rest_file = seq->GenMap::open();
    return rest_file->next();
}

void ListIterator::putback(Root* value)
{
    if (saved != NULL)
	Signal(new UnimplementedOp("Multiple calls to ListIterator::putback."));
    saved = value;
}

int ListIterator::seek(long offset, int kind)
{
    // FIXME:  Handle non-NULL value of saved field.
    if (kind == 2)
	offset += first()->length();
    else if (kind == 1) {
	if (offset < 0) {
	    offset += index;
	    if (rest_file)
		offset += rest_file->seek(0, 1);
	}
	else if (rest_file) {
	    int rest_offset = rest_file->seek(offset, 1);
	    return rest_offset < 0 ? rest_offset : rest_offset + index;
	}
	else
	    offset += index;
    }

    if (offset < index) {
	// First seek(0,0):
	current = _first;
	index = 0;
	delete rest_file;
	rest_seq = 0;
    }
    else if (rest_file) {
	int rest_offset = rest_file->seek(offset - index, kind);
	return rest_offset < 0 ? rest_offset : rest_offset + index;
    }
    while (index < offset) {
	Root *n = next();
	if (n == Missing || n == NULL)
	    Signal(new GenericCondition("Bad seek."));
    }
}

int AList::sizeof_file() const { return sizeof(ListIterator); }
void AList::open(GenFile *f, OpenFlags flags)
{
    ListIterator *it = (ListIterator*)f;
    CONSTRUCT(it, ListIterator, (this));
}

#if 0
class RootNoDelete : public Root {
public:
  void operator delete(void*) { } // Nothing
};
#endif

StackIterator::~StackIterator()
{
  file->destroy();
}
