///|
pub(all) struct GllRecord {
  talker_value : String
  latitude_value : Coordinate
  longitude_value : Coordinate
  time_value : UtcTime
  status_value : RmcStatus
  mode_value : String?
} derive(Eq, Debug)

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

///|
pub fn parse_gll(frame : NmeaFrame) -> Result[GllRecord, NmeaError] {
  if frame.formatter() != "GLL" {
    return Err(gll_error("frame formatter is not GLL"))
  }
  let cursor = FieldCursor::new(frame.fields())
  let latitude_text = match cursor.required(0) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let latitude_hemi = match cursor.required(1) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let longitude_text = match cursor.required(2) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let longitude_hemi = match cursor.required(3) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let time_text = match cursor.required(4) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let status_text = match cursor.required(5) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let status = if status_text == "A" {
    Active
  } else if status_text == "V" {
    Void
  } else {
    return Err(gll_error("GLL status must be A or V"))
  }
  let latitude = match
    parse_latitude(latitude_text, latitude_hemi, SourceRef::field(0, 0)) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let longitude = match
    parse_longitude(longitude_text, longitude_hemi, SourceRef::field(0, 2)) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let time = match UtcTime::parse(time_text, SourceRef::field(0, 4)) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let mode = match cursor.optional(6) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  Ok({
    talker_value: frame.talker(),
    latitude_value: latitude,
    longitude_value: longitude,
    time_value: time,
    status_value: status,
    mode_value: mode,
  })
}

///|
pub fn GllRecord::latitude(self : GllRecord) -> Coordinate {
  self.latitude_value
}

///|
pub fn GllRecord::longitude(self : GllRecord) -> Coordinate {
  self.longitude_value
}

///|
pub fn GllRecord::time(self : GllRecord) -> UtcTime {
  self.time_value
}

///|
pub fn GllRecord::status(self : GllRecord) -> RmcStatus {
  self.status_value
}

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

///|
pub fn GllRecord::encode(self : GllRecord) -> Result[String, NmeaError] {
  let fields = [
    coordinate_wire_text(self.latitude_value),
    if self.latitude_value.hemisphere_value == 78 {
      "N"
    } else {
      "S"
    },
    coordinate_wire_text(self.longitude_value),
    if self.longitude_value.hemisphere_value == 69 {
      "E"
    } else {
      "W"
    },
    utc_time_wire(self.time_value),
    match self.status_value {
      Active => "A"
      Void => "V"
    },
  ]
  match self.mode_value {
    Some(mode) => fields.push(mode)
    None => ()
  }
  Ok(NmeaFrame::new(36, self.talker_value, "GLL", fields).encode())
}