#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#include "machdefs.h"

#ifndef  IBMVM
#include <sys/types.h>
#include <unistd.h>
#endif

#include "filecache.h"

#ifdef RFIO
#include <shift.h>
#endif

#ifdef __mac__
#include <stat.h>
#endif

/* 
++ 
  Module 	Buffered I/O (C)
  Lib	fsv
  include	filecache.h

	Les fonctions décrites ci-dessous permettent de lire et d'écrire
	des fichiers avec une gestion de mémoire tampon, afin d'optimiser 
	le débit d'entrée-sortie en cas d'utilisation lourde du positionnement
	dans le fichier (seek()). L'interface est similaire à celle des 
	fonctions d'entrée sortie standard C ("fopen(...)", "fread(...)", ...).
	Au moment de l'ouverture du fichier (par zfopen()),
	une liste chainée de zones mémoire, (des pages)
	est créé. Ces pages sont destinées a contenir une
	image mémoire des zones accédées du fichier au moment
	des opérations de lecture et d'écriture, réduisant
	ainsi le nombre des accés effectifs au fichier disque.
	La taille et le nombre de page allouées sont contrôlés par 
	l'utilisateur.
--
*/

static int zf_prtlev = 0;
/* Nouvelle-Fonction */
void zfSetPrtLevel(int lev)
{
zf_prtlev = lev;
}

int pgread(FILECACHE *fcp, long fr, long n, char *buff);
int pgwrite(FILECACHE *fcp, long fr, long n, char *buff);

/*
++
FILECACHE  *zfopen(char *flnm, char *mode, int npg, int pgsiz)
	Ouverture du fichier "flnm", en mode lecture seule ou
	lecture-écriture. "npg" pages de tailles "pgsiz" sont 
	allouées pour la mémoire tampon. Les caractères "rw+"
	peuvent être spécifié pour "mode". Si "npg <= 0",
	les fonctions standard "fseek()", "fread()" et "fwrite()"
	sont utilisées. 

void zfclose( FILECACHE *fcp )
	Fermeture du fichier. Il est impératif d'appeler zfclose()
	si le fichier a été ouvert en écriture.

void zfSetPrtLevel(int lev)
	Modification du niveau d'impression pour le debug.
	si "lev > 2" des statistiques concernant l'utilisation 
	de la mémoire tampon sont imprimées à la fermeture du fichier.
--
*/ 



/* Nouvelle-Fonction */ 
FILECACHE  *zfopen(char *flnm, char *mode, int npg, int pgsiz)
 
