//============================================================================
//
//  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 3.0 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
//  
//  Copyright 2004, The University of Texas at Austin
//  Dagoberto Salazar - gAGE ( http://www.gage.es ). 2009, 2011
//
//============================================================================

//============================================================================
//
//This software developed by Applied Research Laboratories at the University of
//Texas at Austin, under contract to an agency or agencies within the U.S. 
//Department of Defense. The U.S. Government retains all rights to use,
//duplicate, distribute, disclose, or release this software. 
//
//Pursuant to DoD Directive 523024 
//
// DISTRIBUTION STATEMENT A: This software has been approved for public 
//                           release, distribution is unlimited.
//
//=============================================================================

/**
 * @file AntexReader.hpp
 * Class to read antenna data from files in Antex format.
 */

#ifndef GPSTK_ANTEXREADER_HPP
#define GPSTK_ANTEXREADER_HPP

#include <string>
#include <map>

#include "Exception.hpp"
#include "FFTextStream.hpp"
#include "StringUtils.hpp"
#include "CommonTime.hpp"
#include "SatID.hpp"
#include "Antenna.hpp"


namespace gpstk
{

      /// Thrown when some problem appeared when reading Antex data
      /// @ingroup exceptiongroup
   NEW_EXCEPTION_CLASS(InvalidAntex, gpstk::Exception);


      /// @ingroup formattedfile 
      //@{

      /** This is a class to read and parse antenna data in Antex file format.
       *
       * You may find antenna-related data at:
       *
       *    ftp://igscb.jpl.nasa.gov/pub/station/general/
       *
       * At that site you'll find some very important files:
       *
       * \li rcvr_ant.tab: Official IGS naming conventions for GNSS equipment.
       * \li antex13.txt: ANTEX format definition.
       * \li igs05_wwww.atx: Absolute IGS phase center corrections for
       * satellite and receiver antennas. Field 'wwww' represents GPS week of
       * last file change.
       * \li igs05.atx: Link to latest igs05_wwww.atx file.
       * \li igs_01.atx: Relative IGS phase center corrections for
       * satellite and receiver antennas.
       *
       * This class is normally used in combination with 'Antenna' class. A
       * typical way to use these classes follows:
       *
       * @code
       *      // Declare some Antenna objects
       *   Antenna antenna1;
       *   Antenna antenna2;
       *   Antenna satGPS02;
       *
       *      // Create AntexReader object
       *   AntexReader antexread;
       *
       *      // Open Antex file. 'igs05.atx' is for absolute phase centers,
       *      // while 'igs_01.atx' is for relative phase centers.
       *   antexread.open("igs05.atx");
       *
       *   double elevation( 34.5 );
       *   double azimuth( 163.2 );
       *
       *      // Get antenna data and eccentricity for L1 for satellite GPS-07
       *      // at a specific epoch
       *   CivilTime epoch(2008, 6, 15, 10, 21, 12654.0);
       *   satGPS07 = antexread.getAntenna( "G07", epoch );
       *   cout << satGPS07.getAntennaPCOffset( Antenna::G01 ) << endl;
       *
       *      // Get antenna data and non-azimuth dependent phase center
       *      // offset value + eccentricity for L2.
       *      // Radome type is NOT taken into account
       *   antenna1  = antexread.getAntennaNoRadome("AOAD/M_B");
       *   cout << antenna1.getAntennaPCOffset( Antenna::G02, elevation );
       *
       *      // Get antenna data and azimuth-dependent phase center offset
       *      // value + eccentricity for L1.
       *      // Ask for a specific "strict IGS" model+radome combination
       *   std::string strictIGSModel( "ASH700936B_M    SNOW" );
       *   antenna2  = antexread.getAntenna( strictIGSModel );
       *   cout << antenna2.getAntennaPCOffset( Antenna::G01,
       *                                        elevation,
       *                                        azimuth );
       *
       * @endcode
       *
       * @sa Antenna.hpp
       */
   class AntexReader : public FFTextStream
   {
   public:


