// -*- c++ -*-

// TODO(schwehr): Create an archive of messages that do not decode.

#ifndef LIBAIS_AIS_H_
#define LIBAIS_AIS_H_

#include <array>
#include <bitset>
#include <cassert>
#include <cstring>
#include <string>
#include <iostream>
#include <vector>

using std::bitset;
using std::ostream;
using std::string;
using std::vector;

#define LIBAIS_VERSION_MAJOR 0
#define LIBAIS_VERSION_MINOR 15

extern "C" {
// For configuration scripts to detect libais and the version numbers.
int LibAisVersionMajor();
int LibAisVersionMinor();
}

namespace libais {

// Returns the text in the nth field starting with the first field being 0.
// Empty delim_str is not allowed.
string GetNthField(const string &str, const size_t n, const string &delim_str);

// Returns the number of pad bits in an AIS AIVDM NMEA string.
// Returns -1 if there is an error.
int GetPad(const string &nmea_str);

// Returns the armored payload of an AIS AIVDM NMEA string.
// Returns an empty string if there was an error.
string GetBody(const string &nmea_str);


// Note: Needs to be kept in sync with AIS_STATUS_STRINGS list in ais.cpp.
enum AIS_STATUS {
  AIS_UNINITIALIZED,  // Message is not yet completely decoded.
  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,
  AIS_STATUS_NUM_CODES
};

extern const char *const AIS_STATUS_STRINGS[AIS_STATUS_NUM_CODES];

// Designated Area Codes (DAC) / Maritime Identification Digits define
// which country controls a subset of the submessage spaces within
// AIS "binary" messages 6, 8, 25, and 26.  See:
//   http://www.itu.int/online/mms/glad/cga_mids.sh?lng=E
//   http://en.wikipedia.org/w/index.php?title=Maritime_identification_digits
// River Information System (RIS):
//   ECE-TRANS-SC3-2006-10r-RIS.pdf

enum Dac {
  AIS_DAC_0_TEST = 0,
  AIS_DAC_1_INTERNATIONAL,
  AIS_DAC_200_RIS = 200,
  AIS_DAC_201_ALBANIA = 201,
  AIS_DAC_202_ANDORRA = 202,
  AIS_DAC_203_AUSTRIA = 203,
  AIS_DAC_204_AZORES = 204,
  AIS_DAC_205_BELGIUM = 205,
  AIS_DAC_206_BELARUS = 206,
  AIS_DAC_207_BULGARIA = 207,
  AIS_DAC_208_VATICAN_CITY_STATE = 208,
  AIS_DAC_209_CYPRUS = 209,
  AIS_DAC_210_CYPRUS = 210,
  AIS_DAC_211_GERMANY = 211,
  AIS_DAC_212_CYPRUS = 212,
  AIS_DAC_213_GEORGIA = 213,
  AIS_DAC_214_MOLDOVA = 214,
  AIS_DAC_215_MALTA = 215,
  AIS_DAC_216_ARMENIA = 216,
  AIS_DAC_218_GERMANY = 218,
  AIS_DAC_219_DENMARK = 219,
  AIS_DAC_220_DENMARK = 220,
  AIS_DAC_224_SPAIN = 224,
  AIS_DAC_225_SPAIN = 225,
  AIS_DAC_226_FRANCE = 226,
  AIS_DAC_227_FRANCE = 227,
  AIS_DAC_228_FRANCE = 228,
  AIS_DAC_229_MALTA = 229,
  AIS_DAC_230_FINLAND = 230,
  AIS_DAC_231_FAROE_ISLANDS = 231,
  AIS_DAC_232_UNITED_KINGDOM_OF_GREAT_BRITAIN_AND_NORTHERN_IRELAND = 232,
  AIS_DAC_233_UNITED_KINGDOM_OF_GREAT_BRITAIN_AND_NORTHERN_IRELAND = 233,
  AIS_DAC_234_UNITED_KINGDOM_OF_GREAT_BRITAIN_AND_NORTHERN_IRELAND = 234,
  AIS_DAC_235_UNITED_KINGDOM_OF_GREAT_BRITAIN_AND_NORTHERN_IRELAND = 235,
  AIS_DAC_236_GIBRALTAR = 236,
  AIS_DAC_237_GREECE = 237,
  AIS_DAC_238_CROATIA = 238,
  AIS_DAC_239_GREECE = 239,
  AIS_DAC_240_GREECE = 240,
  AIS_DAC_241_GREECE = 241,
  AIS_DAC_242_MOROCCO = 242,
  AIS_DAC_243_HUNGARY = 243,
  AIS_DAC_244_NETHERLANDS = 244,
  AIS_DAC_245_NETHERLANDS = 245,
  AIS_DAC_246_NETHERLANDS = 246,
  AIS_DAC_247_ITALY = 247,
  AIS_DAC_248_MALTA = 248,
  AIS_DAC_249_MALTA = 249,
  AIS_DAC_250_IRELAND = 250,
  AIS_DAC_251_ICELAND = 251,
  AIS_DAC_252_LIECHTENSTEIN = 252,
  AIS_DAC_253_LUXEMBOURG = 253,
  AIS_DAC_254_MONACO = 254,
  AIS_DAC_255_MADEIRA = 255,
  AIS_DAC_256_MALTA = 256,
  AIS_DAC_257_NORWAY = 257,
  AIS_DAC_258_NORWAY = 258,
  AIS_DAC_259_NORWAY = 259,
  AIS_DAC_261_POLAND = 261,
  AIS_DAC_262_MONTENEGRO = 262,
  AIS_DAC_263_PORTUGAL = 263,
  AIS_DAC_264_ROMANIA = 264,
  AIS_DAC_265_SWEDEN = 265,
  AIS_DAC_266_SWEDEN = 266,
  AIS_DAC_267_SLOVAKIA = 267,
  AIS_DAC_268_SAN_MARINO = 268,
  AIS_DAC_269_SWITZERLAND = 269,
  AIS_DAC_270_CZECH_REPUBLIC = 270,
  AIS_DAC_271_TURKEY = 271,
  AIS_DAC_272_UKRAINE = 272,
  AIS_DAC_273_RUSSIAN_FEDERATION = 273,
  AIS_DAC_274_MACEDONIA = 274,
  AIS_DAC_275_LATVIA = 275,
  AIS_DAC_276_ESTONIA = 276,
  AIS_DAC_277_LITHUANIA = 277,
  AIS_DAC_278_SLOVENIA = 278,
  AIS_DAC_279_SERBIA = 279,
  AIS_DAC_301_ANGUILLA = 301,
  AIS_DAC_303_ALASKA = 303,
  AIS_DAC_304_ANTIGUA_AND_BARBUDA = 304,
  AIS_DAC_305_ANTIGUA_AND_BARBUDA = 305,
  AIS_DAC_306_NETHERLANDS_ANTILLES = 306,
  AIS_DAC_307_ARUBA = 307,
  AIS_DAC_308_BAHAMAS = 308,
  AIS_DAC_309_BAHAMAS = 309,
  AIS_DAC_310_BERMUDA = 310,
  AIS_DAC_311_BAHAMAS = 311,
  AIS_DAC_312_BELIZE = 312,
  AIS_DAC_314_BARBADOS = 314,
  AIS_DAC_316_CANADA = 316,
  AIS_DAC_319_CAYMAN_ISLANDS = 319,
  AIS_DAC_321_COSTA_RICA = 321,
  AIS_DAC_323_CUBA = 323,
  AIS_DAC_325_DOMINICA = 325,
  AIS_DAC_327_DOMINICAN_REPUBLIC = 327,
  AIS_DAC_329_GUADELOUPE = 329,
  AIS_DAC_330_GRENADA = 330,
  AIS_DAC_331_GREENLAND = 331,
  AIS_DAC_332_GUATEMALA = 332,
  AIS_DAC_334_HONDURAS = 334,
  AIS_DAC_336_HAITI = 336,
  AIS_DAC_338_UNITED_STATES_OF_AMERICA = 338,
  AIS_DAC_339_JAMAICA = 339,
  AIS_DAC_341_SAINT_KITTS_AND_NEVIS = 341,
  AIS_DAC_343_SAINT_LUCIA = 343,
  AIS_DAC_345_MEXICO = 345,
  AIS_DAC_347_MARTINIQUE = 347,
  AIS_DAC_348_MONTSERRAT = 348,
  AIS_DAC_350_NICARAGUA = 350,
  AIS_DAC_351_PANAMA = 351,
  AIS_DAC_352_PANAMA = 352,
  AIS_DAC_353_PANAMA = 353,
  AIS_DAC_354_PANAMA = 354,
  AIS_DAC_355_PANAMA = 355,
  AIS_DAC_356_PANAMA = 356,
  AIS_DAC_357_PANAMA = 357,
  AIS_DAC_358_PUERTO_RICO = 358,
  AIS_DAC_359_EL_SALVADOR = 359,
  AIS_DAC_361_SAINT_PIERRE_AND_MIQUELON = 361,
  AIS_DAC_362_TRINIDAD_AND_TOBAGO = 362,
  AIS_DAC_364_TURKS_AND_CAICOS_ISLANDS = 364,
  AIS_DAC_366_UNITED_STATES_OF_AMERICA = 366,
  AIS_DAC_367_UNITED_STATES_OF_AMERICA = 367,
  AIS_DAC_368_UNITED_STATES_OF_AMERICA = 368,
  AIS_DAC_369_UNITED_STATES_OF_AMERICA = 369,
  AIS_DAC_370_PANAMA = 370,
  AIS_DAC_371_PANAMA = 371,
  AIS_DAC_372_PANAMA = 372,
  AIS_DAC_373_PANAMA = 373,
  AIS_DAC_375_SAINT_VINCENT_AND_THE_GRENADINES = 375,
  AIS_DAC_376_SAINT_VINCENT_AND_THE_GRENADINES = 376,
  AIS_DAC_377_SAINT_VINCENT_AND_THE_GRENADINES = 377,
  AIS_DAC_378_BRITISH_VIRGIN_ISLANDS = 378,
  AIS_DAC_379_UNITED_STATES_VIRGIN_ISLANDS = 379,
  AIS_DAC_401_AFGHANISTAN = 401,
  AIS_DAC_403_SAUDI_ARABIA = 403,
  AIS_DAC_405_BANGLADESH = 405,
  AIS_DAC_408_BAHRAIN = 408,
  AIS_DAC_410_BHUTAN = 410,
  AIS_DAC_412_CHINA = 412,
  AIS_DAC_413_CHINA = 413,
  AIS_DAC_414_CHINA = 414,
  AIS_DAC_416_TAIWAN = 416,
  AIS_DAC_417_SRI_LANKA = 417,
  AIS_DAC_419_INDIA = 419,
  AIS_DAC_422_IRAN = 422,
  AIS_DAC_423_AZERBAIJAN = 423,
  AIS_DAC_425_IRAQ = 425,
  AIS_DAC_428_ISRAEL = 428,
  AIS_DAC_431_JAPAN = 431,
  AIS_DAC_432_JAPAN = 432,
  AIS_DAC_434_TURKMENISTAN = 434,
  AIS_DAC_436_KAZAKHSTAN = 436,
  AIS_DAC_437_UZBEKISTAN = 437,
  AIS_DAC_438_JORDAN = 438,
  AIS_DAC_440_KOREA = 440,
  AIS_DAC_441_KOREA = 441,
  AIS_DAC_443_STATE_OF_PALESTINE = 443,
  AIS_DAC_445_DEMOCRATIC_PEOPLES_REPUBLIC_OF_KOREA = 445,
  AIS_DAC_447_KUWAIT = 447,
  AIS_DAC_450_LEBANON = 450,
  AIS_DAC_451_KYRGYZ_REPUBLIC = 451,
  AIS_DAC_453_MACAO = 453,
  AIS_DAC_455_MALDIVES = 455,
  AIS_DAC_457_MONGOLIA = 457,
  AIS_DAC_459_NEPAL = 459,
  AIS_DAC_461_OMAN = 461,
  AIS_DAC_463_PAKISTAN = 463,
  AIS_DAC_466_QATAR = 466,
  AIS_DAC_468_SYRIAN_ARAB_REPUBLIC = 468,
  AIS_DAC_470_UNITED_ARAB_EMIRATES = 470,
  AIS_DAC_472_TAJIKISTAN = 472,
  AIS_DAC_473_YEMEN = 473,
  AIS_DAC_475_YEMEN = 475,
  AIS_DAC_477_HONG_KONG = 477,
  AIS_DAC_478_BOSNIA_AND_HERZEGOVINA = 478,
  AIS_DAC_501_ADELIE_LAND = 501,
  AIS_DAC_503_AUSTRALIA = 503,
  AIS_DAC_506_MYANMAR = 506,
  AIS_DAC_508_BRUNEI_DARUSSALAM = 508,
  AIS_DAC_510_MICRONESIA = 510,
  AIS_DAC_511_PALAU = 511,
  AIS_DAC_512_NEW_ZEALAND = 512,
  AIS_DAC_514_CAMBODIA = 514,
  AIS_DAC_515_CAMBODIA = 515,
  AIS_DAC_516_CHRISTMAS_ISLAND = 516,
  AIS_DAC_518_COOK_ISLANDS = 518,
  AIS_DAC_520_FIJI = 520,
  AIS_DAC_523_COCOS = 523,
  AIS_DAC_525_INDONESIA = 525,
  AIS_DAC_529_KIRIBATI = 529,
  AIS_DAC_531_LAO_PEOPLES_DEMOCRATIC_REPUBLIC = 531,
  AIS_DAC_533_MALAYSIA = 533,
  AIS_DAC_536_NORTHERN_MARIANA_ISLANDS = 536,
  AIS_DAC_538_MARSHALL_ISLANDS = 538,
  AIS_DAC_540_NEW_CALEDONIA = 540,
  AIS_DAC_542_NIUE = 542,
  AIS_DAC_544_NAURU = 544,
  AIS_DAC_546_FRENCH_POLYNESIA = 546,
  AIS_DAC_548_PHILIPPINES = 548,
  AIS_DAC_553_PAPUA_NEW_GUINEA = 553,
  AIS_DAC_555_PITCAIRN_ISLAND = 555,
  AIS_DAC_557_SOLOMON_ISLANDS = 557,
  AIS_DAC_559_AMERICAN_SAMOA = 559,
  AIS_DAC_561_SAMOA = 561,
  AIS_DAC_563_SINGAPORE = 563,
  AIS_DAC_564_SINGAPORE = 564,
  AIS_DAC_565_SINGAPORE = 565,
  AIS_DAC_566_SINGAPORE = 566,
  AIS_DAC_567_THAILAND = 567,
  AIS_DAC_570_TONGA = 570,
  AIS_DAC_572_TUVALU = 572,
  AIS_DAC_574_VIETNAM = 574,
  AIS_DAC_576_VANUATU = 576,
  AIS_DAC_577_VANUATU = 577,
  AIS_DAC_578_WALLIS_AND_FUTUNA_ISLANDS = 578,
  AIS_DAC_601_SOUTH_AFRICA = 601,
  AIS_DAC_603_ANGOLA = 603,
  AIS_DAC_605_ALGERIA = 605,
  AIS_DAC_607_SAINT_PAUL_AND_AMSTERDAM_ISLANDS = 607,
  AIS_DAC_608_ASCENSION_ISLAND = 608,
  AIS_DAC_609_BURUNDI = 609,
  AIS_DAC_610_BENIN = 610,
  AIS_DAC_611_BOTSWANA = 611,
  AIS_DAC_612_CENTRAL_AFRICAN_REPUBLIC = 612,
  AIS_DAC_613_CAMEROON = 613,
  AIS_DAC_615_CONGO = 615,
  AIS_DAC_616_COMOROS = 616,
  AIS_DAC_617_CABO_VERDE = 617,
  AIS_DAC_618_CROZET_ARCHIPELAGO = 618,
  AIS_DAC_619_COTE_DIVOIRE = 619,
  AIS_DAC_620_COMOROS = 620,
  AIS_DAC_621_DJIBOUTI = 621,
  AIS_DAC_622_EGYPT = 622,
  AIS_DAC_624_ETHIOPIA = 624,
  AIS_DAC_625_ERITREA = 625,
  AIS_DAC_626_GABONESE_REPUBLIC = 626,
  AIS_DAC_627_GHANA = 627,
  AIS_DAC_629_GAMBIA = 629,
  AIS_DAC_630_GUINEABISSAU = 630,
  AIS_DAC_631_EQUATORIAL_GUINEA = 631,
  AIS_DAC_632_GUINEA = 632,
  AIS_DAC_633_BURKINA_FASO = 633,
  AIS_DAC_634_KENYA = 634,
  AIS_DAC_635_KERGUELEN_ISLANDS = 635,
  AIS_DAC_636_LIBERIA = 636,
  AIS_DAC_637_LIBERIA = 637,
  AIS_DAC_638_SOUTH_SUDAN = 638,
  AIS_DAC_642_LIBYA = 642,
  AIS_DAC_644_LESOTHO = 644,
  AIS_DAC_645_MAURITIUS = 645,
  AIS_DAC_647_MADAGASCAR = 647,
  AIS_DAC_649_MALI = 649,
  AIS_DAC_650_MOZAMBIQUE = 650,
  AIS_DAC_654_MAURITANIA = 654,
  AIS_DAC_655_MALAWI = 655,
  AIS_DAC_656_NIGER = 656,
  AIS_DAC_657_NIGERIA = 657,
  AIS_DAC_659_NAMIBIA = 659,
  AIS_DAC_660_REUNION = 660,
  AIS_DAC_661_RWANDA = 661,
  AIS_DAC_662_SUDAN = 662,
  AIS_DAC_663_SENEGAL = 663,
  AIS_DAC_664_SEYCHELLES = 664,
  AIS_DAC_665_SAINT_HELENA = 665,
  AIS_DAC_666_SOMALIA = 666,
  AIS_DAC_667_SIERRA_LEONE = 667,
  AIS_DAC_668_SAO_TOME_AND_PRINCIPE = 668,
  AIS_DAC_669_SWAZILAND = 669,
  AIS_DAC_670_CHAD = 670,
  AIS_DAC_671_TOGOLESE_REPUBLIC = 671,
  AIS_DAC_672_TUNISIA = 672,
  AIS_DAC_674_TANZANIA = 674,
  AIS_DAC_675_UGANDA = 675,
  AIS_DAC_676_DEMOCRATIC_REPUBLIC_OF_THE_CONGO = 676,
  AIS_DAC_677_TANZANIA = 677,
  AIS_DAC_678_ZAMBIA = 678,
  AIS_DAC_679_ZIMBABWE = 679,
  AIS_DAC_701_ARGENTINE_REPUBLIC = 701,
  AIS_DAC_710_BRAZIL = 710,
  AIS_DAC_720_BOLIVIA = 720,
  AIS_DAC_725_CHILE = 725,
  AIS_DAC_730_COLOMBIA = 730,
  AIS_DAC_735_ECUADOR = 735,
  AIS_DAC_740_FALKLAND_ISLANDS = 740,
  AIS_DAC_745_GUIANA = 745,
  AIS_DAC_750_GUYANA = 750,
  AIS_DAC_755_PARAGUAY = 755,
  AIS_DAC_760_PERU = 760,
  AIS_DAC_765_SURINAME = 765,
  AIS_DAC_770_URUGUAY = 770,
  AIS_DAC_775_VENEZUELA = 775
};

class AisPoint {
 public:
  double lng_deg;
  double lat_deg;

