/*char RcsId[] = "$Header: /scisoft/users/mirone/SOURCESCVS/specfile/src/sfdata.c,v 1.2 2003/02/12 08:45:31 mirone Exp $"; */
/************************************************************************
 *
 *   File:          sfdata.c
 *
 *   Project:       SpecFile library
 *
 *   Description:   Functions for getting data
 * 
 *   Author:        V.Rey
 *
 *   Date:          $Date: 2003/02/12 08:45:31 $
 *
 ************************************************************************/
/*
 *   Log: $Log: sfdata.c,v $
 *   Log: Revision 1.2  2003/02/12 08:45:31  mirone
 *   Log: just some log comments
 *   Log:
 *   Log: Revision 3.0  2000/12/20 14:17:19  rey
 *   Log: Python version available
 *   Log:
 * Revision 2.1  2000/07/31  19:05:11  19:05:11  rey (Vicente Rey-Bakaikoa)
 * SfUpdate and bug corrected in ReadIndex
 * 
 * Revision 2.0  2000/04/13  13:28:54  13:28:54  rey (Vicente Rey-Bakaikoa)
 * New version of the library. Complete rewrite
 * Adds support for MCA
 * 
 */
#include <SpecFile.h>
#include <SpecFileP.h>

#include <ctype.h>
/*
 * Define macro
 */
#define isnumber(this) ( isdigit(this) || this == '-' || this == '+' || this == '.')

/*
 * Mca continuation character
 */
#define MCA_CONT '\\'
#define D_INFO   3

/*
 * Declarations
 */
DllExport long SfNoDataLines  ( SpecFile *sf, long index, int *error );
DllExport int  SfData         ( SpecFile *sf, long index, double ***retdata, 
                                          long **retinfo, int *error );
DllExport long SfDataAsString ( SpecFile *sf, long index, 
                                          char ***data, int *error ); 
DllExport long SfDataLine     ( SpecFile *sf, long index, long line, 
                                          double **data_line, int *error ); 
DllExport long SfDataCol      ( SpecFile *sf, long index, long col, 
                                          double **data_col, int *error ); 
DllExport long SfDataColByName( SpecFile *sf, long index, 
                                  char *label, double **data_col, int *error ); 


/*********************************************************************
 *   Function:        long SfNoDataLines( sf, index, error )
 *
 *   Description:    Gets number of data lines in a scan
 *
 *   Parameters:
 *        Input :    (1) File pointer   
 *            (2) Index
 *        Output:
 *            (3) error number
 *   Returns:
 *            Number of data lines , 
 *            ( -1 ) => errors.
 *   Possible errors:
 *            SF_ERR_SCAN_NOT_FOUND
 *
 *********************************************************************/
DllExport long
SfNoDataLines( SpecFile *sf, long index, int *error )
{
    long     *dinfo    = NULL;
    double  **data     = NULL;

    int      ret;

    ret = SfData(sf,index,&data,&dinfo,error);

    if (ret == -1) {
        return(-1);
    }

    return(dinfo[ROW]);
}  


/*********************************************************************
 *   Function:        int SfData(sf, index, data, data_info, error)
 *
 *   Description:    Gets data.
 *   Parameters:
 *        Input :    (1) File pointer   
 *            (2) Index
 *        Output:
 *            (3) Data array
 *            (4) Data info : [0] => no_lines
 *                    [1] => no_columns
 *                    [2] = ( 0 ) => regular    
 *                          ( 1 ) => not regular ! 
 *            (5) error number
 *   Returns:
 *            (  0 ) => OK
 *                ( -1 ) => errors occured
 *   Possible errors:
 *            SF_ERR_MEMORY_ALLOC   
 *            SF_ERR_FILE_READ
 *            SF_ERR_SCAN_NOT_FOUND
 *            SF_ERR_LINE_NOT_FOUND
 *
 *   Remark:  The memory allocated should be freed by the application
 *
 *********************************************************************/
