/* %W% %E% */
/*
 *  include files
 */

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#include <ctype.h>

#include "RINEX.h"

/*
 *  function prototypes
 */

int GetLastRINEXEpoch( FILE *, int, char **, struct RINEX_obs * );
int GetNextRINEXEpoch( FILE *, int, char **, struct RINEX_obs * );
int FindRINEXEndOfHeader( FILE * );
int ReadRINEXObsTypes( FILE *, int *, char ** );
int WriteRINEXHeader( FILE *, FILE * );
int WriteRINEXObs( FILE *, FILE *, int, char ** );

long ymdmjd( int, int, int );
double hmsday( int, int, double );

/*
 *  global definitions and variables
 */

char buf1[MAX_LINE]= { "" };
char buf2[MAX_LINE]= { "" };

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


int ReadRINEXObsTypes( FILE *ptr, 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
 * 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, MAX_LINE, ptr ) ) != NULL ) {
    if( strncmp( buf1+60, "# / T", (size_t)5 ) == 0 ) {
      *num_list= atoi( buf1 );
      if( *num_list <= TYPE_MAX ) {
        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:         9607.31
 * 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
 ********1*********2*********3*********4*********5*********6*********7*********/
{
  char *flag_read= NULL;
  int j;
  int ios= 0;

  rewind( source );
  while( ( flag_read= fgets( buf1, MAX_LINE, source ) ) != NULL ) {
    buf1[MAX_LINE-1]= '\0';
    for( j= 0; j < strlen(buf1); j++ ) {
      if( buf1[j] == '\n' ||
          buf1[j] == '\015' ) {
        buf1[j]= '\0';
      }
    }
    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:         9607.31
 * 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
 ********1*********2*********3*********4*********5*********6*********7*********/
{
  char *bad_char= NULL;
  char *flag_read= NULL;
  char *types[TYPE_MAX];
  char *value[VALUE_LENGTH+1];
  int i;
  int ios= 0;
  int j;
  int new_order[TYPE_MAX];
  int nsat;
  int ntypes;
  int records= 0;
  int sat;
/*
 *  1.0  get observable types
 */
  rewind( source );
  ios= ReadRINEXObsTypes( source, &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, MAX_LINE, 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( ; ; ) {
/*
 *  4.1  read epoch line and perform a value check on the satellite PRN numbers
 */
    if( ( flag_read= fgets( buf1, MAX_LINE, source ) ) != NULL ) {
      for( j= 0; j < strlen(buf1); j++ ) {
        if( buf1[j] == '\n' ||
            buf1[j] == '\015' ) {
          buf1[j]= '\0';
        }
      }
      nsat= atoi( buf1+29 );
      if( 0 > nsat || nsat > PRN_MAX ) {
        return( records );
      }
      for( i= 0; i < nsat; i++ ) {
        sat= atoi( buf1 + 32 + (3 * j) );
        if( 0 > sat || sat > PRN_MAX ) {
          return( records );
        }
      }
    } else {
      return( records );
    }

    fputs( buf1, target );
    fputs( "\n", target );
/*
 *  4.2  read data line(s) and output values in the order of the input list
 *       NOTE: no value checking is done here.
 */
    for( sat= 0; sat < nsat; sat++ ) {
      for( i= 0; i < ntypes; i++ ) {
        j= i % MAX_VALUES_PER_LINE;
        if( j == 0 ) {
          if( ( flag_read= fgets( buf2, MAX_LINE, 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.3  output values in the order of the input list
 */
      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++;
  }
}



int FindRINEXEndOfHeader( FILE *ptr )
/********1*********2*********3*********4*********5*********6*********7*********
 * name:            FindRINEXEndOfHeader
 * version:         9612.15
 * written by:      M. Schenewerk
 * purpose:         reads a RINEX o file stopping at the end of the header
 *
 * input parameters
 * ----------------
 *
 * 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
 *:9612.15, MSS, Creation
 ********1*********2*********3*********4*********5*********6*********7*********/
{
  int ios;

  rewind( ptr );
  while( fgets( buf1, MAX_LINE, ptr ) != NULL )
    if( ( strncmp(buf1+60,"END O",(size_t)5) == 0 ) ) {
      ios= 0;
      return( ios );
    }

        ios= 1;
  return( ios );
}



int GetNextRINEXEpoch( FILE *ptr, int num_list, char *list[],
                       struct RINEX_obs *obs )
/********1*********2*********3*********4*********5*********6*********7*********
 * name:            GetNextRINEXEpoch
 * version:         0503.27
 * written by:      M. Schenewerk
 * purpose:         reads and returns the data from the next RINEX o record
 *
 * input parameters
 * ----------------
 *
 * 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
 *:9612.15, MSS, Creation
 *:0503.27, MSS, Trap and work past corrupt lines.
 *:0503.29, MSS, When read epoch data, remove minimum limit on line length.
 ********1*********2*********3*********4*********5*********6*********7*********/
{
  char buf[MAX_LINE];
  char tmp[16];
  double sec;
  double val;
  int day;
  int hr;
  int i;
  int ios;
  int j;
  int min;
  int mon;
  int yr;
/*
 *   1.0  Read epoch line
 */
  if( fgets( buf, MAX_LINE, ptr ) == NULL ) {
          ios= 1;
          return( ios );
  } else if( strncmp( buf, "END O", 5 ) == 0 ) {
          ios= 0;
          return( ios );
  } else if( strlen(buf) < 32 ||
             strlen(buf) > MAX_VALID_LINE ||
             buf[3] != ' ' ||
             buf[6] != ' ' ||
             buf[9] != ' ' ||
             buf[12] != ' ' ||
             buf[15] != ' ' ||
             buf[26] != ' ' ||
             buf[29] != ' ' ) {
    return( GetNextRINEXEpoch( ptr, num_list, list, obs ) );
        }
  yr= atoi( buf );  
  mon= atoi( buf+3 );  
  day= atoi( buf+6 );  
  obs->mjd= ymdmjd( yr, mon, day );

  hr= atoi( buf+9 );  
  min= atoi( buf+12 );  
  sec= atof( buf+15 );  
  obs->day= hmsday( hr, min, sec );

  obs->nsv= atoi( buf+29 );  
  for( i= 0; i < obs->nsv; i++ )
    obs->sv[i]= atoi( buf+32+i*3+1 );
/*
 *   2.0  Read obs
 */
  for( i= 0; i < obs->nsv; i++ ) {
    obs->L1phs[i]= EDIT;
    obs->L2phs[i]= EDIT;
    obs->L1rng[i]= EDIT;
    obs->L2rng[i]= EDIT;
    for( j= 0; j < num_list; j++ ) {
      if( j%5 == 0 && fgets( buf, MAX_LINE, ptr ) == NULL ) {
        ios= 1;
        return( ios );
      } else if( j%5 == 0 &&
        strlen(buf) > MAX_VALID_LINE ) {
        return( GetNextRINEXEpoch( ptr, num_list, list, obs ) );
      }
      val = 0.0;
      if (strlen (buf) > (j%5)*VALUE_LENGTH) {
        strncpy( tmp, buf+((j%5)*VALUE_LENGTH), VALUE_LENGTH-1 );
        tmp[VALUE_LENGTH]= '\0';
        val= atof( tmp );
      }
      if( strcmp( list[j], TYPE_C1 ) == 0 && val != (double)0 )
        obs->L1rng[i]= val;
      else if( strcmp( list[j], TYPE_P1 ) == 0 && val != (double)0 )
        obs->L1rng[i]= val;
      else if( strcmp( list[j], TYPE_L1 ) == 0 )
        obs->L1phs[i]= val;
      else if( strcmp( list[j], TYPE_P2 ) == 0 )
        obs->L2rng[i]= val;
      else if( strcmp( list[j], TYPE_L2 ) == 0 )
        obs->L2phs[i]= val;
      
    }
  }

  ios= 0;
  return( ios );
}



int GetLastRINEXEpoch( FILE *ptr, int num_list, char *list[],
                       struct RINEX_obs *obs )
/********1*********2*********3*********4*********5*********6*********7*********
 * name:            GetLastRINEXEpoch
 * version:         9612.15
 * written by:      M. Schenewerk
 * purpose:         reads and returns the data from the previous RINEX o record
 *
 * input parameters
 * ----------------
 *
 * output parameters
 * -----------------
 *
 *
 * local variables and constants
 * -----------------------------
 *
 * global variables and constants
 * ------------------------------
 *
 *
 * called by:       
 *
 * calls:           
 * GetNextRINEXEpoch reads and returns the data from the previous RINEX o record
 *
 * include files:   
 *
 * references:      
 *
 * comments:        
 *
 * see also:        
 *
 ********1*********2*********3*********4*********5*********6*********7*********
 *:modification history
 *:9804.04, MSS, Creation
 ********1*********2*********3*********4*********5*********6*********7*********/
{
  char buf[MAX_LINE];
  char c;
  int i;
  int ios;
  long here;
/*
 *   1.0  Loop to scan backwards
 */
        i= MAX_LINE - 1;
        buf[i]= '\0';
  while( fseek( ptr, -2L, SEEK_CUR ) == 0 ) {
    c= fgetc( ptr );
                if( c == '\n' ) {
      if( (MAX_LINE - i) >= 33 &&
          isdigit( buf[i+2] ) &&
          isblank( buf[i+3] ) &&
          isdigit( buf[i+5] ) &&
          isblank( buf[i+6] ) &&
          isdigit( buf[i+8] ) &&
          isblank( buf[i+9] ) &&
          isdigit( buf[i+11] ) &&
          isblank( buf[i+12] ) &&
          isdigit( buf[i+14] ) &&
          isblank( buf[i+15] ) &&
          buf[i+18] == '.' ) {
        here= ftell( ptr );
        ios= GetNextRINEXEpoch( ptr, num_list, list, obs );
        fseek( ptr, here, SEEK_SET );
        return( ios );
      } else {
              i= MAX_LINE - 1;
      }
    } else if( i > 0 ) {
      i--;
      buf[i]= c;
    } else {
      ios= MAX_LINE;
      return( ios );
    }
  }
/*
 *   2.0  trap for rewinding to beginning of file
 */
  if( (MAX_LINE - i) >= 33 ) {
    ios= GetNextRINEXEpoch( ptr, num_list, list, obs );
    rewind( ptr );
  } else {
    ios= MAX_LINE;
  }

  return( ios );
}
