/************************************************/
/*                                              */
/* File        : rockmfifo_private.c            */
/* Description : ROCKM FIFO specific library    */
/*               private part                   */
/*                                              */
/* Author: Sfiligoi Igor                        */
/*                                              */
/* Created      : 16.06.1997                    */
/* Last modified: 01.07.1997                    */
/*                                              */
/************************************************/

#include <stdlib.h>
#include <Error.h>
#include <bits.h>

#include "RockM_field_access.h"

#include "rockmfifo_private.h"

	/* return the number of elements held in the cache */
unsigned int rockm_fifo_cache_get_nrels(ROCKM_FIFO_id fifo_id)
{
 if (fifo_id->head==fifo_id->tail)
   return 0; /* empty */
 else if (fifo_id->head<fifo_id->tail)
   return fifo_id->tail-fifo_id->head;
 else
   return ROCKM_FIFO_CACHE_SIZE+fifo_id->tail-fifo_id->head;
}

int rockm_fifo_cache_read(ROCKM_FIFO_id fifo_id,	/* IN : fifo id */
			  unsigned int  nrels,  	/* IN : nr. of elements to read */
			  unsigned int  *data)  	/* OUT: data from the cache, must be allocated by caller */
{
  int csize;
  int i;

  csize = rockm_fifo_cache_get_nrels(fifo_id);

  if (csize<nrels)
    return ROCKM_ERROR_FIFO_EMPTY;

  for (i=0; i<nrels; i++)
    {
      data[i] = fifo_id->cache[fifo_id->head];
      fifo_id->head++;
      if (fifo_id->head>=ROCKM_FIFO_CACHE_SIZE)
	fifo_id->head=0;   /* wrap */
    }
  return ROCKM_ERROR_OK;
}

int rockm_fifo_cache_return(ROCKM_FIFO_id  fifo_id,	/* IN : fifo_id */
			    unsigned int  *data,	/* IN : data to be returned to the cache */
			    unsigned int   nrels)	/* IN : nr. of elements of data to return */
{
  int i;
  
  for (i=nrels; i>0; i--)
    {
      if (fifo_id->head>0)
	fifo_id->head--;
      else
	fifo_id->head = ROCKM_FIFO_CACHE_SIZE-1;

      if (fifo_id->head==fifo_id->tail)
	{ /* ERROR, cache overflow */
	  ErrorSet(ROCKM_ERROR_UNKNOWN,"rockm_fifo_cache_return","Cache overflow.");
	  return ROCKM_ERROR_UNKNOWN;
	}

      fifo_id->cache[fifo_id->head] = data[i-1];
    }

  return ROCKM_ERROR_OK;
}

/* this routine needs the data to be at the fisical start of the cache */
/* no range checking is done */
/* read the data field by field */
int rockm_fifo_fillcache_singular(ROCKM_FIFO_id fifo_id,	/* IN : fifo id */
				  unsigned int  nrels)  	/* IN : nr of elements to read, ignore actual size */
{
  unsigned int i;
  RockM_reg_declaration;

  for (i=0; (RockM_read_d_e(fifo_id->rockm_id))&&(i<nrels); i++)
    {
      FIFO = RockM_read_fifo(fifo_id->rockm_id);

      if (NVD==0)
	break;  /* exit as if an Empty Fifo was found */
      else
	{
	  fifo_id->cache[fifo_id->tail++] = DATA;
	}
    }
 
  if (i<nrels)
    return ROCKM_ERROR_FIFO_EMPTY;
  else
    return ROCKM_ERROR_OK;
}

int rockm_fifo_fillcache(ROCKM_FIFO_id fifo_id,	/* IN : fifo id */
		          unsigned int nrels)	/* IN : fill the cache at least with the nr elements */
						/*      if more than ROCKM_FIFO_FIFO_SIZE, truncated to ROCKM_FIFO_FIFO_SIZE */ 
{
  int csize;
  unsigned int minread;
  unsigned int maxread;     /* max elements that can be read */
  int err;

  if (nrels>ROCKM_FIFO_FIFO_SIZE)
    nrels=ROCKM_FIFO_FIFO_SIZE;

  csize = rockm_fifo_cache_get_nrels(fifo_id);

  if (csize>=nrels)
    return ROCKM_ERROR_OK;  /* nothing to do */

  if (csize>0)
    { /* put the data at fisical start of the cache */
      unsigned int *buffer;
  
      buffer = (unsigned int *) malloc(csize*sizeof(unsigned int));
      rockm_fifo_cache_read(fifo_id,csize,buffer);
      memcpy(fifo_id->cache,buffer,csize*sizeof(unsigned int));
      fifo_id->head = 0;
      fifo_id->tail = csize;
      free(buffer);
    }
  else
    { /* this simplifies the cache */
      fifo_id->head=0;
      fifo_id->tail=0;
    }

  /* now all the data is at the fisical start of the cache */

  /* read only the missing elements */
  minread = nrels - csize;
  maxread = ROCKM_FIFO_FIFO_SIZE - csize;

  err = rockm_fifo_fillcache_singular(fifo_id,minread);

  if ((err!=ROCKM_ERROR_OK)&&(err!=ROCKM_ERROR_FIFO_EMPTY))
    { /* serious error */
      ErrorSetF(err,"rockm_fifo_fillcache","Error found: %s",ErrorGetMessage());
      return err;
    }

  /* no error */
  /* reget csize */
  csize = rockm_fifo_cache_get_nrels(fifo_id);
  if (csize<nrels)
    return ROCKM_ERROR_FIFO_EMPTY;  /* the fillcache has not obtained enough elements */
  else
    return ROCKM_ERROR_OK;
}