  AisPoint();
  AisPoint(double lng_deg_, double lat_deg_);
};
ostream& operator<< (ostream &o, const AisPoint &position);

//////////////////////////////////////////////////////////////////////
// Support class for decoding
//////////////////////////////////////////////////////////////////////
static const int MAX_BITS = 1192;

class AisBitset : protected bitset<MAX_BITS> {
 public:
  AisBitset();

  AIS_STATUS ParseNmeaPayload(const char *nmea_payload, int pad);

  int GetNumBits() const { return num_bits; }
  int GetNumChars() const { return num_chars; }
  int GetPosition() const { return current_position; }
  int GetRemaining() const { return num_bits - current_position; }

  const AisBitset& SeekRelative(int d) const;
  const AisBitset& SeekTo(size_t pos) const;

  bool operator[](size_t pos) const;

  unsigned int ToUnsignedInt(const size_t start, const size_t len) const;
  int ToInt(const size_t start, const size_t len) const;
  string ToString(const size_t start, const size_t len) const;

  const AisPoint ToAisPoint(const size_t start, const size_t point_size) const;

  // Visible for testing.
  static bitset<6> Reverse(const bitset<6> &bits);

 protected:
  int num_bits;
  int num_chars;

  static bool nmea_ord_initialized_;
  static bitset<6> nmea_ord_[128];
  static const char bits_to_char_tbl_[];

