/*******************************************************/
/*                                                     */
/* File        : cybos.c                               */
/* Description : (simpilified) c interface to YBOS     */
/*                                                     */
/* Author: Sfiligoi Igor                               */
/*                                                     */
/* Created      : 07.09.1998                           */
/* Last modified: 10.09.1998                           */
/*                                                     */
/*******************************************************/

#include <Error.h>

#include "cybos.h"
#include <byteorder.h>

#if BYTE_ORDER == BIG_ENDIAN
#include <Swap.h>
#endif /* BYTEORDER */

/**************************************************************************/

/* implemented below */

pcybos cybos_newid();

void cybos_freeid(pcybos ybos_id);

int cybos_read_physic(pcybos ybos_id,
		      int    record_nr);

int cybos_seekread(pcybos ybos_id,
		   int   *lrec,
		   int    maxsize);

int cybos_lrec_swap(int *lrec, 
		    int lrec_size);

/**************************************************************************/

pcybos cybos_open(char *filename)
{
  pcybos ybos_id;
  int errnr;

  ybos_id = cybos_newid();

  /* open the ybos file */
  ybos_id->ybfile = fopen(filename,"r");
  if (ybos_id->ybfile==NULL)
    { /* error opening file */
      cybos_freeid(ybos_id);
      ErrorSetF(0,"cybos_open","error opening file. file:%s",filename);
      return NULL;
    }

  /* Read the record size of the first physical record */
  /* Should be the same for all the physical records in the file */
  {
    int count;
    
    count = fread(&ybos_id->record_size,sizeof(int),1,ybos_id->ybfile);

    if (count<1)
      {
	cybos_freeid(ybos_id);
	ErrorSetF(0,"cybos_open","error reading record size. file:%s",filename);
	return NULL;
      }
  }
#if BYTE_ORDER == BIG_ENDIAN
  swap4mv((unsigned int *) &ybos_id->record_size,1);
#endif /* BYTEORDER */
  ybos_id->record_size++;

  /* Allocate the buffer for the physical records */
  ybos_id->record_buffer = (tcybos_record *) malloc(ybos_id->record_size*sizeof(int));

  /* Read the first physical record, filling the record_buffer, curr_record */
  errnr = cybos_read_physic(ybos_id,0);

  if (errnr!=0)
    { /* error reading first physical record */
      fclose(ybos_id->ybfile);
      cybos_freeid(ybos_id);
      ErrorSetF(0,"cybos_open","error reading first physical record. file:%s",filename);
      return NULL;
    }

  /* Set various curr_ values */
  ybos_id->curr_pointer = 0;
  ybos_id->curr_lrec = 0;

  return ybos_id;
}

void cybos_close(pcybos ybos_id)
{
  fclose(ybos_id->ybfile);
  cybos_freeid(ybos_id);
}

int cybos_seek(pcybos ybos_id,
	       int    nr_lrecs)
{
  int i;

  if (nr_lrecs==0)
    return ybos_id->curr_lrec; /* nothing to do */

  if (nr_lrecs<0)
    return cybos_seek(ybos_id,ybos_id->curr_lrec+nr_lrecs); /* don't know how to do it else */

  for (i=0; i<nr_lrecs; i++)
    cybos_seekread(ybos_id,NULL,-1);

  return ybos_id->curr_lrec;
}

int cybos_seek_abs(pcybos ybos_id,
	           int    lrec_nr)
{
  if (lrec_nr<0) lrec_nr = 0;

  if (lrec_nr==ybos_id->curr_lrec)
    return ybos_id->curr_lrec; /* nothing to do */

  if (lrec_nr>ybos_id->curr_lrec)
    return cybos_seek(ybos_id,lrec_nr-ybos_id->curr_lrec); /* faster */

  cybos_read_physic(ybos_id,0);
  ybos_id->curr_pointer = 0;
  ybos_id->curr_lrec = 0;
  
  for (;ybos_id->curr_lrec<lrec_nr;)
    cybos_seekread(ybos_id,NULL,-1);
  
  return ybos_id->curr_lrec;
}

int cybos_read(pcybos ybos_id,
	       int   *lrec,
	       int    maxsize)
{
  int count;

  if (maxsize<0)
    {
      ErrorSetF(0,"cybos_read","maxsize(%i) invalid.",maxsize);

      return -10;  /* must be at least an int */
    }

  count =  cybos_seekread(ybos_id,lrec,maxsize);

#if BYTE_ORDER == BIG_ENDIAN
  if ((count>0) && (count<=maxsize))
    cybos_lrec_swap(lrec,count);
#endif

  return count;
}

/********************************************************************/

/* allocate a new ybos_id */

pcybos cybos_newid()
{
  pcybos ybos_id;

  ybos_id = (tcybos *) malloc(sizeof(tcybos));
  ybos_id->record_size = 0;
  ybos_id->record_buffer = NULL;
  ybos_id->curr_pointer = 0;
  ybos_id->curr_lrec = 0;

  return ybos_id;
}

/* deallocate an initialized ybos_id */

void cybos_freeid(pcybos ybos_id)
{
  if (ybos_id->record_buffer!=NULL)
    free(ybos_id->record_buffer);

  free(ybos_id);
}

/* Read a physical record */

