/*
 * filesystem.c:
 *
 * Copyright (c) 2000 James McKenzie <james@fishsoup.dhs.org>,
 * All rights reserved.
 *
 */

static char rcsid[] =
    "$Id: history.c,v 1.2 2001/10/04 18:00:36 root Exp $";

/*
 * $Log: history.c,v $
 * Revision 1.2  2001/10/04 18:00:36  root
 * #
 *
 * Revision 1.1  2001/01/30 06:16:18  root
 * #
 *
 * Revision 1.2  2000/12/01 04:42:10  root
 * #
 *
 * Revision 1.1  2000/12/01 04:13:42  root
 * #
 *
 * Revision 1.5  2000/11/23 23:45:19  root
 * #
 *
 * Revision 1.4  2000/09/24 01:44:47  root
 * #
 *
 * Revision 1.3  2000/09/24 01:12:43  root
 * #
 *
 * Revision 1.2  2000/09/19 05:06:22  root
 * #
 *
 * Revision 1.1  2000/09/19 03:53:27  root
 * #
 *
 */

#include "project.h"



#define BUFSIZE (64*1024)
#define CHUNK	BUFSIZE

BEGIN_OBJECT(CFH_hs)
    CFH_FILLER;

char *buf;                      //Buffer
int bufsize;

int wptr;                       //writeptr
int rptr;                       //readptr
int vptr;                       //validptr

off_t rtell;                    //readtell
off_t wtell;
off_t vtell;

CFH real_cfh;

END_OBJECT(CFH_hs)

static void fh_destructor(CFH_hs);

CREATOR(CFH_hs) = (Creator(CFH_hs)) Obj_DefaultCreator;
DESTRUCTOR(CFH_hs) = fh_destructor;

static void fh_destructor(CFH_hs c)
{
  if (c->real_cfh)
    Obj_Delete(c->real_cfh);
  if (c->buf)
    free(c->buf);
  printf("Buffer at %p freed\n", c->buf);

  free(c);
}



//rptr==wptr means no more data in the buffer... 
//vptr==wptr means no data in the history...

#define TOVALID(fh) (( fh->wptr >= fh->vptr) ?  ( fh->wptr - fh->vptr) : ( fh->wptr + ( fh->bufsize - fh->vptr )))
#define TOREAD(fh) (( fh->wptr >= fh->rptr) ?  ( fh->wptr - fh->rptr) : ( fh->wptr + ( fh->bufsize - fh->rptr )))
#define TOWRITE(fh) ((fh->rptr >  fh->wptr) ?  ((fh->rptr - fh->wptr) - 1) : (fh->rptr + ( (fh->bufsize - fh->wptr ) - 1 )))


int inrange(int l, int m, int h)
{

  if (l == h)
    return 0;

  if (l < h)
    if ((l < m) && (m <= h))
      return 1;

  if (l < m)
    return 1;
  if (m <= h)
    return 1;

  return 0;

}

static off_t fs_tell(CFH cfh)
{
  CFH_hs fh = (CFH_hs) cfh;
  return fh->rtell;
}




static off_t fs_size(CFH cfh)
{
  CFH_hs fh = (CFH_hs) cfh;
  return fh->real_cfh->size(fh->real_cfh);
}




static int fs_read(CFH cfh, void *d, int s)
{
  CFH_hs fh = (CFH_hs) cfh;
  off_t end, len;
  int i, j;

  if (s > fh->bufsize)
    s = fh->bufsize;

  end = fh->rtell + s;
  len = fs_size(cfh);

  if (end > len) {
    end -= len;
    s -= end;
  }

  if (!s)
    return s;

  if (fh->wptr != fh->rptr) {

    i = TOREAD(fh);


    j = (i > s) ? s : i;

    i = fh->bufsize - fh->rptr; //How many bytes we can read before wrapping

    if (i > j) {
      bcopy(&fh->buf[fh->rptr], d, j);
      fh->rptr += j;
      fh->rtell += j;
    } else {

      bcopy(&fh->buf[fh->rptr], d, i);
      fh->rptr = 0;
      d += i;
      i = j - i;
      bcopy(d, fh->buf, i);
      fh->rptr = i;
      fh->rtell += j;

    }
    s -= j;
    d += j;

    if (!s)
      return j;

    i = fs_read(cfh, d, s);

    if (i <= 0)
      return j;

    j += i;

    return j;

  }


  s = fh->real_cfh->read(fh->real_cfh, d, s);

  if (s <= 0)
    return s;

  i = fh->bufsize - fh->rptr;   //How many bytes we can read before wrapping

  if (i > s) {
    bcopy(d, &fh->buf[fh->rptr], s);
    fh->rptr += s;
    fh->rtell += s;
  } else {

    bcopy(d, &fh->buf[fh->rptr], i);
    fh->rptr = 0;
    d += i;
    i = s - i;
    bcopy(d, fh->buf, i);
    fh->rptr = i;
    fh->rtell += s;

  }

  fh->wtell = fh->rtell;

  if (inrange(fh->wptr, fh->vptr, fh->rptr)) {

    fh->vptr = fh->wptr = fh->rptr;
    fh->vptr++;
    if (fh->vptr == fh->bufsize)
      fh->vptr = 0;

    fh->vtell = fh->wtell - TOVALID(fh);

  }

  fh->wptr = fh->rptr;


  return s;
}


