// General helper functions for working with AIS and NMEA.

#include "ais.h"

extern "C" {
int LibAisVersionMajor() {
  return LIBAIS_VERSION_MAJOR;
}

int LibAisVersionMinor() {
  return LIBAIS_VERSION_MINOR;
}
}  // extern "C"

namespace libais {

string GetNthField(const string &str, const size_t n,
                   const string &delim_str) {
  assert(!delim_str.empty());
  if (str.empty())
    return "";

  size_t prev = 0;
  size_t off = str.find(delim_str);
  size_t count = 0;

  for (; off != string::npos && count != n;
       off = str.find(delim_str, off + 1), count++) {
    prev = off + delim_str.size();
  }

  if (count == n)
    return str.substr(prev, off-prev);

  return "";
}

int GetPad(const string &nmea_str) {
  // -1 on error
  const string chksum_block(GetNthField(nmea_str, 6, ","));
  if (chksum_block.size() != 4 || chksum_block[1] != '*')
    return -1;
  const char pad = chksum_block[0];
  if (pad < '0' || pad > '5')
    return -1;
  return static_cast<int>(pad - '0');
}

string GetBody(const string &nmea_str) {
  return GetNthField(nmea_str, 5, ",");
}

// Note: Needs to be kept in sync with enum AIS_STATUS list in ais.h.
const char * const AIS_STATUS_STRINGS[AIS_STATUS_NUM_CODES] = {
  "AIS_UNINITIALIZED",
  "AIS_OK",
  "AIS_ERR_BAD_BIT_COUNT",
  "AIS_ERR_BAD_NMEA_CHR",
  "AIS_ERR_BAD_PTR",
  "AIS_ERR_UNKNOWN_MSG_TYPE",
  "AIS_ERR_MSG_NOT_IMPLEMENTED",
  "AIS_ERR_MSG_SUB_NOT_IMPLEMENTED",
  "AIS_ERR_EXPECTED_STRING",
  "AIS_ERR_BAD_MSG_CONTENT",
  "AIS_ERR_MSG_TOO_LONG",
  "AIS_ERR_BAD_SUB_MSG",
  "AIS_ERR_BAD_SUB_SUB_MSG",
};

AisMsg::AisMsg(const char *nmea_payload, const size_t pad)
    : message_id(0), repeat_indicator(0), mmsi(0), status(AIS_UNINITIALIZED),
      num_chars(0), num_bits(0), bits() {
  assert(nmea_payload);
  assert(pad < 6);

  const AIS_STATUS r = bits.ParseNmeaPayload(nmea_payload, pad);
  if (r != AIS_OK) {
    status = r;
    return;
  }
  num_bits = bits.GetNumBits();
  num_chars = bits.GetNumChars();

  if (bits.GetNumBits() < 38) {
    status = AIS_ERR_BAD_BIT_COUNT;
    return;
  }

  message_id = bits.ToUnsignedInt(0, 6);
  repeat_indicator = bits.ToUnsignedInt(6, 2);
  mmsi = bits.ToUnsignedInt(8, 30);
}

bool AisMsg::CheckStatus() const {
  if (status == AIS_OK || status == AIS_UNINITIALIZED) {
    return true;
  }
#ifdef LIBAIS_DEBUG
  std::cerr << "AisMsg::CheckStatus failed: "
            << AIS_STATUS_STRINGS[status]
            << std::endl;
#endif
  return false;
}

AisPoint::AisPoint() : lng_deg(0), lat_deg(0) {
}

AisPoint::AisPoint(double lng_deg_, double lat_deg_)
    : lng_deg(lng_deg_), lat_deg(lat_deg_) {
}

ostream& operator<< (ostream &o, const AisPoint &position) {
  return o << " (" << position.lng_deg << ", " << position.lat_deg << ")";
}

}  // namespace libais