  static void InitNmeaOrd();

 private:
  // This will help uncover dicontinuities when querying sequential bits, i.e.
  // when we query a bit sequence that is not in direct succession of the
  // previous one. In the future, we may use this to automatically determine
  // the next read location.
  // This field is also used to determine the number of remaining bits after the
  // last read position.
  // That being said, the 'start' argument in all the 'To...' methods above is
  // redundant and the only purpose is to discover typos in the bit positions in
  // each message's parse method, i.e. debugging.
  mutable int current_position;
};

class AisMsg {
 public:
  int message_id;
  int repeat_indicator;
  int mmsi;

  // TODO(schwehr): make status private and have accessors.
  bool had_error() const {  return status != AIS_OK;  }
  AIS_STATUS get_error() const { return status; }

  virtual ~AisMsg() {}

 protected:
  AIS_STATUS status;  // AIS_OK or error code
  int num_chars;  // Number of characters in the nmea_payload.
  size_t num_bits;  // Number of bits in the nmea_payload.
  AisBitset bits;  // The bitset that was constructed out of the nmea_payload.

  AisMsg() : status(AIS_UNINITIALIZED), num_chars(0), num_bits(0), bits() {}
  AisMsg(const char *nmea_payload, const size_t pad);

  // Returns true if the msg is in a good state "so far", i.e. either AIS_OK or
  // AIS_UNINITIALIZED.
  bool CheckStatus() const;
};

// TODO(schwehr): factor out commstate from all messages?
class Ais1_2_3 : public AisMsg {
 public:
  int nav_status;
  bool rot_over_range;
  int rot_raw;
  float rot;
  float sog;  // Knots.
  int position_accuracy;
  AisPoint position;
  float cog;  // Degrees.
  int true_heading;
  // TODO(schwehr): What about a leap second when timestamp 60 may be valid?
  int timestamp;
  int special_manoeuvre;
  int spare;
  bool raim;

  // COMM state SOTDMA msgs 1 and 2
  int sync_state;  // SOTDMA and ITDMA
  bool slot_timeout_valid;
  int slot_timeout;

  // Based on slot_timeout which ones are valid
  bool received_stations_valid;
  int received_stations;

  bool slot_number_valid;
  int slot_number;

  bool utc_valid;
  int utc_hour;
  int utc_min;
  int utc_spare;

  bool slot_offset_valid;
  int slot_offset;

  // ITDMA - msg type 3
  bool slot_increment_valid;
  int slot_increment;

  bool slots_to_allocate_valid;
  int slots_to_allocate;

  bool keep_flag_valid;
  bool keep_flag;  // 3.3.7.3.2 Annex 2 ITDMA.  Table 20

  Ais1_2_3(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais1_2_3 &msg);

// 4 bsreport and 11 utc date response
class Ais4_11 : public AisMsg {
 public:
  int year;
  int month;
  int day;
  int hour;
  int minute;
  int second;
  int position_accuracy;
  AisPoint position;
  int fix_type;
  int transmission_ctl;
  int spare;
  bool raim;

  // COMM state SOTDMA msgs 1 and 2
  int sync_state;
  int slot_timeout;

  // Based on slot_timeout which ones are valid
  bool received_stations_valid;
  int received_stations;

  bool slot_number_valid;
  int slot_number;

  bool utc_valid;
  int utc_hour;
  int utc_min;
  int utc_spare;

  bool slot_offset_valid;
  int slot_offset;

  // **NO** ITDMA
  Ais4_11(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais4_11 &msg);

class Ais5 : public AisMsg {
 public:
  int ais_version;
  int imo_num;
  string callsign;
  string name;
  int type_and_cargo;
  int dim_a;
  int dim_b;
  int dim_c;
  int dim_d;
  int fix_type;
  int eta_month;
  int eta_day;
  int eta_hour;
  int eta_minute;
  float draught;  // present static draft. m
  string destination;
  int dte;
  int spare;

  Ais5(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais5 &msg);

// AIS Binary Broadcast message ... parent to many
class Ais6 : public AisMsg {
 public:
  int seq;  // sequence number
  int mmsi_dest;
  bool retransmit;
  int spare;
  int dac;  // dac+fi = app id
  int fi;

  // TODO(schwehr): how to make Ais6 protected?
  Ais6(const char *nmea_payload, const size_t pad);

 protected:
  Ais6() {}
};
ostream& operator<< (ostream &o, const Ais6 &msg);

// http://www.e-navigation.nl/content/monitoring-aids-navigation
// Zeni Lite Buoy Co., Ltd buoy status.
class Ais6_0_0 : public Ais6 {
 public:
  int sub_id;
  float voltage;
  float current;
  bool dc_power_supply;  // False is AC.
  bool light_on;
  bool battery_low;
  bool off_position;
  int spare2;

  Ais6_0_0(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais6_0_0 &msg);

// Text message.  ITU 1371-1
class Ais6_1_0 : public Ais6 {
 public:
  bool ack_required;
  int msg_seq;
  string text;
  int spare2;

  Ais6_1_0(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais6_1_0 &msg);

// Application ack.  ITU 1371-1
class Ais6_1_1 : public Ais6 {
 public:
  int ack_dac;
  int msg_seq;
  int spare2;

  Ais6_1_1(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais6_1_1 &msg);

// Interrogation for a DAC/FI.  ITU 1371-1
class Ais6_1_2 : public Ais6 {
 public:
  int req_dac;
  int req_fi;
  // TODO(schwehr): spare2?

  Ais6_1_2(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais6_1_2 &msg);

// Capability interogation.  ITU 1371-1
class Ais6_1_3 : public Ais6 {
 public:
  unsigned int req_dac;
  unsigned int spare2;
  unsigned int spare3;
  unsigned int spare4;

  Ais6_1_3(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais6_1_3 &msg);

// Capability interogation reply.  ITU 1371-1
// 5.4 International function message 4: Capability reply
class Ais6_1_4 : public Ais6 {
 public:
  int ack_dac;
  std::array<int, 64> capabilities;
  std::array<int, 64> cap_reserved;
  // 126 bits of spare.  So dumb.
  int spare2;
  int spare3;
  int spare4;
  int spare5;

  Ais6_1_4(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais6_1_4 &msg);

// International function message 5: Application ack to addr binary message.
class Ais6_1_5 : public Ais6 {
 public:
  // TODO(schwehr): How to handle the sequence number and retransmit flag?
  int ack_dac;
  int ack_fi;
  int seq_num;
  bool ai_available;  // TODO(schwehr): AI?  Is this the dac/fi being acked?
  int ai_response;
  int spare;
  int spare2;

  Ais6_1_5(const char *nmea_payload, const size_t pad);
};

// IMO Circ 236 Dangerous cargo indication
// Not to be transmitted after 2012-Jan-01
class Ais6_1_12 : public Ais6 {
 public:
  string last_port;
  int utc_month_dep;  // actual time of departure
  int utc_day_dep;
  int utc_hour_dep;
  int utc_min_dep;
  string next_port;
  int utc_month_next;  // estimated arrival
  int utc_day_next;
  int utc_hour_next;
  int utc_min_next;
  string main_danger;
  string imo_cat;
  int un;
  int value;  // TODO(schwehr): units?
  int value_unit;
  int spare2;

  Ais6_1_12(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais6_1_12 &msg);


class Ais6_1_14_Window {
 public:
  AisPoint position;
  int utc_hour_from;
  int utc_min_from;
  int utc_hour_to;
  int utc_min_to;
  int cur_dir;
  float cur_speed;
};


// IMO Circ 236 Tidal window
// Not to be transmitted after 2012-Jan-01
class Ais6_1_14 : public Ais6 {
 public:
  int utc_month;
  int utc_day;
  vector<Ais6_1_14_Window> windows;