{
FILE *fip;
FILECACHE *fcp;
int i,j;
DSKPAGE  *first, *cur, *prev;
 
 
if ( (fip=fopen(flnm,mode)) == NULL )
  {
  puts("zfopen_Erreur - Erreur fopen !");
  return(NULL);
  }
 
 
if ( (fcp=malloc(sizeof(FILECACHE))) == NULL )
  {
  fclose(fip);
  puts("zfopen_Erreur - Erreur malloc(fcp) !");
  return(NULL);
  }
 
if ( npg < 0 )  {  npg = DEF_NPAGE ;  pgsiz = DEF_PGSIZE ; }
 
fcp->fip = fip;

#if defined(IBMVM)
fcp->fidesc = -1;
#elif defined(HPUX)
fcp->fidesc = (int)fip->__fileH*256+(int)fip->__fileL;
#elif defined(__MWERKS__) 
//fcp->fidesc = (int)fip->_Handle;  // ANSI ancien
fcp->fidesc = (int)fip->handle; // MSL
#elif defined(Linux) 
fcp->fidesc = (int)fip->_fileno;
#else
fcp->fidesc = (int)fip->_file;
#endif

fcp->fpos = 0;
fcp->nread = fcp->nwrite = 0;
fcp->totrd = fcp->totwr =0;
fseek(fip,0,SEEK_END);
fcp->fsz = ftell(fip);
fseek(fip,0,SEEK_SET);
 
fcp->rwfg = 0;
for(i=0; i<strlen(mode); i++)
  if ( ( *(mode+i) == 'w' ) || ( *(mode+i) == '+' ) )  fcp->rwfg = 1;
 
fcp->memode = 0;
fcp->first = fcp->last = NULL;
fcp->pages = NULL;
fcp->npage = fcp->pgsize = 0;
 
if (npg <= 0)   return(fcp);       /*  Si pas d'option caching  */
 
if (pgsiz < MIN_PGSIZE)  pgsiz = MIN_PGSIZE;
if (npg < MIN_NPAGE)  npg = MIN_NPAGE;
 
fcp->memode = 1;
fcp->npage = npg;
fcp->pgsize = pgsiz;
 
if ( ( first = (DSKPAGE *)malloc(npg*sizeof(DSKPAGE)) ) == NULL )
  {
  fclose(fip);   free(fcp);
  puts("zfopen_Erreur: Pb malloc(DSKPAGE) !");
  return(NULL);
  }

prev = NULL;
cur = first-1;
for(i=0; i<npg; i++)
  {
  cur++;
  cur->wrfg = 0;
  cur->pgno = -1;
  if ( (cur->page=malloc(pgsiz)) == NULL)
    {
    for(j=0; j<i; j++)
      free((first + j)->page);
    free(first);  free(fcp);
    fclose(fip);
    return(NULL);
    }
  memset((void *)cur->page, (int)('\0'), (size_t)pgsiz);
  cur->next = cur+1;
  cur->prev = prev;
  prev = cur;
  }
 
cur->next = NULL;
fcp->last = cur;
fcp->first = first;
fcp->pages = first; 
 
if (zf_prtlev > 5)
  {
  printf("zfopen_Debug: NPage= %d , PgSize= %d \n", npg, pgsiz);
  cur = fcp->first;
  for (i=0; i<npg; i++)
    {
    printf("Pg[%d] *DskPg = %lx *page= %lx *Next= %lx \n",
           (i+1), (unsigned long)cur, (unsigned long)cur->page, (unsigned long)cur->next);
    cur = cur->next;
    }
  }
 
return(fcp);
}
 
 
 
/* Nouvelle-Fonction */ 
void zfclose( FILECACHE *fcp )
{
float effr,effw;
DSKPAGE  *cur;
int_4 pgsiz;
long int fp;
 
if (fcp == NULL)  return;
 
 
pgsiz = fcp->pgsize;
cur = fcp->first;
while (cur != NULL)
  {
  if ( cur->wrfg && fcp->rwfg )
    {
    fp = cur->pgno*fcp->pgsize;
#ifdef  IBMVM
   fseek(fcp->fip, fp, SEEK_SET);
   fwrite(cur->page, 1, pgsiz, fcp->fip);
#else
    lseek(fcp->fidesc, (off_t)fp, SEEK_SET);
    write(fcp->fidesc, cur->page, pgsiz);
#endif
    fcp->nwrite ++;
    }
  free(cur->page);
  cur = cur->next;
  }
 
if (zf_prtlev > 2)
  {
  if (fcp->nread > 0)
  effr = (float)(fcp->totrd)/(float)(fcp->nread*pgsiz);
  else   effr = 1.0;
  if (fcp->nwrite > 0)
    effw = (float)(fcp->totwr)/(float)(fcp->nwrite*pgsiz);
  else   effw = 1.0;
  puts(" ================= Statistiques Memoire Cache =================");
  printf(" NPage= %d   PgSize= %d \n", fcp->npage, fcp->pgsize);
  printf(" PgRead= %d  TotRd= %ld TotRd/PgRead= %g \n",
         fcp->nread, fcp->totrd, effr);
  printf(" PgWrite= %d  TotWr= %ld TotWr/PgWrite= %g \n",
         fcp->nwrite, fcp->totwr, effw);
  puts(" ==============================================================");
  }

/* fcp->first a tourne et ne pointe plus vers la 1ere structure des DSKPAGES* */
free(fcp->pages);
fclose(fcp->fip);
free(fcp);
 
return;
 
}
 
