/***************************************************************
* PrMatlab.cpp
*
* This class provides methods to save data in matlab format.
*
****************************************************************/

/*
* modification history
*----------------------
*
* 01/10/03: Weyn Ong: Version for RTLinux.
* 04/01/98: K.C. Chang: created.
*/

#include "PrMatlab.h"
#include <stdio.h>
#include <string.h>

using std::queue;

typedef struct 
{
   long	type;
   long	mrows;
   long	ncols;
   long	imagf;
   long	namlen;
} Fmatrix;

PrMatlab::PrMatlab() {
   filePtr_ = NULL;
   endian_ = getEndian();
}

PrMatlab::~PrMatlab() {
   end();
}

int 
PrMatlab::start( const char * filename, char * matrixName, PrMatlabType type ) {
   if (filePtr_ != NULL)
      end();

   filePtr_ = fopen(filename,"w");
   if (filePtr_ == NULL) {
      fprintf(stderr,"unable to open outfile %s\n",filename);
      return PR_MATLAB_ERROR;
   }
   this->matrixName = strdup( matrixName );
   this->dataType = type;
   numCols = 0;
   curRow = 0;
   buf = new char[BUFFER_SIZE];
   while ( !bufQueue.empty() )
      bufQueue.pop();

   return PR_MATLAB_OK;
}

void 
PrMatlab::save( void * data, int numCols ) {
   if ( this->numCols == 0 ) {
      this->numCols = numCols;
      switch ( dataType ) {
            case PR_MATLAB_DOUBLE :
               rowSize = numCols * 8;
               break;
            default :
               rowSize = numCols * 4;
               break;
      }
   }
   else if ( this->numCols != numCols ) {
      fprintf( stderr, "Attempted to change number of columns in gather matrix.\n" );
      exit( EXIT_FAILURE );
   }

   if ( (curRow+1)*rowSize > BUFFER_SIZE ) {
      // Can't fit this new row into the current buffer.
      // Create a new buffer and add to the queue.
      bufQueue.push( buf );
      buf = new char[BUFFER_SIZE];
      if ( buf == NULL ) {
         fprintf( stderr, "Couldn't create gather buffer.\n" );
         exit( EXIT_FAILURE );
      }
      curRow = 0;
   }

   memcpy( static_cast<void *>(&buf[curRow*rowSize]), data, rowSize );
   curRow++;
}

void 
PrMatlab::end() {
   if ( filePtr_ == NULL )
      return;

   int flag;
   Fmatrix h;

   int numRows = (bufQueue.size() * (BUFFER_SIZE / rowSize)) + curRow;

   if ( numRows == 0 || numCols == 0)
      return;

   if (endian_ == BIG)
      flag = 1000;
   else
      flag = 0;

   /* undocumented - allows matrix in row format */
   flag += 100;

   flag += dataType;

   h.type = flag;
   h.mrows = numRows;
   h.ncols = numCols;
   h.imagf = 0;
   h.namlen = strlen( matrixName )+1;

   fwrite( &h, sizeof(Fmatrix), 1, filePtr_ );
   fwrite( matrixName, h.namlen, 1, filePtr_ );

   while ( !bufQueue.empty() ) {
      char * curBuf = bufQueue.front();
      bufQueue.pop();
      fwrite( curBuf, BUFFER_SIZE/rowSize, rowSize, filePtr_ );
      delete [] curBuf;
   }

   fwrite( buf, curRow, rowSize, filePtr_ );
   delete [] buf;
   buf = NULL;

   fclose(filePtr_);
   filePtr_ = NULL;
}

// little endian
//   [0] [1] [2] [3]
//    1   0   0   0
// big endian
//   [0] [1] [2] [3]
//    0   0   0   1
PrMatlab::Endian
PrMatlab::getEndian()
{
   int aint=1;
   char *cptr = (char *)&aint;
   if (*cptr == '\0')
      return BIG; // 1 => big endian
   else
      return LITTLE;
}
