#ifndef _NMEA_H_
#define _NMEA_H_

/** 
    \file NMEA.h
*/
/*
   NMEA.h - Class encapsulating NMEA message strings.

   The NMEA class is an abstract base class used to represent NMEA sentence (message)
   strings. Each different NMEA sentence has an array of Field objects, each 
   of which represents one field (token) in the sentence.
   NMEA declares methods to set various types of fields, get Fields, 
   calculate checksums, and generate the NMEA string (an abstract method).
   Subclasses are expected to provide methods for setting their Field members 
   (which should enforce type checking and call the setField methods in NMEA),
   and implement the toString() method.

   Currently GPGGA and GPRMC message types are implemented.

   NMEA does not currently implement parse methods (to read in NMEA from a 
   string), since it was developed to synthesize NMEA strings.

  copyright MBARI 2008 
  author k. headley
*/

/****************************************************************************/
/* Copyright (c) 2008 MBARI                                                 */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
/* Summary  :                                                               */
/* Filename : NMEA.h                                                        */
/* Author   : K Headley                                                     */
/* Project  : Benthic Imaging AUV                                           */
/* Created  : 04/01/2008                                                    */
/****************************************************************************/
/* Modification History:                                                    */
/*  $Id: NMEA.h,v 1.5.4.1 2016/05/19 18:28:57 headley Exp $   */
/*  $Name: gnc $ */
/****************************************************************************/

#ifdef _QNX
#include <strstream.h>
#include <time.h>
#include <sys/time.h>
#include <iostream.h>
#include <iomanip.h>
#else
#include <iostream>
#include <iomanip>
#include <sstream>
#include <ctime>
#include <sys/time.h>
#endif

#ifndef _QNX
using namespace std;
#endif

/** Max NMEA sentence name field length. */
#define MAX_NMEA_NAME 7
/** Max NMEA sentence fields. */
#define MAX_NMEA_FIELDS 32
/** Max NMEA sentence length. */
#define MAX_NMEA_LEN 128

/** Field represents a generic NMEA field.
    A field can have one of several types 
    There are set/get methods that are used to
    change or access the field's values.

    @see Field::FieldType
 */
class Field{
  friend ostream& operator<<(ostream& output, const Field& n);
public:
  /** NMEA field type enum */
  typedef enum FIELD_TYPES {INT=0,UINT,DOUBLE,UCHAR,CHAR,PCHAR,TIMET,DATET,TVALT} FieldType;/**< Field data type enumeration */

  Field();
  Field(char *value);
  Field(char value);
  Field(int value);
  Field(unsigned int value);
  Field(double value);
  Field(unsigned char value);
  Field(time_t value);
  Field(struct timeval value);
  FieldType getType();
  char *pcharValue();
  int intValue();
  unsigned int uintValue();
  unsigned char ucharValue();
  char charValue();
  double doubleValue();
  time_t timeValue();
  struct timeval tvalValue();
  void setValue(int value);
  void setValue(unsigned int value);
  void setValue(char *value);
  void setValue(char value);
  void setValue(unsigned char value);
  void setValue(double value);
  void setValue(time_t value);
  void setValue(struct timeval value);
  /** Represent Field as string. */
   const char *toString()const{
#ifdef _QNX
    ostrstream outs;
    outs << *this <<ends;
    return outs.str();
#else
    ostringstream outs;
    outs << *this;
    return outs.str().c_str();
#endif
  } 

 private:
  /** Primitive data type of this field. 
      @see FieldType.
   */
  FieldType _type;
  /** Field name */
  char*_name;
  /** char ptr value */
  char *_pcharValue;
  /** char value */
  char _charValue;
  /** int value */
  int _intValue;
  /** uint value */
  unsigned int _uintValue;
  /** uchar value */
  unsigned char _ucharValue;
  /** double value */
  double _doubleValue;
  /** time_t value */
  time_t _timeValue;
  /** timeval value */
  struct timeval _tvalValue;

};/* class Field */

/** Invalid NMEA Exception.
    Indicates that NMEA does not conform to NMEA specification.
 */
class InvalidNMEAException {
 public:
  /** Exception message. */
  char *message;
  /** Constuctor 
      @param m message
  */
  InvalidNMEAException(char *m){message=m;}
  /** @return exception message 
  */
  const char *msg(){return message;}
};

/**
   NMEA abstract base class for representing NMEA 0183 message strings.
*/
class NMEA{