DllExport int 
SfData( SpecFile *sf, long index, double ***retdata, long **retinfo, int *error )
{
     long     *dinfo    = NULL;
     double  **data     = NULL;
     double   *dataline = NULL;
     long      headersize;

     char *ptr,
          *from,
          *to;

     char    strval[100];
     double  val;
     double  valline[200];
     long    cols,
             maxcol=200;
     long    rows;
     int     i;

     if (sfSetCurrent(sf,index,error) == -1 )
             return(-1);

     /*
      * Copy if already there
      */
     if (sf->data_info != (long *)NULL) {
          dinfo = ( long * ) malloc ( sizeof(long) * D_INFO);
          dinfo[ROW] = sf->data_info[ROW];
          dinfo[COL] = sf->data_info[COL];
          dinfo[REG] = sf->data_info[REG];
          data =  ( double **) malloc ( sizeof(double *) * dinfo[ROW]); 
          for (i=0;i<dinfo[ROW];i++) {
              data[i] = (double *)malloc (sizeof(double) * dinfo[COL]);
              memcpy(data[i],sf->data[i],sizeof(double) * dinfo[COL]);
          }
          *retdata = data;
          *retinfo = dinfo;
          return(0);
     }

     /*
      * else do the job
      */

     if ( ((SpecScan *)sf->current->contents)->data_offset == -1 ) {
          *retdata = data;
          *retinfo = dinfo;
          return(-1);
     }

     headersize = ((SpecScan *)sf->current->contents)->data_offset
                - ((SpecScan *)sf->current->contents)->offset;

     from = sf->scanbuffer + headersize;
     to   = sf->scanbuffer + ((SpecScan *)sf->current->contents)->size;

     ptr = from;
     rows = -1;
     cols = -1;
     i    = 0;

     /*
      * Alloc memory
      */
     if ( (data = (double **) malloc (sizeof(double *)) ) == (double **)NULL) {
         *error = SF_ERR_MEMORY_ALLOC;
          return(-1);
     }

     if ( (dinfo = (long *) malloc(sizeof(long) * D_INFO) ) == (long *)NULL) { 
          free(data);
         *error = SF_ERR_MEMORY_ALLOC;
          return(-1);
     }

     dinfo[ROW] = dinfo[COL] = dinfo[REG] = 0;

     /*
      * first characters of buffer
      */
     while (*ptr == ' ') ptr++;  /* get rid of empty spaces */

     if (isnumber(*ptr)) {
         rows++; cols++;
         strval[i] = *ptr;
         i++;
     } else if (*ptr == '@') {
        /*
         * read all mca block: go while in buffer ( ptr < to - 1 )
         * and while a newline is preceded by a slash
         */
         for (    ptr = ptr + 2; 
               (*ptr != '\n' || (*(ptr-1) == MCA_CONT)) && ptr < to ; 
                  ptr++); 
     }

    /*
     * continue
     */
     ptr++;

     for ( ; ptr < to - 1; ptr++) {
        /*
         * check for lines and for mca
         */
        if ( *(ptr-1) == '\n' ) {

           if (i != 0) {

              strval[i] = '\0';
              i = 0;
              val = atof(strval);
              valline[cols] = val;

              cols++;
              if ( cols >= maxcol ) return(-1);

              if (dinfo[COL] != 0 && cols != dinfo[COL]) {
                    dinfo[REG] = 1;
              } else {
                    dinfo[COL] = cols;
              }
              dataline = (double *)malloc(sizeof(double) * cols);
              memcpy(dataline,valline,sizeof(double) * cols);
              data = (double **) realloc ( data, sizeof(double) * (rows+1));
              data[rows] = dataline;
           }
   
           while ( *ptr == ' ') ptr++;

           if ( *ptr == '@') {  /* Mca --> pass it all */
               for (    ptr = ptr + 2; 
                     (*ptr != '\n' || (*(ptr-1) == MCA_CONT)) && ptr < to ; 
                        ptr++); 
           } else if ( *ptr == '#') {  /* Comment --> pass one line */
              for (ptr = ptr + 1; *ptr != '\n';ptr++); 
           } else if ( isnumber(*ptr) ) {
                  rows++; cols=0;
                  strval[i] = *ptr;
                  i++;
           } 
        } else {
           if (rows == -1) continue;

           /*
            * in the middle of a line 
            */ 
            if (*ptr == ' ' || *ptr == '\t' ) {
                strval[i] = '\0';
                i = 0;
                val = atof(strval);
                valline[cols] = val;
                cols++;
                while(*(ptr+1) == ' ' || *(ptr+1) == '\t') ptr++;
            } else {
                strval[i] = *ptr;
                i++;
            }
         }
     }

    /*
     * last line
     */
     if (i) {
         strval[i] = '\0';
         val = atof(strval);
         valline[cols] = val;
         cols++;
     }
     if (rows != -1 ) {

         if (dinfo[COL] != 0 && cols != dinfo[COL]) {
              dinfo[REG] = 1;
         } else {
              dinfo[COL] = cols;
         }
         dataline = (double *)malloc(sizeof(double) * cols);
         memcpy(dataline,valline,sizeof(double) * cols);
         data = (double **) realloc ( data, sizeof(double) * (rows+1));
         data[rows] = dataline;

         dinfo[ROW] = rows+1;
     }

   /* 
    * make a copy in specfile structure
    */

    if ( dinfo[ROW] != 0 && dinfo[REG] == 0) {
       sf->data_info = ( long * ) malloc ( sizeof(long) * D_INFO);
       sf->data_info[ROW] = dinfo[ROW];
       sf->data_info[COL] = dinfo[COL];
       sf->data_info[REG] = dinfo[REG];
       sf->data =  ( double **) malloc ( sizeof(double *) * dinfo[ROW]); 
       for (i=0;i<dinfo[ROW];i++) {
           sf->data[i] = (double *)malloc (sizeof(double) * dinfo[COL]);
           memcpy(sf->data[i],data[i],sizeof(double) * dinfo[COL]);
       }
    } else {
       if (dinfo[REG] == 0) free(data);
    }

    *retinfo = dinfo;
    *retdata = data;

     return( 0 );
}


