
// Part of BNC, a utility for retrieving decoding and
// converting GNSS data streams from NTRIP broadcasters.
//
// Copyright (C) 2007
// German Federal Agency for Cartography and Geodesy (BKG)
// http://bkg.bund.de
// Czech Technical University Prague, Department of Geodesy
// http://www.fsv.cvut.cz
//
// Email: euref-ip@bkg.bund.de
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation, version 2.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

/* -------------------------------------------------------------------------
 * BKG NTRIP Client
 * -------------------------------------------------------------------------
 *
 * Class:      t_pppCrdFile
 *
 * Purpose:    Read a priori coordinate file
 *
 * Author:     L. Mervart
 *
 * Created:    29-Jul-2014
 *
 * Changes:
 *
 * -----------------------------------------------------------------------*/

#include <cmath>
#include <cstdlib>
#include <fstream>
#include <sstream>
#include "pppCrdFile.h"
#include "bncutils.h"

using namespace std;
using namespace BNC_PPP;

// Convert a 'D,M,S' (degrees, minutes, seconds) token into decimal degrees;
// the sign of D carries the hemisphere (negative == South/West).
//////////////////////////////////////////////////////////////////////////////
static double dmsToDeg(const string& dms) {
  string str = dms;
  for (string::iterator it = str.begin(); it != str.end(); ++it) {
    if (*it == ',') *it = ' ';
  }
  istringstream in(str);
  double d = 0.0, m = 0.0, s = 0.0;
  in >> d >> m >> s;
  double sign = (d < 0.0) ? -1.0 : 1.0;
  return sign * (fabs(d) + m / 60.0 + s / 3600.0);
}

//
//////////////////////////////////////////////////////////////////////////////
void t_pppCrdFile::readCrdFile(const string& fileName, vector<t_staInfo>& staInfoVec) {

  staInfoVec.clear();

  ifstream inFile(fileName.c_str());
  while (inFile.good()) {
    string line; getline(inFile,line);
    stripWhiteSpace(line);
    if ( line.empty() || line[0] == '#'|| line[0] == '!') {
      continue;
    }

    istringstream in;

    t_staInfo staInfo;

    size_t q1 = line.find('"');
    if (q1 != string::npos) {
      size_t q2 = line.find('"', q1+1);
      if (q2 == string::npos) {
        continue;
      }
      staInfo._name = line.substr(q1+1, q2-q1-1);
      in.str(line.substr(q2+1));
    }
    else {
      in.str(line);
      in >> staInfo._name;
    }

    // The a priori coordinate is given either as plain Cartesian 'X Y Z', or,
    // if the line's first coordinate token is 'LAT:<D,M,S>', as geodetic
    // 'LAT:<D,M,S> LON:<D,M,S> H:<height>' (converted to XYZ below).
    streampos posBeforeCoord = in.tellg();
    string firstCoordTok;
    bool geodetic = false;
    if (in >> firstCoordTok) {
      geodetic = (firstCoordTok.compare(0, 4, "LAT:") == 0);
      in.seekg(posBeforeCoord);
    }
    if (!geodetic) {
      in >> staInfo._xyz(1) >> staInfo._xyz(2) >> staInfo._xyz(3);
    }

    // Optional keyword tokens, may appear in any order before the antenna
    // eccentricity / name fields:
    //  - 'LAT:<D,M,S>', 'LON:<D,M,S>', 'H:<height>'  (geodetic a priori coordinate)
    //  - 'EPOCH:<decimalYear>'                       (ITRF reference epoch of the coordinate)
    //  - 'VEL:<vx>,<vy>,<vz>'                         (ITRF velocity [m/year] of the coordinate)
    double lat = 0.0, lon = 0.0, hgt = 0.0;
    bool haveLat = false, haveLon = false, haveHgt = false;
    while (!in.eof()) {
      streampos posBeforeToken = in.tellg();
      string token;
      if (!(in >> token)) {
        break;
      }
      if (token.compare(0, 4, "LAT:") == 0) {
        lat = dmsToDeg(token.substr(4));
        haveLat = true;
      }
      else if (token.compare(0, 4, "LON:") == 0) {
        lon = dmsToDeg(token.substr(4));
        haveLon = true;
      }
      else if (token.compare(0, 2, "H:") == 0) {
        hgt = atof(token.substr(2).c_str());
        haveHgt = true;
      }
      else if (token.compare(0, 6, "EPOCH:") == 0) {
        staInfo._epoch = atof(token.substr(6).c_str());
      }
      else if (token.compare(0, 4, "VEL:") == 0) {
        string velStr = token.substr(4);
        for (string::iterator it = velStr.begin(); it != velStr.end(); ++it) {
          if (*it == ',') *it = ' ';
        }
        istringstream velIn(velStr);
        velIn >> staInfo._velocity(1) >> staInfo._velocity(2) >> staInfo._velocity(3);
      }
      else {
        in.seekg(posBeforeToken);
        break;
      }
    }

    if (geodetic && haveLat && haveLon && haveHgt) {
      double Ell[3] = { lat * M_PI / 180.0, lon * M_PI / 180.0, hgt };
      double XYZ[3];
      ell2xyz(Ell, XYZ);
      staInfo._xyz(1) = XYZ[0];
      staInfo._xyz(2) = XYZ[1];
      staInfo._xyz(3) = XYZ[2];
    }

    if (!in.eof()) {
      in >> staInfo._neuAnt(1) >> staInfo._neuAnt(2) >> staInfo._neuAnt(3);
    }

    if (!in.eof()) {
      std::string hlp;
      getline(in, hlp);
      stripWhiteSpace(hlp);
      staInfo._antenna.assign( hlp.substr(0,20));
      hlp = hlp.erase(0, 20);
      if (hlp.length()) {
        stripWhiteSpace(hlp);
        staInfo._receiver = hlp;
      }
    }

    staInfoVec.push_back(staInfo);
  }
}
