#pragma ident "$Id: Rinex3ClockHeader.hpp 3140 2012-06-18 15:03:02Z susancummins $"

/**
 * @file Rinex3ClockHeader.hpp
 * Encapsulate header of RINEX3 clock file, including I/O
 * See more at: ftp://igscb.jpl.nasa.gov/pub/data/format/rinex_clock.txt
 */

#ifndef GPSTK_RINEX3CLOCKHEADER_HPP
#define GPSTK_RINEX3CLOCKHEADER_HPP

//============================================================================
//
//  This file is part of GPSTk, the GPS Toolkit.
//
//  The GPSTk is free software; you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License as published
//  by the Free Software Foundation; either version 2.1 of the License, or
//  any later version.
//
//  The GPSTk is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with GPSTk; if not, write to the Free Software Foundation,
//  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
//
//  Octavian Andrei - FGI ( http://www.fgi.fi ). 2008
//
//============================================================================

// system
#include <vector>
#include <list>
#include <map>
#include <iostream>
#include <iomanip>
// GPSTk
#include "CommonTime.hpp"
#include "FFStream.hpp"
#include "Rinex3ClockBase.hpp"
#include "Triple.hpp"
#include "RinexSatID.hpp"



namespace gpstk
{
      /** @addtogroup Rinex3Clock */
      //@{

      /** This class models the header for a RINEX3 clock file.
       *
       * @sa gpstk::Rinex3ClockData and gpstk::Rinex3ClockStream.
       * @sa rinex_clk_test.cpp and rinex_clk_read_write.cpp for examples.
       */
   class Rinex3ClockHeader : public Rinex3ClockBase
   {
   public:

         /// A Simple Constructor.
      Rinex3ClockHeader() : 
         version(3.0), valid(false), timeFirst(CommonTime::BEGINNING_OF_TIME)
         {}


         /// Clear (empty out) header
      void clear(void);


         /**
          * @name Rinex3ClockHeaderFormatStrings
          * RINEX3 Clock Header Formatting Strings
          */
         //@{
      static const std::string versionString;       ///< "RINEX VERSION / TYPE"
      static const std::string runByString;         ///< "PGM / RUN BY / DATE"
      static const std::string commentString;       ///< "COMMENT"
      static const std::string numObsString;        ///< "SYS / # / OBS TYPES"
      static const std::string timeSystemString;    ///< "TIME SYSTEM ID"
      static const std::string leapSecondsString;   ///< "LEAP SECONDS"
      static const std::string sysDCBString;        ///< "SYS / DCBS APPLIED"
      static const std::string sysPCVString;        ///< "SYS / PCVS APPLIED"
      static const std::string dataTypesString;     ///< "# / TYPES OF DATA"
      static const std::string stationNameString;   ///< "STATION NAME / NUM"
      static const std::string calibrationClkString;///< "STATION CLK REF"
      static const std::string acNameString;        ///< "ANALYSIS CENTER"
      static const std::string numRefClkString;     ///< "# OF CLK REF"
      static const std::string analysisClkRefString;///< "ANALYSIS CLK REF"
      static const std::string numStationsString;   ///< "# OF SOLN STA / TRF"
      static const std::string solnStaNameString;   ///< "SOLN STA NAME / NUM"
      static const std::string numSatsString;       ///< "# OF SOLN SATS"
      static const std::string prnListString;       ///< "PRN LIST"
      static const std::string endOfHeader;         ///< "END OF HEADER"
         //@}

         /// Validity bits for the RINEX3 Clock Header
      enum validBits
      {
         versionValid         = 0x01,        ///< "RINEX VERSION / TYPE"
         runByValid           = 0x02,        ///< "PGM / RUN BY / DATE"
         commentValid         = 0x04,        ///< "COMMENT"                (optional)
         numObsValid          = 0x08,        ///< "SYS / # / OBS TYPES"
         timeSystemValid      = 0x010,       ///< "TIME SYSTEM ID"
         leapSecondsValid     = 0x020,       ///< "LEAP SECONDS"           (optional)
         sysDCBsValid         = 0x040,       ///< "SYS / DCBS APPLIED"
         sysPCVsValid         = 0x080,       ///< "SYS / PCVS APPLIED"
         dataTypesValid       = 0x0100,      ///< "# / TYPES OF DATA"
         stationNameValid     = 0x0200,      ///< "STATION NAME / NUM"
         calibrationClkValid  = 0x0400,      ///< "STATION CLK REF"
         acNameValid          = 0x0800,      ///< "ANALYSIS CENTER"
         numRefClkValid       = 0x01000,     ///< "# OF CLK REF"
         analysisClkRefValid  = 0x02000,     ///< "ANALYSIS CLK REF"
         numStationsValid     = 0x04000,     ///< "# OF SOLN STA / TRF"
         solnStaNameValid     = 0x08000,     ///< "SOLN STA NAME / NUM"
         numSatsValid         = 0x010000,    ///< "# OF SOLN SATS"
         prnListValid         = 0x020000,    ///< "PRN LIST"

         endValid = 0x080000000,          ///< "END OF HEADER"

            /// This mask is for all valid fields
         allValid = 0x08003FFFF

      };