DllExport long 
SfDataCol ( SpecFile *sf, long index, long col, double **retdata, int *error )
{
    double *datacol=NULL;

    long     *dinfo    = NULL;
    double  **data     = NULL;

    long     selection;
    int      i,ret;

    ret = SfData(sf,index,&data,&dinfo,error);

    if (ret == -1) {
        *error = SF_ERR_COL_NOT_FOUND; 
        *retdata = datacol;
        return(-1);
    }

    if (col < 0) {
       selection = dinfo[COL] + col;
    } else {
       selection = col - 1;
    } 

    if ( selection < 0 || selection > dinfo[COL] - 1) {
       *error  = SF_ERR_COL_NOT_FOUND;
        if ( dinfo != (long *)NULL) {
           freeArrNZ((void ***)&data,dinfo[ROW]);
        }
        free(dinfo);
        return(-1);
    }

    datacol = (double *) malloc( sizeof(double) * dinfo[ROW]);
    if (datacol == (double *)NULL) {
       *error  = SF_ERR_MEMORY_ALLOC;
       if ( dinfo != (long *)NULL)
             freeArrNZ((void ***)&data,dinfo[ROW]);
       free(dinfo);
       return(-1);
    }

    for (i=0;i<dinfo[ROW];i++) {
       datacol[i] = data[i][selection];
    }

    ret = dinfo[ROW];

    if ( dinfo != (long *)NULL)
           freeArrNZ((void ***)&data,dinfo[ROW]);
    free(dinfo);

   *retdata = datacol;
    return(ret);
}


DllExport long 
SfDataLine( SpecFile *sf, long index, long line, double **retdata, int *error ) 
{
    double *datarow=NULL;

    long     *dinfo    = NULL;
    double  **data     = NULL;

    long     selection;
    int      ret;

    ret = SfData(sf,index,&data,&dinfo,error);

    if (ret == -1) {
        *error = SF_ERR_LINE_NOT_FOUND; 
        *retdata = datarow;
        return(-1);
    }

    if (line < 0) {
       selection = dinfo[ROW] + line;
    } else {
       selection = line - 1;
    } 

    if ( selection < 0 || selection > dinfo[ROW] - 1) {
       *error  = SF_ERR_LINE_NOT_FOUND;
        if ( dinfo != (long *)NULL) {
           freeArrNZ((void ***)&data,dinfo[ROW]);
        }
        free(dinfo);
        return(-1);
    }

    datarow = (double *) malloc( sizeof(double) * dinfo[COL]);
    if (datarow == (double *)NULL) {
       *error  = SF_ERR_MEMORY_ALLOC;
       if ( dinfo != (long *)NULL)
             freeArrNZ((void ***)&data,dinfo[ROW]);
       free(dinfo);
       return(-1);
    }


    memcpy(datarow,data[selection],sizeof(double) * dinfo[COL]);

    ret = dinfo[COL];

    if ( dinfo != (long *)NULL)
           freeArrNZ((void ***)&data,dinfo[ROW]);
    free(dinfo);

   *retdata = datarow;
    return(ret);
}