int cybos_read_physic(pcybos ybos_id,
		      int    record_nr)
{
  int count;

  /* Fill the buffer with the first record */
  ybos_id->curr_record = record_nr;
  fseek(ybos_id->ybfile,record_nr*ybos_id->record_size*sizeof(int),SEEK_SET);
  count = fread(ybos_id->record_buffer,sizeof(int),ybos_id->record_size,ybos_id->ybfile);

  if (count!=ybos_id->record_size)
    {
      ErrorSetF(0,"cybos_read_physic","error reading file.");

      return -1; /* error reading file */
    }

#if BYTE_ORDER == BIG_ENDIAN
  swap4mv((unsigned int *) ybos_id->record_buffer,ybos_id->record_size);
#endif /* BYTEORDER */

  if ((ybos_id->record_buffer->rec_size+1) != ybos_id->record_size)
    {
      ErrorSetF(0,"cybos_read_physic","Record size changed (%i,%i).",ybos_id->record_buffer->rec_size+1,ybos_id->record_size);

      return -2;  /* Cannot manage this case */
    }
  
  if (ybos_id->record_buffer->header_size != 4)
    {
      ErrorSetF(0,"cybos_read_physic","invalid header size. (%i,4)",ybos_id->record_buffer->header_size);

      return -3;  /* Cannot manage this case */
    }
  
  if (ybos_id->record_buffer->ph_rec_nr != record_nr)
    {
      ErrorSetF(0,"cybos_read_physic","Invalid record number. (%i,%i)",ybos_id->record_buffer->ph_rec_nr,record_nr);

      return -4;  /* ??? maybe corrupted file ??? */
    }
  
  return 0;
}

/* same as read if maxsize>0, else the lrec is not filled */

int cybos_seekread(pcybos ybos_id,
		   int   *lrec,
		   int    maxsize)
{
  int lrec_size;

  lrec_size = ybos_id->record_buffer->data[ybos_id->curr_pointer];

  while (lrec_size==-1)
    { /* lrec should be in the next record */
      int errnr;

      errnr = cybos_read_physic(ybos_id,ybos_id->curr_record+1);
      ybos_id->curr_pointer = 0;
      if (errnr!=0)
	return errnr;

      lrec_size = ybos_id->record_buffer->data[ybos_id->curr_pointer];
    }

  if (lrec_size==0)
    { /* treat as an error for the moment */
      ErrorSetF(0,"cybos_seekread","unsupported case 1.");

      return -20;
    }

  if ((maxsize>=0) && ((lrec_size+1)>maxsize))
    return (lrec_size+1);

  {
    int lrec_read;   /* nr of ints already read, excluding the lrec_size */


    lrec[0] = lrec_size;
    lrec_read = 0;

    ybos_id->curr_pointer++;

    while (lrec_read<lrec_size)
      {
	int curr_avail;
	int curr_to_read;
	
	curr_avail = ybos_id->record_buffer->rec_size-ybos_id->curr_pointer-ybos_id->record_buffer->header_size;
	curr_to_read = lrec_size-lrec_read;
	if (curr_avail>=curr_to_read)
	  {
	    if (maxsize>0)
	      memcpy(&(lrec[lrec_read+1]),
		     &(ybos_id->record_buffer->data[ybos_id->curr_pointer]),
		     curr_to_read*sizeof(int));
	    ybos_id->curr_pointer+=curr_to_read;
	    lrec_read+=curr_to_read;
	  }
	else
	  {
	    int errnr;
	    
	    if (maxsize>0)
	      memcpy(&(lrec[lrec_read+1]),
		     &(ybos_id->record_buffer->data[ybos_id->curr_pointer]),
		     curr_avail*sizeof(int));
	    lrec_read+=curr_avail;
	    
	    
	    errnr = cybos_read_physic(ybos_id,ybos_id->curr_record+1);
	    ybos_id->curr_pointer = 0;
	    if (errnr!=0)
	      return errnr;
	  }
      } /* while */
    
  }

  ybos_id->curr_lrec++;  /* Pointer moved to the next logical record */
  return (lrec_size+1);
}

#define YBHEADLEN 5

/* Reswap the names of the banks */
int cybos_lrec_swap(int *lrec, 
		    int lrec_size)
{
  int record_datasize,datasize;
  int runsize,ybhead,ybnext,ybstart;
  int bnum;
  
  if( lrec==NULL)
    {
      printf("cybos_lrec_swap: lrec pointer not initialized");
      return -1;
    }
  record_datasize = *lrec;
  
  if( record_datasize > 0 )
    {
      runsize = 0;  /*First word of Logical Record is Buff Datasize*/
      ybhead  = 1;  /* skip one word to jump to LRID header*/
      ybnext  = 0;  /* at the beginning assume no NEXT banks */ 
      ybstart = ybhead;  /* we WILL start from LRID header */
      for (bnum=0; runsize<record_datasize; bnum++)
	{
	  int *head;

	  head = &(lrec[ybhead]); 
	  
	  datasize = head[3]-1; /* Num Words of data*/
	  runsize += (datasize+YBHEADLEN);  /* running size*/

	  /*printf("Record datasize %i datasize %i runsize %i\n",
	    record_datasize,datasize,runsize);*/
	  swap4mv((unsigned int *) head, 1); /* swap just the bname*/
	  
	  if( runsize <= record_datasize )
	    {
	      int i;
	      ybstart = ybhead +YBHEADLEN;  /* running datastart */
	      ybhead  = ybstart+datasize;   /* beginning of next YBHEADER if any */
	      ybnext  = ybhead+YBHEADLEN;
	      if( runsize == record_datasize) break;
	    }
	  else
	    {
	      printf(" cybos_lrec_swap FATAL: exceeding RecordSIze \n");
	    }
	}
    }
  return 0;
}