         /**
          * @name AntexReaderFormatStrings
          * Antex Formatting Strings
          */
         //@{
      static const std::string versionString;      ///< "ANTEX VERSION / SYST"
      static const std::string pcvTypeString;      ///< "PCV TYPE / REFANT"
      static const std::string commentString;      ///< "COMMENT"
      static const std::string endOfHeader;        ///< "END OF HEADER"

      static const std::string startOfAntenna;     ///< "START OF ANTENNA"
      static const std::string typeSerial;         ///< "TYPE / SERIAL NO"
      static const std::string calibrationMethod;  ///< "METH / BY / # / DATE"
      static const std::string incrementAzimuth;   ///< "DAZI"
      static const std::string zenithGrid;         ///< "ZEN1 / ZEN2 / DZEN"
      static const std::string numberFreq;         ///< "# OF FREQUENCIES"
      static const std::string validFrom;          ///< "VALID FROM"
      static const std::string validUntil;         ///< "VALID UNTIL"
      static const std::string sinexCode;          ///< "SINEX CODE"
      static const std::string startOfFreq;        ///< "START OF FREQUENCY"
      static const std::string antennaEcc;         ///< "NORTH / EAST / UP"
      static const std::string endOfFreq;          ///< "END OF FREQUENCY"
      static const std::string startOfFreqRMS;     ///< "START OF FREQ RMS"
      static const std::string antennaEccRMS;      ///< "NORTH / EAST / UP"
      static const std::string endOfFreqRMS;       ///< "END OF FREQ RMS"
      static const std::string endOfAntenna;       ///< "END OF ANTENNA"
         //@}


         /// Phase center variation type
      enum pcvType
      {
         absolute = 1,           ///< Absolute PCV
         relative,               ///< Relative PCV
         Unknown                 ///< Unknown PCV
      };


         /// Default constructor
      AntexReader()
         : fileName(""), version(1.3), valid(false)
      {};


         /** Common constructor. It will always open Antex file for read and
          *  will load Antex file header data in one pass.
          *
          * @param fn   Antex data file to read
          *
          */
      AntexReader(const char* fn)
         : FFTextStream( fn, std::ios::in )
      { fileName = fn; loadHeader(); };


         /** Common constructor. It will always open Antex file for read and
          *  will load Antex file header data in one pass.
          *
          * @param fn   Antex data file to read
          *
          */
      AntexReader(const std::string& fn)
         : FFTextStream( fn.c_str(), std::ios::in )
      { fileName = fn; loadHeader(); };
       
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Woverloaded-virtual"
       

         /// Method to open and load Antex file header data.
      virtual void open(const char* fn);


         /// Method to open and load Antex file header data.
      virtual void open(const std::string& fn);
#pragma clag diagnostic pop

         /** Method to get antenna data from a given model. Just the model,
          *  without including the radome
          *
          * @param model      Antenna model, without including radome.
          *
          * @note Antenna model case is NOT relevant.
          *
          * @warning The antenna returned will be the first one in the Antex
          * file that matches the condition.
          */
      virtual Antenna getAntennaNoRadome(const std::string& model)
         throw(ObjectNotFound);


         /** Method to get antenna data from a given IGS model.
          *
          * @param model      IGS antenna model
          *
          * @note Antenna model case is NOT relevant.
          *
          * @note IGS antenna model combines antenna type and radome.
          *
          * @warning The antenna returned will be the first one in the Antex
          * file that matches the condition.
          *
          * @warning If IGS model doesn't include radome, method
          * 'getAntennaNoRadome()' will be automatically called.
          */
      virtual Antenna getAntenna(const std::string& model)
         throw(ObjectNotFound);