  Ais6_1_14(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais6_1_14 &msg);


// IMO Circ 289 Clearance time to enter port
class Ais6_1_18 : public Ais6 {
 public:
  int link_id;
  int utc_month;
  int utc_day;
  int utc_hour;
  int utc_min;
  string port_berth;
  string dest;
  AisPoint position;
  std::array<int, 2> spare2;  // 32 bits per spare

  Ais6_1_18(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais6_1_18 &msg);


// IMO Circ 289 Berthing data
class Ais6_1_20 : public Ais6 {
 public:
  int link_id;
  int length;
  float depth;
  int mooring_position;
  int utc_month;
  int utc_day;
  int utc_hour;
  int utc_min;
  bool services_known;
  // TODO(schwehr): enum of service types
  std::array<int, 26> services;
  string name;
  AisPoint position;

  Ais6_1_20(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais6_1_20 &msg);

class Ais6_1_25_Cargo {
 public:
  int code_type;
  bool imdg_valid;  // also set with BC
  int imdg;
  bool spare_valid;
  int spare;  // imdg or dc or marpols
  bool un_valid;
  int un;
  bool bc_valid;
  int bc;
  bool marpol_oil_valid;
  int marpol_oil;
  bool marpol_cat_valid;
  int marpol_cat;

  Ais6_1_25_Cargo();
  // TODO(schwehr): Add a constructor from an AisBitset.
};

// IMO Circ 289 Dangerous cargo indication 2
// Replaces 8_1_12?
class Ais6_1_25 : public Ais6 {
 public:
  int amount_unit;
  int amount;

  vector<Ais6_1_25_Cargo> cargos;  // 0 to 17 cargo entries

  Ais6_1_25(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais6_1_25 &msg);

// TODO(schwehr): Addressed sensor report 6_1_26.
// TODO(schwehr): IMO Circ 289 Route information 6_1_28.
// TODO(schwehr): IMO Circ 289 Text description 6_1_30.

// IMO Circ 289
// Warning: The bit encoding for 6_1_14_Window and 6_1_32 on
//   the wire has x and y in a different order.
// TODO(schwehr): Reuse Ais6_1_14_Window
class Ais6_1_32_Window {
 public:
  AisPoint position;
  int from_utc_hour;
  int from_utc_min;
  int to_utc_hour;
  int to_utc_min;
  int cur_dir;
  float cur_speed;  // knots

  Ais6_1_32_Window();
};


// IMO Circ 289 Tidal window
class Ais6_1_32 : public Ais6 {
 public:
  int utc_month;
  int utc_day;
  vector<Ais6_1_32_Window> windows;

  Ais6_1_32(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais6_1_32 &msg);

// Number of persons on board.  ITU 1371-1
class Ais6_1_40 : public Ais6 {
 public:
  int persons;
  int spare2;

  Ais6_1_40(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais6_1_40 &msg);

//////////////////////////////////////////////////////////////////////

// 7 and 13 are ACKs for msg 6 and 12
class Ais7_13 : public AisMsg {
 public:
  int spare;

  vector<int> dest_mmsi;
  vector<int> seq_num;

  Ais7_13(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais7_13 &msg);

// AIS Binary Broadcast message ... parent to many
class Ais8 : public AisMsg {
 public:
  int spare;
  // TODO(schwehr): seq? // ITU M.R. 1371-3 Anex 2 5.3.1
  int dac;  // dac+fi = app id
  int fi;

  // TODO(schwehr): make Ais8 protected
  Ais8(const char *nmea_payload, const size_t pad);

 protected:
  Ais8() {}
};
ostream& operator<< (ostream &o, const Ais8 &msg);

// Text telegram ITU 1371-1
class Ais8_1_0 : public Ais8 {
 public:
  bool ack_required;
  int msg_seq;
  string text;
  int spare2;

  Ais8_1_0(const char *nmea_payload, size_t pad);
};
ostream& operator<< (ostream &o, const Ais8_1_0 &msg);

// 8_1_1 No message
// 8_1_2 No message
// 8_1_3 No message
// 8_1_4 No message

// IMO Circ 289 met hydro - Not to be transmitted after 2013-Jan-01
// See also IMO Circ 236
class Ais8_1_11 : public Ais8 {
 public:
  AisPoint position;
  int day;
  int hour;
  int minute;
  int wind_ave;  // kts
  int wind_gust;  // kts
  int wind_dir;
  int wind_gust_dir;
  float air_temp;  // C
  int rel_humid;
  float dew_point;
  float air_pres;
  int air_pres_trend;
  float horz_vis;  // NM
  float water_level;  // m
  int water_level_trend;
  float surf_cur_speed;
  int surf_cur_dir;
  float cur_speed_2;  // kts
  int cur_dir_2;
  int cur_depth_2;  // m
  float cur_speed_3;  // kts
  int cur_dir_3;
  int cur_depth_3;  // m
  float wave_height;  // m
  int wave_period;
  int wave_dir;
  float swell_height;
  int swell_period;
  int swell_dir;
  int sea_state;  // beaufort scale
  float water_temp;
  int precip_type;
  float salinity;  // Part per mil (1/1000).
  int ice;  // yes/no/undef/unknown
  int spare2;
  int extended_water_level;  // OHMEX uses this for extra water level precision

  Ais8_1_11(const char *nmea_payload, size_t pad);
};
ostream& operator<< (ostream &o, const Ais8_1_11 &msg);


// IMO Circ 236 Fairway closed - Not to be transmitted after 2012-Jan-01
class Ais8_1_13 : public Ais8 {
 public:
  string reason;
  string location_from;
  string location_to;
  int radius;
  int units;
  // TODO(schwehr): utc?  warning: day/month out of order
  int day_from;
  int month_from;
  int hour_from;
  int minute_from;

  int day_to;
  int month_to;
  int hour_to;
  int minute_to;
  int spare2;

  Ais8_1_13(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais8_1_13 &msg);

// IMO Circ 236 Extended ship static and voyage data
// Not to be transmitted after 2012-Jan-01
class Ais8_1_15 : public Ais8 {
 public:
  float air_draught;
  int spare2;

  Ais8_1_15(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais8_1_15 &msg);

// IMO Circ 236 Number of persons on board
class Ais8_1_16 : public Ais8 {
 public:
  int persons;
  int spare2;

  Ais8_1_16(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais8_1_16 &msg);

class Ais8_1_17_Target {
 public:
  int type;
  string id;
  int spare;
  AisPoint position;
  int cog;
  int timestamp;
  int sog;

  Ais8_1_17_Target();
};

// IMO Circ 236 VTS Generated/synthetic targets
class Ais8_1_17 : public Ais8 {
 public:
  vector<Ais8_1_17_Target> targets;

  Ais8_1_17(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais8_1_17 &msg);

// No 8_1_18

// IMO Circ 289 Marine traffic signal
class Ais8_1_19 : public Ais8 {
 public:
  int link_id;
  string name;
  AisPoint position;  // funny bit count
  int status;
  int signal;
  int utc_hour_next;
  int utc_min_next;
  int next_signal;
  std::array<int, 4> spare2;

  Ais8_1_19(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais8_1_19 &msg);

// No message 8_1_20

// IMO Circ 289 Weather observation report from ship
class Ais8_1_21 : public Ais8 {
 public:
  int type_wx_report;

  // TYPE 0
  string location;
  AisPoint position;  // 25, 24 bits
  int utc_day;
  int utc_hour;
  int utc_min;
  // wx - use wx[0]
  float horz_viz;  // nautical miles
  int humidity;  // %
  int wind_speed;  // ave knots
  int wind_dir;
  float pressure;  // hPa - float needed for type 1
  int pressure_tendency;
  float air_temp;  // C
  float water_temp;  // C
  int wave_period;  // s
  float wave_height;
  int wave_dir;
  float swell_height;  // m
  int swell_dir;
  int swell_period;  // s
  int spare2;

  // TYPE 1 - !@#$!!!!!
  // x, y
  int utc_month;
  // utc_day, hour, min
  int cog;
  float sog;
  int heading;  // Assume this is true degrees????
  // pressure defined in type 0
  float rel_pressure;  // 3 hour hPa
  // pressure_tendenc defined in type 0
  // wind_dir defined in type 0
  float wind_speed_ms;  // m/s
  int wind_dir_rel;
  float wind_speed_rel;  // m/s
  float wind_gust_speed;  // m/s
  int wind_gust_dir;
  int air_temp_raw;  // TODO(schwehr): Convert this to C.  Kelvin makes no sense
  // humidity defined in type 0
  // sea_temp_k
  int water_temp_raw;  // TODO(schwehr): fix this
  // hor_viz
  int wx[3];  // current, past 1, past 2
  int cloud_total;
  int cloud_low;
  int cloud_low_type;
  int cloud_middle_type;
  int cloud_high_type;
  float alt_lowest_cloud_base;
  // wave_period
  // wave_height
  // swell_dir
  // swell_period
  // swell_height
  int swell_dir_2;
  int swell_period_2;
  float swell_height_2;
  float ice_thickness;  // Network is cm, storing m.
  int ice_accretion;
  int ice_accretion_cause;
  int sea_ice_concentration;
  int amt_type_ice;
  int ice_situation;
  int ice_devel;
  int bearing_ice_edge;

