/* %W%  %G% */
/*
 *  include files
 */

#include <stdio.h>
#include <malloc.h>
#include <strings.h>

/*
 *  function prototypes
 */

int ReadRINEXObsTypes( FILE *, int, int *, char ** );
int WriteRINEXHeader( FILE *, FILE * );
int WriteRINEXObs( FILE *, FILE *, int, char ** );

/*
 *  global definitions and variables
 */

#define MAX_VALUES_PER_LINE  5
#define PRN_MAX      ((int)36)
#define TYPE_LENGTH    6
#define TYPE_MAX    20
#define VALUE_LENGTH  16
#define VALUE_MAX    20
#define buf_MAX      82

char buf1[buf_MAX+1]= { "\0" };
char buf2[buf_MAX+1]= { "\0" };

static char *sccsid= "%W%  %G%";


int ReadRINEXObsTypes( FILE *ptr, int max_list, int *num_list, char **list )
/********1*********2*********3*********4*********5*********6*********7*********
 * name:            ReadRINEXObsTypes
 * version:         9510.27
 * written by:      M. Schenewerk
 * purpose:         reads a RINEX o file header for observable types
 *
 * input parameters
 * ----------------
 * ptr              pointer to file
 * max_list         maximum items allowed in list
 * num_list         number of items in list
 * list             list of observable type names
 *
 * output parameters
 * -----------------
 *
 *
 * local variables and constants
 * -----------------------------
 *
 * global variables and constants
 * ------------------------------
 *
 *
 * called by:       
 *
 * calls:           
 *
 * include files:   
 *
 * references:      
 *
 * comments:        
 *
 * see also:        
 *
 ********1*********2*********3*********4*********5*********6*********7*********
 *:modification history
 *:9510.27, MSS, Creation
 ********1*********2*********3*********4*********5*********6*********7*********/
{
  char *flag_read= NULL;
  int flag_list= 0;
  int i;
  int ios= 0;

  rewind( ptr );
  while( flag_list == 0 &&
         ios == 0 &&
         ( flag_read= fgets( buf1, buf_MAX+1, ptr ) ) != NULL ) {
    if( strncmp( buf1+60, "# / T", (size_t)5 ) == 0 ) {
      *num_list= atoi( buf1 );
      if( *num_list <= max_list ) {
        for( i= 0; i < *num_list; i++ ) {
          if( ( list[i]= (char *)malloc( (size_t)(TYPE_LENGTH+1) ) ) != NULL ) {
            strncpy( list[i], buf1+6+(6*i), TYPE_LENGTH );
            list[i][TYPE_LENGTH]= '\0';
          } else {
            ios= 3;
            break;
          }
        }
      } else {
        ios= 2;
      }
      flag_list= 1;
    } else if ( strncmp(buf1+60,"END O",(size_t)5) == 0 ) {
      ios= 1;
    }
  }

  if( flag_read == NULL ) {
    ios= -1;
  }
  return( ios );
}



int WriteRINEXHeader( FILE *target, FILE *source )
/********1*********2*********3*********4*********5*********6*********7*********
 * name:            WriteRINEXHeader
 * version:         9610.07
 * written by:      M. Schenewerk
 * purpose:         reads and outputs a RINEX o file header
 *
 * input parameters
 * ----------------
 * target           target file
 * source           source file
 *
 * output parameters
 * -----------------
 *
 *
 * local variables and constants
 * -----------------------------
 *
 * global variables and constants
 * ------------------------------
 *
 *
 * called by:       
 *
 * calls:           
 *
 * include files:   
 *
 * references:      
 *
 * comments:        
 *
 * see also:        
 *
 ********1*********2*********3*********4*********5*********6*********7*********
 *:modification history
 *:9510.27, MSS, Creation
 *:9607.31, MSS, test value string and pad with blanks
 *:9610.07, MSS, add option for user specified output data interval
 ********1*********2*********3*********4*********5*********6*********7*********/
{
  char *flag_read= NULL;
  int j;
  int ios= 0;
  extern int user_int;

  rewind( source );
  while( ( flag_read= fgets( buf1, buf_MAX+1, source ) ) != NULL ) {
    buf1[buf_MAX]= '\0';
    for( j= 0; j < strlen(buf1); j++ ) {
      if( buf1[j] == '\n' ||
          buf1[j] == '\015' ) {
        buf1[j]= '\0';
      }
    }
    if ( user_int > 0 && strncmp( buf1+60, "INTER", (size_t)5 ) == 0 ) {
      sprintf( buf1,
        "%6d                                                      INTERVAL            ",
        user_int );
    }

    fputs( buf1, target );
    fputs( "\n", target );
    if ( strncmp( buf1+60, "END O", (size_t)5 ) == 0 ) {
      break;
    }
  }

  if( flag_read == NULL ) {
    ios= -1;
  }
  return( ios );
}