         /** Method to get antenna data from a given IGS model and serial.
          *
          * @param model      IGS antenna model
          * @param serial     Antenna serial number.
          *
          * @note Antenna model and serial number case is NOT relevant.
          *
          * @note IGS antenna model combines antenna type and radome.
          *
          * @warning The antenna returned will be the first one in the Antex
          * file that matches the conditions.
          */
      virtual Antenna getAntenna( const std::string& model,
                                  const std::string& serial )
         throw(ObjectNotFound);


         /** Method to get antenna data from a given IGS model and serial, and
          *  for a specific epoch.
          *
          * @param model      IGS antenna model
          * @param serial     Antenna serial number.
          * @param epoch      Validity epoch.
          *
          * @note Antenna model and serial number case is NOT relevant.
          *
          * @note IGS antenna model combines antenna type and radome.
          *
          * @warning The antenna returned will be the first one in the Antex
          * file that matches the conditions.
          */
      virtual Antenna getAntenna( const std::string& model,
                                  const std::string& serial,
                                  const CommonTime& epoch )
         throw(ObjectNotFound);


         /** Method to get antenna data from a given serial and a specific
          *  epoch.
          *
          * This method is particularly useful to look for satellite antennas.
          *
          * @param serial     Antenna serial number.
          * @param epoch      Validity epoch.
          *
          * @note Antenna serial number case is NOT relevant.
          *
          * @warning The antenna returned will be the first one in the Antex
          * file that matches the conditions.
          */
      virtual Antenna getAntenna( const std::string& serial,
                                  const CommonTime& epoch )
         throw(ObjectNotFound);


         /// Returns if this object is valid.
      bool isValid() const
      { return valid; };


         /// Returns version of Antex file.
      double getVersion() const
      { return version; };


         /// Returns if loaded antenna data file is absolute or relative.
      bool isAbsolute() const;


         /// This methods dumps all data in Antex file header.
      virtual void dump(std::ostream& s) const;


         /// Destructor
      virtual ~AntexReader() {};


   private:


         // Handy antenna data types

         // Validity:Antennas
      typedef std::map< CommonTime, Antenna > ValAntMap;

         // Calibration:Validity:Antennas
      typedef std::map< std::string, ValAntMap > CalValAntMap;

         // Serial:Calibration:Validity:Antennas
      typedef std::map< std::string, CalValAntMap > SerCalValAntMap;

         // Radome:Serial:Calibration:Validity:Antennas
      typedef std::map< std::string, SerCalValAntMap > RSCalValAntMap;

         // Model:Radome:Serial:Calibration:Validity:Antennas
      typedef std::map< std::string, RSCalValAntMap > AntennaDataMap;


         /// Map holding antennas already serched for (Antenna buffer)
      AntennaDataMap antennaMap;


         /// Antex file name
      std::string fileName;

         /// Antex file version
      double version;

         /// Satellite system
      SatID::SatelliteSystem system;

         /// PCV type
      pcvType type;

         /// Reference antenna type (relative)
      std::string refAntena;

         /// Reference antenna serial (relative)
      std::string refAntenaSerial;

         /// Comments in header (optional)
      std::vector<std::string> commentList;

         /// Whether this Antex header is valid
      bool valid;


         /// Parse a single header line. Returns label.
      std::string parseHeaderLine( const std::string& line )
         throw(InvalidAntex);


         /// Fill most Antenna data
      Antenna fillAntennaData( const std::string& firstLine );


         /// Method to load Antex file header data.
      virtual void loadHeader(void)
         throw( InvalidAntex,
                FFStreamError,
                gpstk::StringUtils::StringException );


   }; // End of class 'AntexReader'


      /// Operator << for AntexReader
   inline std::ostream& operator<<( std::ostream& s,
                                    const AntexReader& antread )
   {

         // Call dump() method
      antread.dump(s);

      return s;

   }  // End of operator '<<'

      //@}

}  // End of namespace gpstk

#endif  // GPSTK_ANTEXREADER_HPP