  Ais8_1_21(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais8_1_21 &msg);


const size_t AIS8_1_22_NUM_NAMES = 128;
const size_t AIS8_1_22_SUBAREA_SIZE = 87;
extern const char *ais8_1_22_notice_names[AIS8_1_22_NUM_NAMES];

enum Ais8_1_22_AreaShapeEnum {
  AIS8_1_22_SHAPE_ERROR = -1,
  AIS8_1_22_SHAPE_CIRCLE = 0,  // OR Point.
  AIS8_1_22_SHAPE_RECT = 1,
  AIS8_1_22_SHAPE_SECTOR = 2,
  AIS8_1_22_SHAPE_POLYLINE = 3,
  AIS8_1_22_SHAPE_POLYGON = 4,
  AIS8_1_22_SHAPE_TEXT = 5,
  AIS8_1_22_SHAPE_RESERVED_6 = 6,
  AIS8_1_22_SHAPE_RESERVED_7 = 7
};

extern const char *ais8_1_22_shape_names[8];

// Sub-Areas for the Area Notice class

class Ais8_1_22_SubArea {
 public:
  virtual Ais8_1_22_AreaShapeEnum getType() const = 0;
  virtual ~Ais8_1_22_SubArea() {}
};

Ais8_1_22_SubArea*
ais8_1_22_subarea_factory(const AisBitset &bs,
                          const size_t offset);

// or Point if radius is 0
class Ais8_1_22_Circle : public Ais8_1_22_SubArea {
 public:
  AisPoint position;  // Longitude and latitude.
  // Going to assume that the precision is not useful.
  int precision;  // How many decimal places for x and y.
  int radius_m;
  unsigned int spare;  // 18 bits.

  Ais8_1_22_Circle(const AisBitset &bs, const size_t offset);
  ~Ais8_1_22_Circle() {}
  Ais8_1_22_AreaShapeEnum getType() const {return AIS8_1_22_SHAPE_CIRCLE;}
};

class Ais8_1_22_Rect : public Ais8_1_22_SubArea {
 public:
  AisPoint position;  // Longitude and latitude.
  int precision;  // How many decimal places for x and y.  Useless.
  int e_dim_m;  // East dimension in meters.
  int n_dim_m;
  int orient_deg;  // Orientation in degrees from true north.
  unsigned int spare;  // 5 bits.

  Ais8_1_22_Rect(const AisBitset &bs, const size_t offset);
  ~Ais8_1_22_Rect() {}
  Ais8_1_22_AreaShapeEnum getType() const {return AIS8_1_22_SHAPE_RECT;}
};

class Ais8_1_22_Sector : public Ais8_1_22_SubArea {
 public:
  AisPoint position;  // Longitude and latitude.
  // TODO(schwehr): precision in IMO, but not RTCM.  Double check.
  int precision;  // How many decimal places for x and y?
  int radius_m;
  int left_bound_deg;
  int right_bound_deg;

  Ais8_1_22_Sector(const AisBitset &bs, const size_t offset);
  ~Ais8_1_22_Sector() {}
  Ais8_1_22_AreaShapeEnum getType() const {return AIS8_1_22_SHAPE_SECTOR;}
};

// Or Waypoint
// Must have a point before on the VDL
// TODO(schwehr): do I bring in the prior point x, y, precision?
class Ais8_1_22_Polyline : public Ais8_1_22_SubArea {
 public:
  // TODO(schwehr): int precision; // How many decimal places for x and y.

  // Up to 4 points
  vector<float> angles;
  vector<float> dists_m;
  unsigned int spare;  // 2 bit.

  Ais8_1_22_Polyline(const AisBitset &bs, const size_t offset);
  ~Ais8_1_22_Polyline() {}
  Ais8_1_22_AreaShapeEnum getType() const {return AIS8_1_22_SHAPE_POLYLINE;}
};

// TODO(schwehr): Bring in the prior point?  And do we fold the sub area data
// into one polygon if there are more than one?
class Ais8_1_22_Polygon : public Ais8_1_22_SubArea {
 public:
  // TODO(schwehr): int precision; // How many decimal places for x and y.

  // Up to 4 points in a first message, but aggregated if multiple sub areas
  vector<float> angles;
  vector<float> dists_m;
  unsigned int spare;  // 2 bit

  Ais8_1_22_Polygon(const AisBitset &bs, const size_t offset);
  ~Ais8_1_22_Polygon() {}
  Ais8_1_22_AreaShapeEnum getType() const {return AIS8_1_22_SHAPE_POLYGON;}
};


class Ais8_1_22_Text : public Ais8_1_22_SubArea {
 public:
  string text;
  // TODO(schwehr): spare?

  Ais8_1_22_Text(const AisBitset &bs, const size_t offset);
  ~Ais8_1_22_Text() {}
  Ais8_1_22_AreaShapeEnum getType() const {return AIS8_1_22_SHAPE_TEXT;}
};

// Area Notice class

class Ais8_1_22 : public Ais8 {
 public:
  int link_id;  // 10 bit id to match up text blocks.
  int notice_type;  // Area type / Notice Description.
  int month;  // These are in UTC.
  int day;
  int hour;
  int minute;
  int duration_minutes;  // Time from the start until the notice expires.

  // 1 or more sub messages
  vector<Ais8_1_22_SubArea *> sub_areas;

  Ais8_1_22(const char *nmea_payload, const size_t pad);
  ~Ais8_1_22();
};
ostream& operator<< (ostream& o, Ais8_1_22 const& msg);

// No message 8_1_23

// IMO Circ 289 Extended ship static and voyage-related
class Ais8_1_24 : public Ais8 {
 public:
  int link_id;
  float air_draught;  // m
  string last_port;
  std::array<std::string, 2> next_ports;

  // TODO(schwehr): enum list of param types
  std::array<int, 26> solas_status;  // 0 NA, 1 operational, 2 SNAFU, 3 no data
  int ice_class;
  int shaft_power;  // horses
  int vhf;
  string lloyds_ship_type;
  int gross_tonnage;
  int laden_ballast;
  int heavy_oil;
  int light_oil;
  int diesel;
  int bunker_oil;  // tonnes
  int persons;
  int spare2;

  Ais8_1_24(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais8_1_24 &msg);

// No message 8_1_25

const size_t AIS8_1_26_REPORT_SIZE = 112;

enum Ais8_1_26_SensorEnum {
  AIS8_1_26_SENSOR_ERROR = -1,
  AIS8_1_26_SENSOR_LOCATION = 0,
  AIS8_1_26_SENSOR_STATION = 1,
  AIS8_1_26_SENSOR_WIND = 2,
  AIS8_1_26_SENSOR_WATER_LEVEL = 3,
  AIS8_1_26_SENSOR_CURR_2D = 4,
  AIS8_1_26_SENSOR_CURR_3D = 5,
  AIS8_1_26_SENSOR_HORZ_FLOW = 6,
  AIS8_1_26_SENSOR_SEA_STATE = 7,
  AIS8_1_26_SENSOR_SALINITY = 8,
  AIS8_1_26_SENSOR_WX = 9,
  AIS8_1_26_SENSOR_AIR_DRAUGHT = 10,
  AIS8_1_26_SENSOR_RESERVED_11 = 11,
  AIS8_1_26_SENSOR_RESERVED_12 = 12,
  AIS8_1_26_SENSOR_RESERVED_13 = 13,
  AIS8_1_26_SENSOR_RESERVED_14 = 14,
  AIS8_1_26_SENSOR_RESERVED_15 = 15,
};

class Ais8_1_26_SensorReport {
 public:
  int report_type;
  int utc_day;
  int utc_hr;
  int utc_min;
  int site_id;  // aka link_id

  virtual Ais8_1_26_SensorEnum getType() const = 0;
  virtual ~Ais8_1_26_SensorReport() {}
};

Ais8_1_26_SensorReport*
ais8_1_26_sensor_report_factory(const AisBitset &bs,
                                const size_t offset);

class Ais8_1_26_Location : public Ais8_1_26_SensorReport {
 public:
  AisPoint position;
  float z;  // alt in m from MSL
  int owner;
  int timeout;
  int spare;

  Ais8_1_26_Location(const AisBitset &bs, const size_t offset);
  Ais8_1_26_Location() {}
  Ais8_1_26_SensorEnum getType() const { return AIS8_1_26_SENSOR_LOCATION; }
};

class Ais8_1_26_Station : public Ais8_1_26_SensorReport {
 public:
  string name;
  int spare;

  Ais8_1_26_Station(const AisBitset &bs, const size_t offset);
  Ais8_1_26_Station() {}
  Ais8_1_26_SensorEnum getType() const {return AIS8_1_26_SENSOR_STATION;}
};

class Ais8_1_26_Wind : public Ais8_1_26_SensorReport {
 public:
  int wind_speed;  // knots
  int wind_gust;  // knots
  int wind_dir;
  int wind_gust_dir;
  int sensor_type;
  int wind_forecast;  // knots
  int wind_gust_forecast;  // knots
  int wind_dir_forecast;
  int utc_day_forecast;
  int utc_hour_forecast;
  int utc_min_forecast;
  int duration;
  int spare;

  Ais8_1_26_Wind(const AisBitset &bs, const size_t offset);
  Ais8_1_26_Wind() {}
  Ais8_1_26_SensorEnum getType() const {return AIS8_1_26_SENSOR_WIND;}
};

class Ais8_1_26_WaterLevel : public Ais8_1_26_SensorReport {
 public:
  int type;
  float level;  // m.  assuming it is being stored at 0.01 m inc.
  int trend;
  int vdatum;
  int sensor_type;
  int forecast_type;
  float level_forecast;
  int utc_day_forecast;
  int utc_hour_forecast;
  int utc_min_forecast;
  int duration;  // minutes
  int spare;

  Ais8_1_26_WaterLevel(const AisBitset &bs, const size_t offset);
  Ais8_1_26_WaterLevel() {}
  Ais8_1_26_SensorEnum getType() const {return AIS8_1_26_SENSOR_WATER_LEVEL;}
};

class Ais8_1_26_Curr2D_Current {
 public:
  float speed;  // knots
  int dir;
  int depth;  // m
};

class Ais8_1_26_Curr2D : public Ais8_1_26_SensorReport {
 public:
  Ais8_1_26_Curr2D_Current currents[3];
  int type;
  int spare;