 public:
  /** NMEA sentence type enum (one for each sentence).
      Message Types
      - GGA: Time, position and fix type data.
      - GLL: Latitude, longitude, UTC time of position fix and status.
      - GSA: GPS receiver operating mode, satellites used in the position solution, and DOP values.
      - GSV: The number of GPS satellites in view satellite ID numbers, elevation, azimuth, and SNR values.
      - MSS: Signal-to-noise ratio, signal strength, frequency, and bit rate from a radio-beacon receiver.
      - RMC: Time, date, position, course and speed data.
      - VTG: Course and speed information relative to the ground.
      - ZDA: PPS timing message (synchronized to PPS).

      \note 
      \par Note:
      Currently only GPRMC and GPGGA messages are implemented.
  */
  typedef enum MESSAGE_TYPES {t_GPGGA=0,t_GPRMC,t_GPGLL,
			      t_GPGSV,t_GPGSA,t_GPMSS,
			      t_GPVTG,tGPZDA,t_UNKNOWN}MessageType;/**< NMEA sentence type enum (one for each sentence).*/

  /** Enumeration of indices used to select NMEA string
      fields. Subclasses create their own enumerations
      of mnemonics that reference (map onto) these field
      indices. 
      @see GPGGA::FIELDS, GPRMC::FIELDS
  */
  enum FIELDS {FIELD_0=0,FIELD_1,FIELD_2,FIELD_3,
	       FIELD_4,FIELD_5,FIELD_6,FIELD_7,
	       FIELD_8,FIELD_9,FIELD_10,FIELD_11,
	       FIELD_12,FIELD_13,FIELD_14,FIELD_15,
	       FIELD_16,FIELD_17,FIELD_18,FIELD_19,
	       FIELD_20,FIELD_21,FIELD_22,FIELD_23,
	       FIELD_24,FIELD_25,FIELD_26,FIELD_27,
	       FIELD_28,FIELD_29,FIELD_30,FIELD_31};
  /** Enumeration of values used to select NMEA string
      timestamp resulotion.
  */
  typedef enum TIMESTAMP_RES {SEC=0,DECISEC,CENTISEC,MILLISEC}TimestampResolution;
  /** NMEA message line terminator style */
  typedef enum LINE_END {NOTERM=0,CRLF=1}LineEnd;

  TimestampResolution _timestampRes;

  NMEA();
  MessageType getType();
  Field *getField(NMEA::FIELDS index);
  void setField(NMEA::FIELDS field,int value);
  void setField(NMEA::FIELDS field,unsigned int value);
  void setField(NMEA::FIELDS field,char *value);
  void setField(NMEA::FIELDS field,char value);
  void setField(NMEA::FIELDS field,unsigned char value);
  void setField(NMEA::FIELDS field,double value);
  void setField(NMEA::FIELDS field,time_t value);
  void setField(NMEA::FIELDS field,struct timeval value);
  void timestampResolution(NMEA::TIMESTAMP_RES resolution);

  //virtual const char *toString()=0;
  /** Generate checksum of NMEA string 
      Checksum is XOR checksum of all bytes between
      '$' and '*', exclusive.
      @param nmeaString NMEA string to compute checksum for
      @return checksum value (unsigned short)
  */
  unsigned short int checksum(const char *nmeaString)const{
    const char *cp=nmeaString;//.c_str();
    char *bp, *ep;
    short unsigned int ret;

    // set begin and end pointers
    bp=strstr(cp,"$");
    ep=strstr(cp,"*");

    // throw exception if the start or end 
    // characters not found
    if(bp==NULL || ep==NULL){
      throw InvalidNMEAException("Missing delimiter * or $");
    }
    // start at character after '$'
    bp++;
    ret=0;
    // compute XOR of all bytes until '*'
    while(bp<ep){
      ret ^= (short unsigned int) (*bp);
      bp++;
    }
    return ret;
  }

  virtual ~NMEA(){}  

 protected:
  /** Number of fields in this NMEA message 
      (differs by subclass)
  */
  unsigned int _nFields;
  /** NMEA sentence (message) type
      @see NMEA::MESSAGE_TYPES
  */
  MessageType  _type;
  /** Array of Field objects */
  Field _fields[MAX_NMEA_FIELDS];

  char _outputBuf[128];
 private:

};/* class NMEA */