DllExport long 
SfDataColByName( SpecFile *sf, long index, char *label, double **retdata, int *error ) 
{

      double *datacol;

      long     *dinfo    = NULL;
      double  **data     = NULL;

      int      i,ret;

      char **labels = NULL;

      long  nb_lab,
            idx;

      short tofree=0;

      if ( sfSetCurrent(sf,index,error) == -1) {
        *retdata = (double *)NULL;
         return(-1);
      }

      if ( sf->no_labels != -1 ) {
         nb_lab = sf->no_labels;
         labels = sf->labels;
      } else {
         nb_lab = SfAllLabels(sf,index,&labels,error);
         tofree = 1;
      }
         
      if ( nb_lab == 0 || nb_lab == -1) {
            *retdata = (double *)NULL;
             return(-1);
      }

      for (idx=0;idx<nb_lab;idx++) 
          if (!strcmp(label,labels[idx])) break;

      if ( idx == nb_lab ) {
          if  (tofree) freeArrNZ((void ***)&labels,nb_lab);
         *error = SF_ERR_COL_NOT_FOUND;
         *retdata = (double *)NULL;
          return(-1);
      }

      ret = SfData(sf,index,&data,&dinfo,error);

      if (ret == -1) {
         *retdata = (double *)NULL;
          return(-1);
      }

      datacol = (double *) malloc( sizeof(double) * dinfo[ROW]);
      if (datacol == (double *)NULL) {
          *error  = SF_ERR_MEMORY_ALLOC;
           if ( dinfo != (long *)NULL)
              freeArrNZ((void ***)&data,dinfo[ROW]);
           free(dinfo);
          *retdata = (double *)NULL;
           return(-1);
      }

      for (i=0;i<dinfo[ROW];i++) {
         datacol[i] = data[i][idx];
      }

      ret = dinfo[ROW];

      if ( dinfo != (long *)NULL)
           freeArrNZ((void ***)&data,dinfo[ROW]);
      free(dinfo);

     *retdata = datacol;
    
      return(ret);
}


DllExport long 
SfDataAsString( SpecFile *sf, long index, char ***retdata, int *error ) 
{
     char **data=NULL;
     char   oneline[300];

     char *from,
          *to,
          *ptr,
          *dataline;

     long  headersize,rows;
     int   i;

     if (sfSetCurrent(sf,index,error) == -1 )
             return(-1);

     if ( ((SpecScan *)sf->current->contents)->data_offset == -1 ) {
          *retdata = data;
          return(-1);
     }

     data = (char **) malloc (sizeof(char *)); 

     headersize = ((SpecScan *)sf->current->contents)->data_offset
                - ((SpecScan *)sf->current->contents)->offset;

     from = sf->scanbuffer + headersize;
     to   = sf->scanbuffer + ((SpecScan *)sf->current->contents)->size;

     rows = -1;
     i    = 0;

    /*
     * first characters of buffer
     */

     ptr = from;

     if (isnumber(*ptr)) {
         rows++;
         oneline[i] = *ptr;
         i++; 
     } else if (*ptr == '@') {
        /*
         * read all mca block: go while in buffer ( ptr < to - 1 )
         * and while a newline is preceded by a slash
         */
         for (    ptr = ptr + 2; 
               (*(ptr+1) != '\n' || (*ptr == MCA_CONT)) && ptr < to - 1 ; 
                  ptr++); 
     }

    /*
     * continue
     */
     ptr++;

     for ( ; ptr < to - 1; ptr++) {
        /*
         * check for lines and for mca
         */
        if ( *(ptr-1) == '\n' ) {

           if ( i != 0 ) {
              oneline[i-1] = '\0';
              i = 0;

              dataline = (char *)strdup(oneline);
              data = (char **) realloc ( data, sizeof(char *) * (rows +1));
              data[rows] = dataline;
           }

           if ( *ptr == '@') {  /* Mca --> pass it all */
               for (    ptr = ptr + 2; 
                     (*ptr != '\n' || (*(ptr-1) == MCA_CONT)) && ptr < to ; 
                        ptr++); 
           } else if ( *ptr == '#') {  /* Comment --> pass one line */
              for (ptr = ptr + 1; *ptr != '\n';ptr++); 
           } else if ( isnumber(*ptr) ) {
              rows++;
              oneline[i] = *ptr;
              i++;
           } 
        } else { 
           if (rows == -1) continue;

           oneline[i] = *ptr;
           i++;
        }
     }

    /*
     * last line
     */

     if (rows != -1 && i) {
         oneline[i-1] = '\0';
         dataline = (char *)strdup(oneline);
         data = (char **) realloc ( data, sizeof(char *) * (rows+1));
         data[rows] = dataline;
     }

    *retdata = data;
     return(rows+1);
}
