// 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_ssrRefSig
 *
 * Purpose:    Reference signals of the satellite clocks per GNSS
 *
 * -----------------------------------------------------------------------*/

#include "ssrRefSig.h"
#include "bncsettings.h"

using namespace std;

QMutex                          t_ssrRefSig::_mutex;
bool                            t_ssrRefSig::_initialized = false;
QMap<char, t_ssrRefSig::t_pair> t_ssrRefSig::_pairs;

static const char* DEFAULT_REF_SIGNALS = "G:1W&2W R:1P&2P E:1C&5Q C:2I&6I J:1C&2L S:1C&5Q";

// Frequency of a band of a system (e.g. 'C', '1' -> C1)
////////////////////////////////////////////////////////////////////////////
static t_frequency::type bandToFreq(char sys, char band) {
  for (int ii = t_frequency::dummy + 1; ii < t_frequency::max; ii++) {
    string str = t_frequency::toString(t_frequency::type(ii));
    if (str.size() >= 2 && str[0] == sys && str[1] == band) {
      return t_frequency::type(ii);
    }
  }
  return t_frequency::dummy;
}

//
////////////////////////////////////////////////////////////////////////////
string t_ssrRefSig::t_pair::sig1() const {
  string str = t_frequency::toString(frq1);
  return str.size() >= 2 ? string(1, str[1]) + att1 : string();
}

//
////////////////////////////////////////////////////////////////////////////
string t_ssrRefSig::t_pair::sig2() const {
  string str = t_frequency::toString(frq2);
  return str.size() >= 2 ? string(1, str[1]) + att2 : string();
}

// Parse 'G:1W&2W C:1P&5P ...'
////////////////////////////////////////////////////////////////////////////
bool t_ssrRefSig::parse(const QString& str, QMap<char, t_pair>& pairs, QStringList* errors) {
  bool ok = true;
  QStringList items = str.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts);
  for (const QString& item : items) {
    QRegularExpressionMatch mm =
      QRegularExpression("^([A-Z]):([0-9])([A-Z])&([0-9])([A-Z])$").match(item.trimmed());
    t_pair pair;
    char sys = 0;
    if (mm.hasMatch()) {
      sys       = mm.captured(1)[0].toLatin1();
      pair.frq1 = bandToFreq(sys, mm.captured(2)[0].toLatin1());
      pair.att1 = mm.captured(3)[0].toLatin1();
      pair.frq2 = bandToFreq(sys, mm.captured(4)[0].toLatin1());
      pair.att2 = mm.captured(5)[0].toLatin1();
    }
    if (sys == 0 || pair.frq1 == t_frequency::dummy || pair.frq2 == t_frequency::dummy ||
        pair.frq1 == pair.frq2) {
      ok = false;
      if (errors) {
        *errors << QString("reference signals '%1' invalid (unknown system or band, or format other than e.g. 'C:1P&5P'), ignored").arg(item);
      }
      continue;
    }
    pairs[sys] = pair;
  }
  return ok;
}

// (Re-)read the option
////////////////////////////////////////////////////////////////////////////
QStringList t_ssrRefSig::init() {
  QMutexLocker locker(&_mutex);
  QStringList errors;
  _pairs.clear();
  parse(DEFAULT_REF_SIGNALS, _pairs, 0);
  bncSettings settings;
  parse(settings.value("ssrRefSignals").toString(), _pairs, &errors);
  _initialized = true;
  return errors;
}

//
////////////////////////////////////////////////////////////////////////////
bool t_ssrRefSig::get(char sys, t_pair& pair) {
  if (!_initialized) {
    init();
  }
  QMutexLocker locker(&_mutex);
  if (!_pairs.contains(sys)) {
    return false;
  }
  pair = _pairs[sys];
  return true;
}

// True if the system uses the default reference signals
////////////////////////////////////////////////////////////////////////////
bool t_ssrRefSig::isDefault(char sys) {
  QMap<char, t_pair> defaults;
  parse(DEFAULT_REF_SIGNALS, defaults, 0);
  t_pair pair;
  bool   has = get(sys, pair);
  if (!defaults.contains(sys)) {
    return !has;
  }
  const t_pair& def = defaults[sys];
  return has && pair.frq1 == def.frq1 && pair.att1 == def.att1 &&
                pair.frq2 == def.frq2 && pair.att2 == def.att2;
}

// True if all systems use the default reference signals
////////////////////////////////////////////////////////////////////////////
bool t_ssrRefSig::allDefault() {
  QMap<char, t_pair> defaults;
  parse(DEFAULT_REF_SIGNALS, defaults, 0);
  if (!_initialized) {
    init();
  }
  QMutexLocker locker(&_mutex);
  if (defaults.keys() != _pairs.keys()) {
    return false;
  }
  for (char sys : defaults.keys()) {
    const t_pair& p1 = defaults[sys];
    const t_pair& p2 = _pairs[sys];
    if (p1.frq1 != p2.frq1 || p1.att1 != p2.att1 || p1.frq2 != p2.frq2 || p1.att2 != p2.att2) {
      return false;
    }
  }
  return true;
}

//
////////////////////////////////////////////////////////////////////////////
QString t_ssrRefSig::defaultString() {
  return DEFAULT_REF_SIGNALS;
}

// Current reference signals, e.g. for the logfile
////////////////////////////////////////////////////////////////////////////
QString t_ssrRefSig::currentString() {
  if (!_initialized) {
    init();
  }
  QMutexLocker locker(&_mutex);
  QStringList items;
  QString     systems = "GRECJSI";
  for (char sys : _pairs.keys()) {
    if (!systems.contains(sys)) {
      systems += sys;
    }
  }
  for (QChar sys : systems) {
    char cs = sys.toLatin1();
    if (_pairs.contains(cs)) {
      items << QString("%1:%2&%3").arg(sys).arg(_pairs[cs].sig1().c_str()).arg(_pairs[cs].sig2().c_str());
    }
  }
  return items.join(" ");
}
