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

static char rcsid[] = "$Id: buffer.c,v 1.6 2001/10/12 13:51:11 root Exp $";

/*
 * $Log: buffer.c,v $
 * Revision 1.6  2001/10/12 13:51:11  root
 * #
 *
 * Revision 1.5  2001/10/04 18:21:16  root
 * #
 *
 * Revision 1.4  2001/10/04 18:00:36  root
 * #
 *
 * Revision 1.3  2000/12/20 02:59:00  root
 * *** empty log message ***
 *
 * 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 (512*1024)
#define CHUNK	BUFSIZE

BEGIN_OBJECT(CFH_bf)
    CFH_FILLER;

char *buf;                      //Buffer
int bufsize;

int wptr;                       //writeptr

int rptr;                       //readptr

off_t rtell;                    //readtell
off_t wtell;

CFH real_cfh;

END_OBJECT(CFH_bf)

static void fh_destructor(CFH_bf);

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

static void fh_destructor(CFH_bf 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);
}



//how many bytes in the buffer wptr==rptr means none
#define TOREAD(fh) (( fh->wptr >= fh->rptr) ?  ( fh->wptr - fh->rptr) : ( fh->wptr + ( fh->bufsize - fh->rptr )))

//how many bytes can be written wptr==(rptr-1) means none special case 
#define TOWRITE(fh) ( (fh->rptr > fh->wptr) ?  ((fh->rptr - fh->wptr) - 1) : (fh->rptr + ( (fh->bufsize - fh->wptr ) - 1 )))



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




static off_t fs_size(CFH cfh)
{
  CFH_bf fh = (CFH_bf) cfh;

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

}

static int fs_status(CFH cfh)
{
  CFH_bf fh = (CFH_bf) cfh;
  int i;

  i = fh->real_cfh->status(fh->real_cfh);
  i *= TOREAD(fh);
  i = i / fh->bufsize;

  return i;
}



static void fs_dispatch(CFH cfh)
{
  CFH_bf fh = (CFH_bf) cfh;
  int i;
  int one;


// Not wonderfully smart but avoids a memory copy

  i = TOWRITE(fh);
  one = fh->bufsize - fh->wptr;

  if (i > one)
    i = one;

  if (!i)
    return;

  if (i > CHUNK)
    i = CHUNK;

#ifdef DEBUG
  printf("dispatch: rptr=%d wptr=%d going for=%d space=%d ", fh->rptr,
         fh->wptr, i, TOWRITE(fh));
#endif

  i = fh->real_cfh->tread(fh->real_cfh, &fh->buf[fh->wptr], i);


  if (i > 0) {

    fh->wtell += i;
    fh->wptr += i;
    if (fh->wptr == fh->bufsize)
      fh->wptr = 0;

  }
#ifdef DEBUG
  printf("got=%d now rptr=%d wptr=%d\n", i, fh->rptr, fh->wptr);
#endif


}

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

  if (TOREAD(fh))
    return 1;

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

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

  float fb, fa;

  //This prevents the loop below blocking indefinately

  fb = ((float) TOREAD(fh)) / (float) BUFSIZE;

  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 (len != fh->wtell) {
    j = fh->real_cfh->canread(fh->real_cfh);
#ifdef DEBUG
    printf("canread %d\n", j);
#endif

    if (j)
      fs_dispatch(cfh);
  }
  //Read stuff until we can satisfy this read.
  while (TOREAD(fh) < s)
    fs_dispatch(cfh);



#ifdef DEBUG
  printf("read: rtell=%ld rptr=%d wptr=%d want=%d have=%d ", fh->rtell,
         fh->rptr, fh->wptr, s, TOREAD(fh));
#endif


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

  if (i > s) {
    bcopy(&fh->buf[fh->rptr], d, s);
    fh->rptr += s;
    fh->rtell += s;
#ifdef DEBUG
    printf("quick rptr=%d\n", fh->rptr);
#endif
  } else {

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

#ifdef DEBUG
    printf("slow rptr=%d\n", fh->rptr);
#endif
  }



  fa = ((float) TOREAD(fh)) / (float) BUFSIZE;

#if 0
  {
    int a, b;
    a = (int) (fa * 10);
    b = (int) (fb * 10);


    if (a != b) {
      printf("Buffer now %.1f%% full %s\n", fa * 100,
             (len == fh->wtell) ? "winding down" : "buffering");
    }
  }
#endif

  return s;
}


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

//We have to track the seeks because relative ones wont work

  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("of=%ld wh=%d ", off, whence);


  if ((where >= fh->rtell) && (where < fh->wtell)) {
    s = where - fh->rtell;
    i = fh->bufsize - fh->rptr; //How many bytes we can read before wrapping

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

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

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

  } else {

    printf("seek ");
    fh->wtell = fh->rtell = fh->real_cfh->seek(fh->real_cfh, off, whence);
    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_bf fh = (CFH_bf) 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);
}

INTERNAL CFH buffer_cfh(CFH real_cfh)
{
  CFH_bf fh;

  fh = CREATE_OBJ(CFH_bf);

  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;
}