  Ais8_1_26_Curr2D(const AisBitset &bs, const size_t offset);
  Ais8_1_26_Curr2D() {}
  Ais8_1_26_SensorEnum getType() const {return AIS8_1_26_SENSOR_CURR_2D;}
};

class Ais8_1_26_Curr3D_Current {
 public:
  float north;
  float east;
  float up;
  int depth;  // m
};

class Ais8_1_26_Curr3D : public Ais8_1_26_SensorReport {
 public:
  Ais8_1_26_Curr3D_Current currents[2];
  int type;
  int spare;

  Ais8_1_26_Curr3D(const AisBitset &bs, const size_t offset);
  Ais8_1_26_Curr3D() {}
  Ais8_1_26_SensorEnum getType() const {return AIS8_1_26_SENSOR_CURR_3D;}
};

class Ais8_1_26_HorzFlow_Current {
 public:
  int bearing;  // deg
  int dist;  // m
  float speed;  // knots
  int dir;  // deg
  int level;  // m
};

class Ais8_1_26_HorzFlow : public Ais8_1_26_SensorReport {
 public:
  Ais8_1_26_HorzFlow_Current currents[2];
  int spare;

  Ais8_1_26_HorzFlow(const AisBitset &bs, const size_t offset);
  Ais8_1_26_HorzFlow() {}
  Ais8_1_26_SensorEnum getType() const {return AIS8_1_26_SENSOR_HORZ_FLOW;}
};

class Ais8_1_26_SeaState : public Ais8_1_26_SensorReport {
 public:
  float swell_height;
  int swell_period;  // seconds
  int swell_dir;  // deg
  int sea_state;
  int swell_sensor_type;
  float water_temp;  // C
  float water_temp_depth;  // m
  int water_sensor_type;
  float wave_height;
  int wave_period;  // seconds
  int wave_dir;  // deg
  int wave_sensor_type;
  float salinity;

  Ais8_1_26_SeaState(const AisBitset &bs, const size_t offset);
  Ais8_1_26_SeaState() {}
  Ais8_1_26_SensorEnum getType() const {return AIS8_1_26_SENSOR_SEA_STATE;}
};

class Ais8_1_26_Salinity : public Ais8_1_26_SensorReport {
 public:
  float water_temp;  // C
  float conductivity;  // siemens/m
  float pressure;  // decibars
  float salinity;  // 0/00 ppt
  int salinity_type;
  int sensor_type;
  int spare[2];

  Ais8_1_26_Salinity(const AisBitset &bs, const size_t offset);
  Ais8_1_26_Salinity() {}
  Ais8_1_26_SensorEnum getType() const {return AIS8_1_26_SENSOR_SALINITY;}
};

class Ais8_1_26_Wx : public Ais8_1_26_SensorReport {
 public:
  float air_temp;  // C
  int air_temp_sensor_type;
  int precip;
  float horz_vis;  // nm
  float dew_point;  // C
  int dew_point_type;
  float air_pressure;  // Pascals (Pa).
  int air_pressure_trend;
  int air_pressor_type;
  float salinity;  // 0/00 ppt
  int spare;

  Ais8_1_26_Wx(const AisBitset &bs, const size_t offset);
  Ais8_1_26_Wx() {}
  Ais8_1_26_SensorEnum getType() const {return AIS8_1_26_SENSOR_WX;}
};

class Ais8_1_26_AirDraught : public Ais8_1_26_SensorReport {
 public:
  float draught;
  float gap;
  float forecast_gap;
  int trend;
  int utc_day_forecast;
  int utc_hour_forecast;
  int utc_min_forecast;
  int spare;

  Ais8_1_26_AirDraught(const AisBitset &bs, const size_t offset);
  Ais8_1_26_AirDraught() {}
  Ais8_1_26_SensorEnum getType() const {return AIS8_1_26_SENSOR_AIR_DRAUGHT;}
};

// IMO Circ 289 Environmental
class Ais8_1_26 : public Ais8 {
 public:
  vector<Ais8_1_26_SensorReport *> reports;

  Ais8_1_26(const char *nmea_payload, const size_t pad);
  ~Ais8_1_26();
};
ostream& operator<< (ostream &o, const Ais8_1_26 &msg);


// IMO Circ 289 Route information
class Ais8_1_27 : public Ais8 {
 public:
  int link_id;
  int sender_type;
  int route_type;
  int utc_month;
  int utc_day;
  int utc_hour;
  int utc_min;
  int duration;
  vector<AisPoint> waypoints;

  Ais8_1_27(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais8_1_27 &msg);


//  No message 8_1_28


// IMO Circ 289 Text description
class Ais8_1_29 : public Ais8 {
 public:
  int link_id;
  string text;
  int spare2;

  Ais8_1_29(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais8_1_29 &msg);


// No message 8_1_30

// IMO Circ 289 Meteorological and Hydrographic data
// Section 1, Table 1.1
// TODO(schwehr): is this exactly the same as 8_1_11 or has anything changed?
//       x,y swapped.
class Ais8_1_31 : public Ais8 {
 public:
  AisPoint position;  // Opposite the bit order of 8_1_11
  int position_accuracy;  // New field
  int utc_day;
  int utc_hour;
  int utc_min;
  int wind_ave;  // kts
  int wind_gust;  // kts
  int wind_dir;
  int wind_gust_dir;
  float air_temp;  // C
  int rel_humid;
  float dew_point;
  float air_pres;  // Pascals (Pa).
  int air_pres_trend;
  float horz_vis;  // NM
  float water_level;  // m
  int water_level_trend;

  float surf_cur_speed;
  int surf_cur_dir;
  float cur_speed_2;  // kts
  int cur_dir_2;
  int cur_depth_2;  // m
  float cur_speed_3;  // kts
  int cur_dir_3;
  int cur_depth_3;  // m

  float wave_height;  // m
  int wave_period;
  int wave_dir;
  float swell_height;  // m
  int swell_period;
  int swell_dir;
  int sea_state;  // beaufort scale - Table 1.2
  float water_temp;
  int precip_type;
  float salinity;
  int ice;  // yes/no/undef/unknown
  int spare2;

  Ais8_1_31(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais8_1_31 &msg);

// TODO(schwehr): Persons on board ITU 1371-1 8_1_40.

// ECE-TRANS-SC3-2006-10e-RIS.pdf - River Information System
// Inland ship static and voyage related data
class Ais8_200_10 : public Ais8 {
 public:
  string eu_id;  // European Vessel ID - 8 characters
  float length;  // m
  float beam;  // m
  int ship_type;
  int haz_cargo;
  float draught;
  int loaded;
  // Sensor quality.
  int speed_qual;
  int course_qual;
  int heading_qual;
  int spare2;

  Ais8_200_10(const char *nmea_payload, const size_t pad);
};

// http://www.ris.eu/docs/File/536/vessel_traking_and_tracing_standard_ed1-2_ccnr_23-apr_2013_en.pdf
// ETA at lock/bridge/terminal
class Ais8_200_21 : public Ais8 {
 public:
  string country;         // UN country code         0 = not available = default
  string location;        // UN location code        0 = not available = default
  string section;         // Fairway section number  0 = not available = default
  string terminal;        // Terminal code           0 = not available = default
  string hectometre;      // Fairway hectometre      0 = not available = default
  // Examples for previous fields.  See:
  // http://www.ris.eu/docs/File/427/implementation_location_code_austria.pdf

  // ETA at lock/bridge/terminal - Estimated Time of Arrival; MMDDHHMM UTC
  int eta_month;          // 1 - 12;  0 = not available = default
  int eta_day;            // 1 - 31;  0 = not available = default
  int eta_hour;           // 0 - 23; 24 = not available = default
  int eta_minute;         // 0 - 59; 60 = not available = default
  int tugboats;           // 0 - 6,   7 = unknown = default
  // Maximum present static air draught 0
  float air_draught;      // 4000 (rest not used), in 1/100m, 0 = not used
  int spare2;             // 5 bits  Not used, should be set to zero.

  Ais8_200_21(const char *nmea_payload, const size_t pad);
};

// vessel_traking_and_tracing_standard_ed1-2_ccnr_23-apr_2013_en.pdf
// RTA at lock/bridge/terminal
class Ais8_200_22 : public Ais8 {
 public:
  string country;         // UN country code         0 = not available
  string location;        // UN location code        0 = not available
  string section;         // Fairway section number  0 = not available
  string terminal;        // Terminal code           0 = not available
  string hectometre;      // Fairway hectometre      0 = not available
  // Examples for previous fields.  See:
  // http://www.ris.eu/docs/File/427/implementation_location_code_austria.pdf

  // RTA at lock/bridge/terminal - Recommended Time of Arrival; MMDDHHMM UTC
  int rta_month;          //  0 = not available
  int rta_day;            //  0 = not available
  int rta_hour;           // 24 = not available
  int rta_minute;         // 60 = not available
  int lock_status;        // Lock/bridge/terminal status
                          // 0 = operational
                          // 1 = limited operation
                          // 2 = out of order
                          // 3 = not available
  int spare2;             // Spare

  Ais8_200_22(const char *nmea_payload, const size_t pad);
};

// ECE-TRANS-SC3-2006-10e-RIS.pdf - River Information System
// EMMA warning
class Ais8_200_23 : public Ais8 {
 public:
  int utc_year_start;
  int utc_month_start;
  int utc_day_start;
  int utc_year_end;
  int utc_month_end;
  int utc_day_end;
  int utc_hour_start;
  int utc_min_start;
  int utc_hour_end;
  int utc_min_end;
  AisPoint position1;
  AisPoint position2;
  int type;
  int min;
  int max;
  int classification;
  int wind_dir;  // EMMA CODE
  int spare2;

