///|
pub(all) struct VtgRecord {
  talker_value : String
  true_course_value : ExactDecimal?
  magnetic_course_value : ExactDecimal?
  speed_knots_value : ExactDecimal?
  speed_kph_value : ExactDecimal?
  mode_value : String?
} derive(Eq, Debug)

///|
fn vtg_error(message : String) -> NmeaError {
  NmeaError::from_diagnostic(
    Diagnostic::new(SentenceInvalid, Error, message, SourceRef::sentence(0)),
  )
}

///|
fn vtg_decimal(
  cursor : FieldCursor,
  index : Int,
) -> Result[ExactDecimal?, NmeaError] {
  match cursor.optional(index) {
    Ok(None) => Ok(None)
    Ok(Some(_)) =>
      match cursor.decimal(index) {
        Ok(value) => Ok(Some(value))
        Err(error) => Err(error)
      }
    Err(error) => Err(error)
  }
}

///|
pub fn parse_vtg(frame : NmeaFrame) -> Result[VtgRecord, NmeaError] {
  if frame.formatter() != "VTG" {
    return Err(vtg_error("frame formatter is not VTG"))
  }
  let cursor = FieldCursor::new(frame.fields())
  let true_course = match vtg_decimal(cursor, 0) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let true_marker = match cursor.optional(1) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let magnetic_course = match vtg_decimal(cursor, 2) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let magnetic_marker = match cursor.optional(3) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let speed_knots = match vtg_decimal(cursor, 4) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let knot_marker = match cursor.optional(5) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let speed_kph = match vtg_decimal(cursor, 6) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let kph_marker = match cursor.optional(7) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  if (true_course is Some(_) && true_marker != Some("T")) ||
    (magnetic_course is Some(_) && magnetic_marker != Some("M")) ||
    (speed_knots is Some(_) && knot_marker != Some("N")) ||
    (speed_kph is Some(_) && kph_marker != Some("K")) {
    return Err(vtg_error("VTG value has an invalid unit marker"))
  }
  let mode = match cursor.optional(8) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  Ok({
    talker_value: frame.talker(),
    true_course_value: true_course,
    magnetic_course_value: magnetic_course,
    speed_knots_value: speed_knots,
    speed_kph_value: speed_kph,
    mode_value: mode,
  })
}

///|
pub fn VtgRecord::true_course(self : VtgRecord) -> ExactDecimal? {
  self.true_course_value
}

///|
pub fn VtgRecord::magnetic_course(self : VtgRecord) -> ExactDecimal? {
  self.magnetic_course_value
}

///|
pub fn VtgRecord::speed_knots(self : VtgRecord) -> ExactDecimal? {
  self.speed_knots_value
}

///|
pub fn VtgRecord::speed_kph(self : VtgRecord) -> ExactDecimal? {
  self.speed_kph_value
}

///|
pub fn VtgRecord::mode(self : VtgRecord) -> String? {
  self.mode_value
}

///|
fn vtg_decimal_text(value : ExactDecimal?) -> String {
  match value {
    Some(decimal) => decimal.to_string()
    None => ""
  }
}

///|
pub fn VtgRecord::encode(self : VtgRecord) -> Result[String, NmeaError] {
  let fields = [
    vtg_decimal_text(self.true_course_value),
    "T",
    vtg_decimal_text(self.magnetic_course_value),
    "M",
    vtg_decimal_text(self.speed_knots_value),
    "N",
    vtg_decimal_text(self.speed_kph_value),
    "K",
  ]
  match self.mode_value {
    Some(mode) => fields.push(mode)
    None => ()
  }
  Ok(NmeaFrame::new(36, self.talker_value, "VTG", fields).encode())
}