/*
++
int zfseek( FILECACHE *fcp, long int offset, int fg)
	Positionnement dans le fichier. "fg" peut valoir
	"SEEK_SET", "SEEK_CUR", "SEEK_END", pour 
	un positionnement avec décalage "offset" à partir
	du début de fichier, de la position courante, ou
	de la fin du fichier.

size_t zfread( void *buff, size_t size, size_t nitems, FILECACHE *fcp)
	Lecture à partir de la position courante.

size_t zfwrite( void *buff, size_t size, size_t nitems, FILECACHE *fcp)
	Ecriture à partir de la position courante.
--
*/ 
 
/* Nouvelle-Fonction */ 
int zfseek( FILECACHE *fcp, long int offset, int fg)
{
long int newpos;
 
if (fcp == NULL)  return(-2);
if ( !fcp->memode )   return ( fseek(fcp->fip, offset, fg) );
 
newpos = offset;
if (fg == SEEK_SET)  newpos = offset;
if (fg == SEEK_CUR)  newpos = fcp->fpos + offset;
if (fg == SEEK_END)  newpos = fcp->fsz - offset;
 
if ( newpos < 0 )   return(-1);
if ( !fcp->rwfg && (newpos >= fcp->fsz) )  return(-1);
fcp->fpos = newpos;
return(0);
 
}
 
 
 
/* Nouvelle-Fonction */
size_t zfread( void *buff, size_t size, size_t nitems, FILECACHE *fcp) 
{
long int m,n;
long int fr,nr;
 
 
if (fcp == NULL)  return(0);
if ( !fcp->memode )   return ( fread(buff, size, nitems, fcp->fip) );
 
#ifdef  DEBUG_FCACHE
printf(" Debug_zfread pos,size,nitem= %d %d %d\n",fcp->fpos,size,nitems);
#endif
 
m = 0;  n = size*nitems;
if ( fcp->fpos+n > fcp->fsz )  n = fcp->fsz-fcp->fpos;
if ( n <= 0)  return(0);
fr = fcp->fpos;
 
fcp->totrd += n;   nr = n;
 
while ((m < n) && (nr > 0))
  {
#ifdef  DEBUG_FCACHE
  printf(" Debug_zfread_2 fr,n,m= %d %d %d\n",fr,n,m);
#endif
  nr = pgread(fcp,fr,(n-m), ((char *)buff)+m);
  m += nr; fr += nr;
  }
 
fcp->fpos += m;
 
return(m);
}
 
 
/* Nouvelle-Fonction */
int pgread(FILECACHE *fcp, long fr, long n, char *buff)
{
long int pgnum, pg;
long int off, nr;
 
long int fp;
DSKPAGE  *cur;
 
pgnum = fr / fcp->pgsize;
 
/*  On regarde si la page est en memoire  */
 
pg = -1;
cur = fcp->first;
while (cur != NULL)
  {
  if (cur->pgno < 0)  break;
  if (cur->pgno == pgnum)  { pg = pgnum; break; }
  cur = cur->next;
  }
 
 
/*  On la lit si elle n'y est pas   */
if ( pg < 0)
  {
  cur = fcp->last;
/*   On reecrit la derniere page si necessaire  */
  if ( cur->wrfg && fcp->rwfg )
    {
    fp = cur->pgno*fcp->pgsize;
#ifdef  IBMVM
   fseek(fcp->fip, fp, SEEK_SET);
   fwrite(cur->page, 1, fcp->pgsize, fcp->fip);
#else
    lseek(fcp->fidesc, (off_t)fp, SEEK_SET);
    write(fcp->fidesc, cur->page, fcp->pgsize);
#endif
    fcp->nwrite ++;
    }
 
  fp = pgnum*fcp->pgsize;
#ifdef IBMVM
  fseek(fcp->fip, fp, SEEK_SET);
  fread(cur->page, 1, fcp->pgsize, fcp->fip);
#else
  lseek(fcp->fidesc, (off_t)fp, SEEK_SET);
  read(fcp->fidesc, cur->page, fcp->pgsize);
#endif
  cur->pgno = pgnum;
  cur->wrfg = 0;
  fcp->nread ++;
  }
 
/*  On reordonne les pages   */
if ( cur != fcp->first )
  {
  if ( cur == fcp->last )
    { fcp->last = cur->prev;   (fcp->last)->next = NULL; }
  else
    { (cur->prev)->next = cur->next;   (cur->next)->prev = cur->prev; }
 
  (fcp->first)->prev = cur;
  cur->next = fcp->first;
  cur->prev = NULL;
  fcp->first = cur;
  }
 
/*  Copie ds buffer utilisateur  */
off = fr - pgnum*fcp->pgsize;
if ( (nr=fcp->pgsize-off) > n )  nr = n;
memcpy(buff, (cur->page)+off, nr);
 
return(nr);
}
 
 
 