  Ais8_200_23(const char *nmea_payload, const size_t pad);
};

// ECE-TRANS-SC3-2006-10e-RIS.pdf - River Information System
// Water Level
class Ais8_200_24 : public Ais8 {
 public:
  // UN 2 letter code.  See Comtrade Country Code and Name.
  // https://unstats.un.org/unsd/tradekb/Knowledgebase/50377/Comtrade-Country-Code-and-Name
  string country;
  std::array<int, 4> gauge_ids;
  std::array<float, 4> levels;  // m

  Ais8_200_24(const char *nmea_payload, const size_t pad);
};

// ECE-TRANS-SC3-2006-10e-RIS.pdf - River Information System
// Signal status
class Ais8_200_40 : public Ais8 {
 public:
  AisPoint position;
  int form;
  int dir;  // degrees
  int stream_dir;
  int status_raw;
  // TODO(schwehr): int status[9];  // WTF is the encoding for this?
  int spare2;

  Ais8_200_40(const char *nmea_payload, const size_t pad);
};

// ECE-TRANS-SC3-2006-10e-RIS.pdf - River Information System
// Number of persons on board
class Ais8_200_55 : public Ais8 {
 public:
  int crew;
  int passengers;
  int yet_more_personnel;
  std::array<int, 3> spare2;  // 51 spare bits.

  Ais8_200_55(const char *nmea_payload, const size_t pad);
};

enum Ais8_366_22_AreaShapeEnum {
  AIS8_366_22_SHAPE_ERROR = -1,
  AIS8_366_22_SHAPE_CIRCLE = 0,
  AIS8_366_22_SHAPE_RECT = 1,
  AIS8_366_22_SHAPE_SECTOR = 2,
  AIS8_366_22_SHAPE_POLYLINE = 3,
  AIS8_366_22_SHAPE_POLYGON = 4,
  AIS8_366_22_SHAPE_TEXT = 5,
  AIS8_366_22_SHAPE_RESERVED_6 = 6,
  AIS8_366_22_SHAPE_RESERVED_7 = 7
};

extern const char *shape_names[8];

class Ais8_366_22_SubArea {
 public:
    virtual Ais8_366_22_AreaShapeEnum getType() = 0;
    virtual ~Ais8_366_22_SubArea() { }
};

Ais8_366_22_SubArea*
ais8_366_22_subarea_factory(const AisBitset &bs,
                            const size_t offset);

// or Point if radius is 0
class Ais8_366_22_Circle : public Ais8_366_22_SubArea {
 public:
  AisPoint position;
  // TODO(schwehr): int precision
  int radius_m;
  unsigned int spare;

  Ais8_366_22_Circle(const AisBitset &bs, const size_t offset);
  ~Ais8_366_22_Circle() {}
  Ais8_366_22_AreaShapeEnum getType() {return AIS8_366_22_SHAPE_CIRCLE;}
};

class Ais8_366_22_Rect : public Ais8_366_22_SubArea {
 public:
  AisPoint position;  // longitude and latitude
  // TODO(schwehr): int precision
  int e_dim_m;  // East dimension in meters
  int n_dim_m;
  int orient_deg;  // Orientation in degrees from true north
  unsigned int spare;  // 5 bits

  Ais8_366_22_Rect(const AisBitset &bs, const size_t offset);
  ~Ais8_366_22_Rect() {}
  Ais8_366_22_AreaShapeEnum getType() {return AIS8_366_22_SHAPE_RECT;}
};

class Ais8_366_22_Sector : public Ais8_366_22_SubArea {
 public:
  AisPoint position;
  // TODO(schwehr): int precision
  int radius_m;
  int left_bound_deg;
  int right_bound_deg;
  // TODO(schwehr): spare?

  Ais8_366_22_Sector(const AisBitset &bs, const size_t offset);
  ~Ais8_366_22_Sector() {}
  Ais8_366_22_AreaShapeEnum getType() {return AIS8_366_22_SHAPE_SECTOR;}
};

// Or Waypoint
// Must have a point before on the VDL, but pulled together here.
class Ais8_366_22_Polyline : public Ais8_366_22_SubArea {
 public:
  AisPoint position;  // longitude and latitude
  // TODO(schwehr): precision

  // Up to 4 points
  vector<float> angles;
  vector<float> dists_m;
  unsigned int spare;

  Ais8_366_22_Polyline(const AisBitset &bs, const size_t offset);
  ~Ais8_366_22_Polyline() {}
  Ais8_366_22_AreaShapeEnum getType() {return AIS8_366_22_SHAPE_POLYLINE;}
};

class Ais8_366_22_Polygon : public Ais8_366_22_SubArea {
 public:
  AisPoint position;  // longitude and latitude
  // TODO(schwehr): precision?

  // Up to 4 points in a first message, but aggregated if multiple sub areas
  vector<float> angles;
  vector<float> dists_m;
  unsigned int spare;

  Ais8_366_22_Polygon(const AisBitset &bs, const size_t offset);
  ~Ais8_366_22_Polygon() {}
  Ais8_366_22_AreaShapeEnum getType() {return AIS8_366_22_SHAPE_POLYGON;}
};

class Ais8_366_22_Text : public Ais8_366_22_SubArea {
 public:
  string text;
  unsigned int spare;  // 3 bits

  Ais8_366_22_Text(const AisBitset &bs, const size_t offset);
  ~Ais8_366_22_Text() {}
  Ais8_366_22_AreaShapeEnum getType() {return AIS8_366_22_SHAPE_TEXT;}
};

class Ais8_366_22 : public Ais8 {
 public:
  // Common block at the front
  int link_id;  // 10 bit id to match up text blocks
  int notice_type;  // area_type / Notice Description
  int month;  // These really are in utc
  int day;
  int utc_hour;
  int utc_minute;
  int duration_minutes;  // Time from the start until the notice expires
  // 1 or more sub messages

  vector<Ais8_366_22_SubArea *> sub_areas;

  Ais8_366_22(const char *nmea_payload, const size_t pad);
  ~Ais8_366_22();
};
ostream& operator<< (ostream& o, Ais8_366_22 const& msg);

const size_t AIS8_366_22_NUM_NAMES = 128;
extern const char *ais8_366_22_notice_names[AIS8_366_22_NUM_NAMES];

// 366 34 - Kurt older whale message 2008-2010
// TODO(schwehr): Ais8_366_34

// USCG Blue Force encrypted message.
class Ais8_366_56 : public Ais8 {
 public:
  vector<unsigned char> encrypted;

  Ais8_366_56(const char *nmea_payload, const size_t pad);
};

class Ais8_367_22_SubArea {
 public:
  virtual Ais8_366_22_AreaShapeEnum getType() const = 0;
  virtual ~Ais8_367_22_SubArea() { }
};

Ais8_367_22_SubArea*
ais8_367_22_subarea_factory(const AisBitset &bs,
                            const size_t offset);

class Ais8_367_22_Circle : public Ais8_367_22_SubArea {
 public:
  AisPoint position;
  int precision;
  int radius_m;
  unsigned int spare;

  Ais8_367_22_Circle(const AisBitset &bs, const size_t offset);
  ~Ais8_367_22_Circle() {}
  Ais8_366_22_AreaShapeEnum getType() const {return AIS8_366_22_SHAPE_CIRCLE;}
};

class Ais8_367_22_Rect : public Ais8_367_22_SubArea {
 public:
  AisPoint position;
  int precision;
  int e_dim_m;
  int n_dim_m;
  int orient_deg;
  unsigned int spare;

  Ais8_367_22_Rect(const AisBitset &bs, const size_t offset);
  ~Ais8_367_22_Rect() {}
  Ais8_366_22_AreaShapeEnum getType() const {return AIS8_366_22_SHAPE_RECT;}
};

class Ais8_367_22_Sector : public Ais8_367_22_SubArea {
 public:
  AisPoint position;
  int precision;
  int radius_m;
  int left_bound_deg;
  int right_bound_deg;
  int spare;

  Ais8_367_22_Sector(const AisBitset &bs, const size_t offset);
  ~Ais8_367_22_Sector() {}
  Ais8_366_22_AreaShapeEnum getType() const {return AIS8_366_22_SHAPE_SECTOR;}
};

// Polyline or Polygon
class Ais8_367_22_Poly : public Ais8_367_22_SubArea {
 public:
  Ais8_366_22_AreaShapeEnum shape;
  AisPoint position;
  int precision;

  // Up to 4 points
  vector<float> angles;
  vector<float> dists_m;
  unsigned int spare;

  Ais8_367_22_Poly(const AisBitset &bs, const size_t offset,
                   Ais8_366_22_AreaShapeEnum area_shape);
  ~Ais8_367_22_Poly() {}
  Ais8_366_22_AreaShapeEnum getType() const {return shape;}
};

class Ais8_367_22_Text : public Ais8_367_22_SubArea {
 public:
  string text;
  unsigned int spare;  // 3 bits

  Ais8_367_22_Text(const AisBitset &bs, const size_t offset);
  ~Ais8_367_22_Text() {}
  Ais8_366_22_AreaShapeEnum getType() const {return AIS8_366_22_SHAPE_TEXT;}
};

class Ais8_367_22 : public Ais8 {
 public:
  int version;
  int link_id;
  int notice_type;
  int month;
  int day;
  int hour;
  int minute;
  int duration_minutes;
  int spare2;