         /// Holds the necessary data for one reference clock corresponding to 
         /// one "ANALYSIS CLK REF" line
      struct RefClk
      {
            /// Name of the reference station
         std::string name;
            /// Constraints for the ref-clock (ms)
         double sigma;
      };


         /// Holds the data for the analysis clock references
         /// i.e., the "# OF CLK REF" lines
      struct RefClkRecord
      {
            /// Number of reference stations in file
         size_t nRef;
            /// List of reference clocks
         std::list<RefClk> clk;
            /// Ref-clock window in the file (sec)
         double refWin[2];

         RefClkRecord() : nRef(0) 
         { refWin[0] = refWin[1] = 0.0; };
      };

         /// RINEX clock data types
      struct RinexClkType
      {
         std::string type;
         std::string description;
         RinexClkType() : type(std::string("UN")),
            description(std::string("Unknown or Invalid")) {}
         RinexClkType(std::string t, std::string d) :
            type(t),description(d) {}
      };


         /** @name Standard RINEX clock data types
          */
         //@{
      static const RinexClkType UN;
      static const RinexClkType AR;
      static const RinexClkType AS;
      static const RinexClkType CR;
      static const RinexClkType DR;
      static const RinexClkType MS;
         //@}

      //static const std::vector<ObsID> StandardRinex3ObsIDs;
      //static std::vector<ObsID> RegisteredRinex3ObsIDs;


         /** @name Rinex3ClockHeaderValues
          */ 
         //@{
      double version;                        ///< RINEX3 VERSION & TYPE
      std::string fileType;                  ///< RINEX3 FILETYPE (Clock data etc)
      RinexSatID system;                     ///< The RINEX3 satellite system
      std::string fileProgram,               ///< The program used to generate this file
         fileAgency,                         ///< Who ran the program.
         date;                               ///< When the program was run.
      std::vector<std::string> commentList;  ///< Comments in header (optional)
      int numObsTyp;                         ///< Number of data types.
      std::vector<std::string> obsTypeList;  ///< List of observation types
      std::string timeSystem;                ///< Time system used
      int leapSeconds;                       ///< Leap second (optional)
      int numTyp;                            ///<
      std::vector<std::string> dataTypeList; ///< List of data types
      std::string clk0Name;                  ///< Name for the clocks
      std::string calName;                   ///< Calibration station identifier
      std::string ac;                        ///< Analysis center ID
      std::string acName;                    ///< Analysis center name
      CommonTime timeFirst;                  ///< First Epoch (CommonTime object)
      std::list<RefClkRecord> refClkList;    ///< List of all reference clock sets
      std::string trfName;                   ///< Terrestrial reference frame
      int numSta,                            ///< Number of station clocks
         numSVs;                             ///< Number of satellite clocks
      std::vector<std::string> clkNameList;  ///< Names of the sta/sat clocks
      std::vector<Triple> staCoordList;      ///< Coordinates of the stations (m)

      unsigned long valid; ///< Bits set when individual header members are present and valid

      bool isPGM;          ///< flag for PGM/RUN BY/DATE
      bool isAC;           ///< flag for AC/ACName

   //@}


         /// Destructor
      virtual ~Rinex3ClockHeader() {}

         // The next four lines is our common interface
         /// Rinex3ClockHeader is a "header" so this function 
         /// always returns true.
      virtual bool isHeader() const {return true;}


         /** 
          * This is a simple Debug output function.
          * It simply outputs the version, name and antenna number of this
          * RINEX3 clock header.
          */
      virtual void dump(std::ostream& s) const;


         /**
          * Parse a single header record, and modify valid accordingly.
          * Used by reallyGetRecord for both Rinex3ClockHeader and 
          * Rinex3ClockData.
          */
      void ParseHeaderRecord(std::string& line)
         throw(FFStreamError);


         /// Return boolean : is this a valid Rinex clock header?
      bool isValid() const { return ((valid & allValid) == allValid); }


   protected:

         /**
          * outputs this record to the stream correctly formatted.
          */
      virtual void reallyPutRecord(FFStream& s) const
         throw(std::exception, FFStreamError, StringUtils::StringException);

         /**
          * This function retrieves the RINEX3 Clock Header from the given FFStream.
          * If an stream error is encountered, the stream is reset to its
          *  original position and its fail-bit is set.
          * @throws StringException when a StringUtils function fails
          * @throws FFStreamError when exceptions(failbit) is set and
          *  a read or formatting error occurs.  This also resets the
          *  stream to its pre-read position.
          */
      virtual void reallyGetRecord(FFStream& s) 
         throw(std::exception, FFStreamError,StringUtils::StringException);

      friend class Rinex3ClockData;

   private:
         /// Converts the CommonTime \a dt into a Rinex3 Clock time
         /// string for the header
      std::string writeTime(const CommonTime& dt) const;

         /**
          * This function sets the time for this header.
          * It looks at \a line to obtain the needed information.
          */
      CommonTime parseTime(const std::string& line) const;

   }; // end class Rinex3ClockHeader

      //@}

}  // End of namespace gpstk

#endif   // GPSTK_RINEX3CLOCKHEADER_HPP