static off_t fs_seek(CFH cfh, off_t off, int whence)
{
  CFH_hs fh = (CFH_hs) cfh;
  off_t where;
  int i, s;

  printf("seek: of=%ld wh=%d ", off, whence);

  switch (whence) {
  case SEEK_SET:
    where = off;
    break;
  case SEEK_END:
    whence = SEEK_SET;
    where = fs_size(cfh) - off;
    break;
  case SEEK_CUR:
    whence = SEEK_SET;
    where = off + fh->rtell;
    break;
  default:
    return (off_t) - 1;
  }

  if (where < 0)
    where = 0;
  if (where > fs_size(cfh))
    where = fs_size(cfh);

  printf("off=%ld wh=%ld wm=%d [%ld,%ld,%ld] ", off, where, whence,
         fh->vtell, where, fh->wtell);


  if (where == fh->rtell)
    return fh->rtell;

  if ((where >= fh->vtell) && (where < fh->wtell)) {
    // In our cache

    printf("Served SEEK FROM history cache ");

    s = where - fh->vtell;

    i = fh->bufsize - fh->vptr; //How many bytes we can read before wrapping

    fh->rptr = fh->vptr;

    if (TOREAD(fh) < s) {
      printf("COCKUP\n");
      abort();
    }

    if (i > s) {
      fh->rptr = fh->vptr + s;
      fh->rtell = fh->vtell + s;
    } else {
      fh->rptr = 0;
      i = s - i;
      fh->rptr = i;
      fh->rtell = fh->vtell + s;
    }

    printf("history[+%d] ", s);

  } else {

    printf("Serving seek not from cache seek ");
    fh->vtell = fh->wtell = fh->rtell =
        fh->real_cfh->seek(fh->real_cfh, off, whence);
    fh->vptr = fh->rptr = fh->wptr = 0;
  }

  printf("now=%ld\n", fh->rtell);

  return fh->rtell;
}

static int fs_write(CFH cfh, void *d, int s)
{
  return -1;
}

static int fs_eof(CFH cfh)
{
  CFH_hs fh = (CFH_hs) cfh;


  if (fh->rtell == fs_size(cfh))
    return 1;

  return 0;
}

static int fs_get(CFH cfh)
{
  unsigned char c;

  if (fs_eof(cfh))
    return EOF;

  fs_read(cfh, &c, 1);          //FIXME

  return (int) c;

}

static int fs_put(int c, CFH cfh)
{
  return -1;
}

static void fs_close(CFH cfh)
{
  Obj_Delete(cfh);
}

static int fs_status(CFH cfh)
{
  CFH_hs fh = (CFH_hs) cfh;

  return fh->real_cfh->status(fh->real_cfh);

}


static int fs_canread(CFH cfh)
{
  CFH_hs fh = (CFH_hs) cfh;

  if (TOREAD(fh))
    return 1;

  return fh->real_cfh->canread(fh->real_cfh);
}

static void fs_dispatch(CFH cfh)
{
  CFH_hs fh = (CFH_hs) cfh;

  return fh->real_cfh->dispatch(fh->real_cfh);
}


INTERNAL CFH history_cfh(CFH real_cfh)
{
  CFH_hs fh;

  fh = CREATE_OBJ(CFH_hs);

  fh->tell = fs_tell;
  fh->seek = fs_seek;
  fh->read = fs_read;
  fh->write = fs_write;
  fh->get = fs_get;
  fh->put = fs_put;
  fh->eof = fs_eof;
  fh->size = fs_size;
  fh->close = fs_close;
  fh->canread = fs_canread;
  fh->dispatch = fs_dispatch;

  fh->status = fs_status;
  fh->real_cfh = real_cfh;

  fh->buf = (char *) malloc(BUFSIZE);
  fh->bufsize = BUFSIZE;

  printf("Buffer at %p allocated\n", fh->buf);

  fh->wptr = fh->rptr = 0;
  fh->wtell = fh->rtell = real_cfh->tell(real_cfh);


  return (CFH) fh;
}