/** NMEA GPGGA message.

GPGGA Message Format
- Message ID             : $GPGGA     : GGA protocol header
- UTC Time               : 161229.487 : hhmmss.sss
- Latitude               : 3723.2475  : ddmm.mmmm
- N/S Indicator          : N          : N=north or S=south
- Longitude              : 12158.3416 : dddmm.mmmm
- E/W Indicator          : W          : E=east or W=west
- Position Fix Indicator : 1          : See Table 1-4
- Satellites Used        : 07         : Range 0 to 12
- HDOP                   : 1.0        : Horizontal Dilution of Precision
- MSL Altitude           : 9.0        : meters
- Units                  : M          : meters
- Geoid Separation       :            : meters
- Units                  : M          : meters
- Age of Diff. Corr.     : second     : Null fields when DGPS is not used
- Diff. Ref. Station ID  : 0000       :
- Checksum               : *18        :
- CRLF                   :            : End of message termination
*/ 
class GPGGA : public NMEA{
  /** output stream operator */
  friend ostream& operator<<(ostream& output, const GPGGA& n);
public:

  /**
     GPGGA Field mnemonics.
     These are mapped onto the field enumerations in the 
     base class.
  */
  enum FIELDS {GGAMSG=NMEA::FIELD_0,GGATIME,
	       GGALATD,GGALATM,GGALATH,
	       GGALOND,GGALONM,GGALONH,
	       GGAQUAL,GGASIU,GGAHDOP,
	       GGAALT,GGAALTU,
	       GGAGEOH,GGAGEOHU,
	       GGADUPT,GGADRID,GGACHK,
	       GGATVTIME};
  GPGGA();
  int setTIME(time_t time);
  int setTIME(struct timeval time);
  int setLATD(int latd);
  int setLATM(double latm);
  int setLATH(char lath);
  int setLOND(int lond);
  int setLONM(double lonm);
  int setLONH(char lonh);
  int setQUAL(int qual);
  int setHDOP(double hdop);
  int setALT(double alt);
  int setALTU(char altu);
  int setSIU(int siu);
  int setGEOH(double alt);
  int setGEOHU(char geohu);
  int setDUPT(int dupt);
  int setDRID(int drid);
  virtual const char *toString();
  virtual const char *toString(NMEA::LineEnd lineEnd);

 private:

};/* class GPGGA */
/** NMEA GPRMC message.

 GPRMC Message Format
 - Message ID: $GPRMC: RMC protocol header
 - UTC Time             : 161229.487 : hhmmss.sss
 - Status               : 1          : A A=data valid or V=data not valid
 - Latitude             : 3723.2475  : ddmm.mmmm
 - N/S Indicator        : N          : N=north or S=south
 - Longitude            : 12158.3416 : dddmm.mmmm
 - E/W Indicator        : W          : E=east or W=west
 - Speed Over Ground    : 0.13       : knots
 - Course Over Ground   : 309.62     : degrees True
 - Date                 : 120598     : ddmmyy
 - Magnetic Variation   : 2          : degrees E=east or W=west
 - Mode                 : A          : A=Autonomous, D=DGPS, E=DR
 - Checksum             : *10        :
 - CRLF                 :            : End of message termination
*/
class GPRMC : public NMEA{
  /** output stream operator */
  friend ostream& operator<<(ostream& output, const GPRMC& n);
public:
  /**
     GPRMC Field mnemonics.
     These are mapped onto the field enumerations in the 
     base class.
     @see NMEA::FIELDS
  */
  enum FIELDS {RMCMSG=NMEA::FIELD_0,RMCTIME,RMCSTAT,
	       RMCLATD,RMCLATM,RMCLATH,
	       RMCLOND,RMCLONM,RMCLONH,
	       RMCSOG,RMCCOG,RMCDATE,
	       RMCMVM,RMCMVD,RMCCHK,
               RMCTVTIME};

  GPRMC();
  int setTIME(time_t time);
  int setTIME(struct timeval time);
  int setSTAT(char stat);
  int setLATD(int latd);
  int setLATM(double latm);
  int setLATH(char lath);
  int setLOND(int lond);
  int setLONM(double lonm);
  int setLONH(char lonh);
  int setSOG(double sog);
  int setCOG(double cog);
  int setDATE(time_t date);
  int setMVM(double nvn);
  int setMVD(char mvd);
  virtual const char *toString();
  virtual const char *toString(NMEA::LineEnd lineEnd);

 private:

};/* class GPRMC */
#endif /*_NMEA_H_*/