int WriteRINEXObs( FILE *target, FILE *source, int nlist, char **list )
/********1*********2*********3*********4*********5*********6*********7*********
 * name:            WriteRINEXObs
 * version:         0202.14
 * written by:      M. Schenewerk
 * purpose:         reads and output RINEX o file data
 *
 * input parameters
 * ----------------
 * target           target file
 * source           source file
 * nlist            number of observable types
 * list             observable types
 *
 * output parameters
 * -----------------
 *
 *
 * local variables and constants
 * -----------------------------
 *
 * global variables and constants
 * ------------------------------
 *
 *
 * called by:       
 *
 * calls:           
 *
 * include files:   
 *
 * references:      
 *
 * comments:        
 *
 * see also:        
 *
 ********1*********2*********3*********4*********5*********6*********7*********
 *:modification history
 *:9510.27, MSS, Creation
 *:9607.31, MSS, test value string and pad with blanks in sect 4.2
 *:9610.07, MSS, add option for user specified output data interval
 *:0106.26, MSS, Explicity read and parrot characters indicating satellite
 *:              type from epoch lines.
 *:0201.29, MSS, Correct reading and writing epoch lines so that epochs
 *:              containing more than 12 satellites are handled correctly.
 *:0202.14, MSS, Trap and print, no interpretation is done, imbedded
 *:              header lines. This is a temporary fix for a common
 *:              occurance. This program will be rewritten to be more
 *:              general.
 *:0210.15, MSS, Correct trap for event markers. Only 2-5 skip subsequent
 *:              lines, not 1-5 as originally coded.
 ********1*********2*********3*********4*********5*********6*********7*********/
{
  char *bad_char= NULL;
  char *flag_read= NULL;
  char *types[TYPE_MAX];
  char *value[VALUE_MAX+1];
  char prntype[PRN_MAX];
  double xsec;
  int i;
  int ios= 0;
  int j;
  int min;
  int new_order[TYPE_MAX];
  int nprn;
  int nsat;
  int ntypes;
  int prn[PRN_MAX];
  int records= 0;
  int sat;
  int sec;
  int skip;
  extern int user_int;
/*
 *  1.0  get observable types
 */
  rewind( source );
  ios= ReadRINEXObsTypes( source, TYPE_MAX, &ntypes, types );
  if( ios != 0 ) {
    return( records );
  }
/*
 *  1.1  match current file obs types to input list
 */
  for( i= 0; i < nlist; i++ ) {
    new_order[i]= VALUE_MAX;
    for( j= 0; j < ntypes; j++ ) {
      if( strcmp( list[i], types[j] ) == 0 ) {
        new_order[i]= j;
        break;
      }
    }
  }
/*
 *  2.0  allocate space for data
 */
  for( i= 0; i < ntypes; i++ ) {
    if( ( value[i]= (char *)malloc( (size_t)(VALUE_LENGTH+1) ) ) == NULL ) {
      return( records );
    }
  }
  if( ( value[VALUE_MAX]= (char *)malloc( (size_t)(VALUE_LENGTH+1) ) ) == NULL ) {
    return( records );
  }
  for( i= 0; i < VALUE_LENGTH; i++ ) {
    value[VALUE_MAX][i]= ' ';
  }
  value[VALUE_MAX][VALUE_LENGTH]= '\0';
/*
 *  3.0  scan past header
 */
  rewind( source );
  while( ( flag_read= fgets( buf1, buf_MAX+1, source ) ) != NULL ) {
    if ( strncmp( buf1+60, "END O", (size_t)5 ) == 0 ) {
      break;
    }
  }
  if( flag_read == NULL ) {
    return( records );
  }
/*
 *  4.0  loop over the file outputing the time and the matching
 *       obs type in the order of the input list
 */
  for( ; ; ) {

    nsat = 0;
/*
 *  4.1  read epoch line and perform a value check on the satellite PRN numbers
 */
    if( ( flag_read= fgets( buf1, buf_MAX+1, source ) ) != NULL ) {
      for( j= 0; j < strlen(buf1); j++ ) {
        if( buf1[j] == '\n' ||
            buf1[j] == '\015' ) {
          buf1[j]= '\0';
        }
      }

/* This does not interpret new header info and is, therefore, a crude fix. */
      if( buf1[28] == '2' ||
          buf1[28] == '3' ||
          buf1[28] == '4' ||
          buf1[28] == '5' ) {
        fputs( buf1, target );
        fputs( "\n", target );

        skip = atoi( buf1+29 );
        j = 0;
        while( j < skip && fgets( buf1, buf_MAX+1, source ) != NULL ) {
          fputs( buf1, target );
          ++j;
        }
        if( j < skip )
          return( records );
        else
          continue;
      }

      nprn= atoi( buf1+29 );
      if( 0 > nprn || nprn > PRN_MAX ) {
        return( records );
      }

      for( sat= 0; sat < 12 && nsat < nprn; sat++ ) {
        prntype[nsat] = buf1[32 + (3 * sat)];
        prn[nsat]= atoi( buf1 + 32 + (3 * sat) + 1 );
        if( 0 >= prn[nsat] || prn[nsat] > PRN_MAX ) {
          return( records );
        }
        nsat++;
      }
    } else {
      return( records );
    }

    sscanf( buf1+12, "%d %lf", &min, &xsec );
    sec= (int)xsec;
    if( (xsec - (double)sec) > 0.5 )
      sec= sec + 1;
    sec= sec + 60*min;
/*
 *  4.2  if the number of satellites is greater than 12, the satellite
 *       list is continued to the next line.
 */
    while( nsat < nprn ) {
      if( ( flag_read= fgets( buf2, buf_MAX+1, source ) ) != NULL ) {
        for( j= 0; j < strlen(buf2); j++ ) {
          if( buf2[j] == '\n' ||
              buf2[j] == '\015' ) {
            buf2[j]= '\0';
          }
        }

        for( sat= 0; sat < 12 && nsat < nprn; sat++ ) {
          prntype[nsat] = buf2[32 + (3 * sat)];
          prn[nsat]= atoi( buf2 + 32 + (3 * sat) + 1 );
          if( 0 >= prn[nsat] || prn[nsat] > PRN_MAX ) {
            return( records );
          }
          nsat++;
        }
      } else {
        return( records );
      }
    }
/*
 *  4.3  rewrite the epoch line(s)
 */
    if( user_int == 0 || sec % user_int == 0 ) {
      buf1[29]= '\0';
      fputs( buf1, target );
      fprintf( target, "%3d", nprn );

      for( sat= 0; sat < nprn; sat++ ) {
        if( sat != 0 && (sat%12) == 0 ) {
          fputs( "\n                                ", target );
        }
        fprintf( target, "%c%2.2d", prntype[sat], prn[sat] );
      }
      fputs( "\n", target );
    }
/*
 *  4.4  read data line(s)
 *       NOTE: no value checking is done here.
 */
    for( sat= 0; sat < nprn; sat++ ) {
      for( i= 0; i < ntypes; i++ ) {
        j= i % MAX_VALUES_PER_LINE;
        if( j == 0 ) {
          if( ( flag_read= fgets( buf2, buf_MAX+1, source ) ) == NULL ) {
            return( records );
          }
        }
        strncpy( value[i], buf2 + (VALUE_LENGTH * j), VALUE_LENGTH );
        value[i][VALUE_LENGTH]= '\0';
        for( j= 0; j < VALUE_LENGTH; j++ ) {
          if( value[i][j] == '\n' ||
              value[i][j] == '	' ||
              value[i][j] == '\015' ||
              value[i][j] == '\0' ) {
            value[i][j]= ' ';
          }
        }

      }
/*
 *  4.5  output values in the order of the input list
 */
      if( (user_int == 0 || sec % user_int == 0) &&
          (0 < prn[sat] && prn[sat] <= PRN_MAX) ) {
        for( i= 0; i < nlist; i++ ) {
          j= i % MAX_VALUES_PER_LINE;
          if( j == 0 && i > 0 ) {
            fputs( "\n", target );
          }
          fputs( value[new_order[i]], target );
        }
        fputs( "\n", target );
      }
    }

    records++;
  }
}