/* Nouvelle-Fonction */
size_t zfwrite( void *buff, size_t size, size_t nitems, FILECACHE *fcp)
{
long int m,n;
long int fr,nw;
 
 
if (fcp == NULL)  return(-1);
if ( !fcp->rwfg )  return(-2);
if ( !fcp->memode )   return ( fwrite(buff, size, nitems, fcp->fip) );
 
 
m = 0;  n = size*nitems;
if ( n <= 0)  return(0);
 
fr = fcp->fpos;
fcp->totwr += n;   nw = n;
 
while ((m < n) && (nw > 0))
  {
  nw = pgwrite(fcp,fr,(n-m), ((char*)buff)+m);
  m += nw; fr += nw; }
 
fcp->fpos += m;
if (fcp->fpos >= fcp->fsz)  fcp->fsz = fcp->fpos+1;
 
return(m);
}
 
 
/* Nouvelle-Fonction */
 
int pgwrite(FILECACHE *fcp, long fr, long n, char *buff)
 
{
long int pgnum, pg;
long int off, nw;
 
long int fp;
DSKPAGE  *cur;
 
pgnum = fr / fcp->pgsize;
 
/*  On regarde si la page est en memoire  */
 
pg = -1;
cur = fcp->first;
while (cur != NULL)
  {
  if (cur->pgno < 0)  break;
  if ( cur->pgno == pgnum)  { pg = pgnum; break; }
  cur = cur->next;
  }
 
 
/*  On la lit si elle n'y est pas   */
if ( pg < 0)
  {
 
  cur = fcp->last;
/*   On reecrit la derniere page si necessaire  */
  if ( cur->wrfg && fcp->rwfg)
    {
    fp = cur->pgno*fcp->pgsize;
#ifdef  IBMVM
    fseek(fcp->fip, fp, SEEK_SET);
    fwrite(cur->page, 1, fcp->pgsize, fcp->fip);
#else
    lseek(fcp->fidesc, (off_t)fp, SEEK_SET);
    write(fcp->fidesc, cur->page, fcp->pgsize);
#endif
    fcp->nwrite ++;
    }
 
  fp = pgnum*fcp->pgsize;
  if ( (fp + fcp->pgsize) >= fcp->fsz )   /* remise a zero de la page */
    memset((void *)cur->page, (int)('\0'), (size_t)fcp->pgsize);
#ifdef IBMVM
  fseek(fcp->fip, fp, SEEK_SET);
  fread(cur->page, 1, fcp->pgsize, fcp->fip);
#else
  lseek(fcp->fidesc, (off_t)fp, SEEK_SET);
  read(fcp->fidesc, cur->page, fcp->pgsize);
#endif
  cur->pgno = pgnum;
  cur->wrfg = 0;
  fcp->nread ++;
  }
 
/*  On reordonne les pages   */
if ( cur != fcp->first )
  {
  if ( cur == fcp->last )
    { fcp->last = cur->prev;   (fcp->last)->next = NULL; }
  else
    { (cur->prev)->next = cur->next;   (cur->next)->prev = cur->prev; }
 
  (fcp->first)->prev = cur;
  cur->next = fcp->first;
  cur->prev = NULL;
  fcp->first = cur;
  }
 
/*  Copie ds buffer utilisateur  */
off = fr - pgnum*fcp->pgsize;
if ( (nw=fcp->pgsize-off) > n )  nw = n;
memcpy((cur->page)+off, buff, nw);
cur->wrfg = 1;
 
return(nw);
}
