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

  Copyright (c) 2003-2007 Turku PET Centre

  Library:     ecat7ml.c
  Description: Reading and writing ECAT 7.x matrix list.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  See the GNU Lesser General Public License for more details:
  http://www.gnu.org/copyleft/lesser.html

  You should have received a copy of the GNU Lesser General Public License
  along with this library/program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 

  Turku PET Centre, Turku, Finland, http://www.turkupetcentre.fi/

  Modification history:
  2003-07-21 Vesa Oikonen
    First created.
  2004-06-20 VO
    ecat7PrintMatlist(): blkNr is printed correctly (+1).
  2004-06-27 VO
    Included ecat7DeleteLateFrames().
  2007-02-27 VO
    Added functions ecat7GetMatrixBlockSize() and ecat7GetPlaneAndFrameNr().
  2007-03-13 VO
    Added functions ecat7GetNums() and ecat7GatherMatlist().
  2007-17-07 Harri Merisaari
    fixed for ANSI

******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
/*****************************************************************************/
#include "swap.h"
#include "ecat7.h"
/*****************************************************************************/

/*****************************************************************************/
/*!
 * Prepare matrix list for additional matrix data and return block number
 * for matrix header. Directory records are written in big endian byte order.
 * Set block_nr to the number of data blocks + (nr of header blocks - 1)
 *
 * @param fp file pointer
 * @param matrix_id matrix identifier coding
 * @param block_nr matrix number [1..number of matrixes]
 * @return returns the block number for matrix header, -1 if invalid input,
 * -2 if first directory block is not found, -3 if failed to read first block,
 * -9 if other directory block is not found, -10 if failed to read other block,
 * -11 if place for new directory block is not found, -12 if failed clear new
 * block, -15 if place for new directory block is not found, -16 if failed to 
 * write into new block
 */
int ecat7EnterMatrix(FILE *fp, int matrix_id, int block_nr) {
  unsigned int i=0, dirblk, little, busy=1, nxtblk=0, oldsize;
  /*unsigned*/ int dirbuf[MatBLKSIZE/4];

  if(ECAT7_TEST) printf("ecat7EnterMatrix(fp, %d, %d)\n", matrix_id, block_nr);
  /* Check the input */
  if(fp==NULL || matrix_id<1 || block_nr<1) return(-1);
  /* Is this a little endian machine? */
  little=little_endian();
  /* Read first directory record block */
  dirblk=MatFirstDirBlk;
  fseek(fp, (dirblk-1)*MatBLKSIZE, SEEK_SET);
  if(ftell(fp)!=(dirblk-1)*MatBLKSIZE) return(-2);
  if(fread(dirbuf, sizeof(int), MatBLKSIZE/4, fp) != MatBLKSIZE/4) return(-3);
  /* Byte order conversion for ints in little endian platforms */
  if(little) swawbip(dirbuf, MatBLKSIZE);
  /* Read through the existing directory records */
  while(busy) {
    /* Go through the directory entries in this record */
    for(i=4, nxtblk=dirblk+1; i<MatBLKSIZE/4; i+=4) {
      oldsize=dirbuf[i+2]-dirbuf[i+1]+1;
      if(dirbuf[i]==0) {  /* Check for end of matrix list */
        busy=0; break;
      } else if(dirbuf[i]==matrix_id) {  /* Maybe this matrix already exists? */
        /* yes it does; is old data smaller? */
        if(oldsize<block_nr) {
          /* it was smaller, so do not use it, but mark it deleted */
          dirbuf[i] = 0xFFFFFFFF; dirbuf[i+3]=-1;
          if(little) swawbip(dirbuf, MatBLKSIZE);
          fseek(fp, (dirblk-1)*MatBLKSIZE, SEEK_SET);
          if(ftell(fp)!=(dirblk-1)*MatBLKSIZE) return(-6);
          if(fwrite(dirbuf, sizeof(int), MatBLKSIZE/4, fp) != MatBLKSIZE/4) return(-7);
          if(little) swawbip(dirbuf, MatBLKSIZE);
          nxtblk=dirbuf[i+2]+1;
        } else { /* old matrix size is ok */
          nxtblk=dirbuf[i+1]; dirbuf[0]++; dirbuf[3]--; busy=0;
          break;
        }
      } else { /* this is not the same matrix */
        /* But is deleted and of same or smaller size? */
        if(dirbuf[i+3]==-1 && block_nr<=oldsize) {
          /* yes it was, so lets recycle it */
          dirbuf[i]=matrix_id;
          nxtblk=dirbuf[i+1]; dirbuf[0]++; dirbuf[3]--; busy=0;
          break;
        }
        /* nothing to be done with this entry */
        nxtblk=dirbuf[i+2]+1;
      }
    } /* next entry in this record */
    if(!busy) break; /* stop reading existing records */
    /* Read the next directory record */
    if(dirbuf[1]!=MatFirstDirBlk) {
      /* There are more records left to read */
      dirblk=dirbuf[1];
      fseek(fp, (dirblk-1)*MatBLKSIZE, SEEK_SET);
      if(ftell(fp)!=(dirblk-1)*MatBLKSIZE) return(-9);
      if(fread(dirbuf, sizeof(int), MatBLKSIZE/4, fp) != MatBLKSIZE/4) return(-10);
      if(little) swawbip(dirbuf, MatBLKSIZE);
    } else {
      /* No more records to read, so lets write a new empty one */
      dirbuf[1]=nxtblk; /* write a pointer to the new one */
      if(little) swawbip(dirbuf, MatBLKSIZE);
      fseek(fp, (dirblk-1)*MatBLKSIZE, SEEK_SET);
      if(ftell(fp)!=(dirblk-1)*MatBLKSIZE) return(-11);
      if(fwrite(dirbuf, sizeof(int), MatBLKSIZE/4, fp) != MatBLKSIZE/4) return(-12);
      /* and then initiate the contents of the next one, but do not write it */
      dirbuf[0]=31; dirbuf[1]=MatFirstDirBlk; dirbuf[2]=dirblk;
      dirbuf[3]=0; dirblk=nxtblk;
      for(i=4; i<MatBLKSIZE/4; i++) dirbuf[i]=0;
    }
  } /* next directory record */
  dirbuf[i]=matrix_id;
  dirbuf[i+1]=nxtblk;
  dirbuf[i+2]=nxtblk+block_nr;
  dirbuf[i+3]=1; /* mark the entry as read/write */
  dirbuf[0]--;
  dirbuf[3]++;
  if(little) swawbip(dirbuf, MatBLKSIZE);
  fseek(fp, (dirblk-1)*MatBLKSIZE, SEEK_SET);
  if(ftell(fp)!=(dirblk-1)*MatBLKSIZE) return(-15);
  if(fwrite(dirbuf, sizeof(int), MatBLKSIZE/4, fp) != MatBLKSIZE/4) return(-16);
  if(ECAT7_TEST) printf("returning %d from ecat7EnterMatrix()\n", nxtblk);
  return(nxtblk);
}