  vector<Ais8_367_22_SubArea *> sub_areas;

  Ais8_367_22(const char *nmea_payload, const size_t pad);
  ~Ais8_367_22();
};
ostream& operator<< (ostream& o, Ais8_367_22 const& msg);

class Ais9 : public AisMsg {
 public:
  int alt;  // m above sea level
  int sog;
  int position_accuracy;
  AisPoint position;
  float cog;
  int timestamp;
  int alt_sensor;
  int spare;
  int dte;
  int spare2;
  int assigned_mode;
  bool raim;
  int commstate_flag;

  int sync_state;  // In both SOTDMA and ITDMA

  // SOTDMA
  bool slot_timeout_valid;
  int slot_timeout;

  // Based on slot_timeout which ones are valid
  bool received_stations_valid;
  int received_stations;

  bool slot_number_valid;
  int slot_number;

  bool utc_valid;
  int utc_hour;
  int utc_min;
  int utc_spare;

  bool slot_offset_valid;
  int slot_offset;

  // ITDMA
  bool slot_increment_valid;
  int slot_increment;

  bool slots_to_allocate_valid;
  int slots_to_allocate;

  bool keep_flag_valid;
  bool keep_flag;

  Ais9(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais9 &msg);

// 10 ":" - UTC and date inquiry
class Ais10 : public AisMsg {
 public:
  int spare;
  int dest_mmsi;
  int spare2;

  Ais10(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais10 &msg);

// 11 ';' - See 4_11

// 12 - '<' - Addressed safety related text.
class Ais12 : public AisMsg {
 public:
  int seq_num;
  int dest_mmsi;
  bool retransmitted;
  int spare;
  string text;
  int spare2;

  Ais12(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais12 &msg);

// 13 '=' - See 7

// 14 - '>' - Safety broadcast text.
class Ais14 : public AisMsg {
 public:
  int spare;
  string text;
  int spare2;

  Ais14(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais14 &msg);

// 15 - '?' - Interrogation
class Ais15 : public AisMsg {
 public:
  int spare;
  int mmsi_1;
  int msg_1_1;
  int slot_offset_1_1;

  int spare2;
  int dest_msg_1_2;
  int slot_offset_1_2;

  int spare3;
  int mmsi_2;
  int msg_2;
  int slot_offset_2;
  int spare4;

  Ais15(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais15 &msg);

// 16 - '@' - Assigned mode command
class Ais16 : public AisMsg {
 public:
  int spare;
  int dest_mmsi_a;
  int offset_a;
  int inc_a;
  int dest_mmsi_b;
  int offset_b;
  int inc_b;
  int spare2;

  Ais16(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais16 &msg);

// ITU-R M.823  http://www.itu.int/rec/R-REC-M.823/en
// 17 - 'A' - GNSS broacast - TODO(schwehr): only partially coded
class Ais17 : public AisMsg {
 public:
  int spare;
  AisPoint position;
  int spare2;
  int gnss_type;
  int z_cnt;
  int station;
  int seq;
  // N - do not need to store this
  int health;
  // TODO(schwehr): Handle payload

  Ais17(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais17 &msg);


// 18 - 'B' - Class B position report
class Ais18 : public AisMsg {
 public:
  int spare;
  float sog;
  int position_accuracy;
  AisPoint position;  // Long and lat
  float cog;
  int true_heading;
  int timestamp;
  int spare2;
  int unit_flag;  // 0 is SOTDMA, 1 is Carrier Sense (CS).
  int display_flag;
  int dsc_flag;
  int band_flag;
  int m22_flag;
  int mode_flag;
  bool raim;
  int commstate_flag;

  // SOTDMA
  int sync_state;
  bool slot_timeout_valid;
  int slot_timeout;

  // Based on slot_timeout which ones are valid
  bool received_stations_valid;
  int received_stations;

  bool slot_number_valid;
  int slot_number;

  bool utc_valid;
  int utc_hour;
  int utc_min;
  int utc_spare;

  bool slot_offset_valid;
  int slot_offset;

  // ITDMA
  bool slot_increment_valid;
  int slot_increment;

  bool slots_to_allocate_valid;
  int slots_to_allocate;

  bool keep_flag_valid;
  bool keep_flag;

  // If commstate set to 1 for Carrier Sense (CS) devices, there is no
  // state and the commstate region is supposed to be filled with this value:
  //   1100000000000000110
  bool commstate_cs_fill_valid;
  int commstate_cs_fill;

  Ais18(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais18 &msg);

// 19 - 'C' - Class B extended ship and position
class Ais19 : public AisMsg {
 public:
  int spare;
  float sog;
  int position_accuracy;
  AisPoint position;  // Long and lat
  float cog;
  int true_heading;
  int timestamp;
  int spare2;
  string name;
  int type_and_cargo;
  int dim_a;
  int dim_b;
  int dim_c;
  int dim_d;
  int fix_type;
  bool raim;
  int dte;
  int assigned_mode;
  int spare3;

  Ais19(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais19 &msg);

// 20 - 'D' - Data link management
// TODO(schwehr): consider a vector
class Ais20 : public AisMsg {
 public:
  int spare;
  int offset_1;
  int num_slots_1;
  int timeout_1;
  int incr_1;

  bool group_valid_2;
  int offset_2;
  int num_slots_2;
  int timeout_2;
  int incr_2;

  bool group_valid_3;
  int offset_3;
  int num_slots_3;
  int timeout_3;
  int incr_3;

  bool group_valid_4;
  int offset_4;
  int num_slots_4;
  int timeout_4;
  int incr_4;

  int spare2;

  Ais20(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais20 &msg);

// 21 - 'E' - Aids to navigation report
class Ais21 : public AisMsg {
 public:
  int aton_type;
  string name;
  int position_accuracy;
  AisPoint position;
  int dim_a;
  int dim_b;
  int dim_c;
  int dim_d;
  int fix_type;
  int timestamp;
  bool off_pos;
  int aton_status;
  bool raim;
  bool virtual_aton;
  bool assigned_mode;
  int spare;
  // Extended name goes on the end of name
  int spare2;

  Ais21(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais21 &msg);

// 22 - 'F' - Channel Management
class Ais22 : public AisMsg {
 public:
  int spare;
  int chan_a;
  int chan_b;
  int txrx_mode;
  bool power_low;

  // if addressed false, then geographic position
  bool pos_valid;
  AisPoint position1;
  AisPoint position2;

  // if addressed is true
  bool dest_valid;
  int dest_mmsi_1;
  int dest_mmsi_2;

  int chan_a_bandwidth;
  int chan_b_bandwidth;
  int zone_size;

  int spare2;  // Lame that they make a huge spare here.  Bad bad bad

  Ais22(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais22 &msg);

// 23 - 'G' - Group Assignment Command
class Ais23 : public AisMsg {
 public:
  int spare;
  AisPoint position1;
  AisPoint position2;
  int station_type;
  int type_and_cargo;

  int spare2;  // 22 bits of spare here?  what were people thinking?

  int txrx_mode;
  int interval_raw;  // raw value, not sec
  int quiet;
  int spare3;

  Ais23(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais23 &msg);

// 24 - 'H' - Class B Static Data report
class Ais24 : public AisMsg {
 public:
  int part_num;

  // Part A
  string name;

  // Part B
  int type_and_cargo;
  string vendor_id;
  string callsign;
  int dim_a;
  int dim_b;
  int dim_c;
  int dim_d;
  int spare;

  // Part C - Not defined by ITU 1371-5
  // Part D - Not defined by ITU 1371-5

  Ais24(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais24 &msg);

// 25 - 'I' - Single slot binary message - addressed or broadcast
// TODO(schwehr): handle payload
class Ais25 : public AisMsg {
 public:
  bool use_app_id;  // if false, payload is unstructured binary.

  bool dest_mmsi_valid;
  int dest_mmsi;  // only valid if addressed
  // If unstructured:
  // TODO(schwehr): vector<unsigned char> payload;

  int dac;  // valid if use_app_id is true
  int fi;

  Ais25(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais25 &msg);

// 26 - 'J' - Multi slot binary message with comm state
// TODO(schwehr): handle payload
class Ais26 : public AisMsg {
 public:
  bool use_app_id;  // if false, payload is unstructured binary

  bool dest_mmsi_valid;
  int dest_mmsi;  // only valid if addressed

  int dac;  // valid it use_app_id
  int fi;

  // TODO(schwehr): vector<unsigned char> payload;  // If unstructured.  Yuck.

  int commstate_flag;  // 0 - SOTDMA, 1 - TDMA

  // SOTDMA
  int sync_state;
  bool slot_timeout_valid;
  int slot_timeout;

  // Based on slot_timeout which ones are valid
  bool received_stations_valid;
  int received_stations;

  bool slot_number_valid;
  int slot_number;

  bool utc_valid;
  int utc_hour;
  int utc_min;
  int utc_spare;

  bool slot_offset_valid;
  int slot_offset;

  // ITDMA
  bool slot_increment_valid;
  int slot_increment;

  bool slots_to_allocate_valid;
  int slots_to_allocate;

  bool keep_flag_valid;
  bool keep_flag;

  Ais26(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais26 &msg);

// 27 - 'K' - Long-range position report - e.g. for satellite receivers
class Ais27 : public AisMsg {
 public:
  int position_accuracy;
  bool raim;
  int nav_status;
  AisPoint position;
  int sog;  // Knots.
  int cog;  // Degrees.
  bool gnss;  // warning: bits in AIS are flipped sense
  int spare;

  Ais27(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais27 &msg);

}  // namespace libais

#endif  // LIBAIS_AIS_H_
