///|
pub(all) struct FieldCursor {
  fields_value : Array[String]
  record_index_value : Int
} derive(Debug)

///|
fn field_error(
  code : NmeaErrorCode,
  index : Int,
  message : String,
) -> NmeaError {
  NmeaError::from_diagnostic(
    Diagnostic::new(code, Error, message, SourceRef::field(0, index)),
  )
}

///|
pub fn FieldCursor::new(
  fields : Array[String],
  record_index? : Int = 0,
) -> FieldCursor {
  { fields_value: fields.copy(), record_index_value: record_index }
}

///|
pub fn FieldCursor::length(self : FieldCursor) -> Int {
  self.fields_value.length()
}

///|
pub fn FieldCursor::record_index(self : FieldCursor) -> Int {
  self.record_index_value
}

///|
fn FieldCursor::at(
  self : FieldCursor,
  index : Int,
) -> Result[String, NmeaError] {
  if index < 0 || index >= self.fields_value.length() {
    return Err(field_error(FieldMissing, index, "required field is missing"))
  }
  Ok(self.fields_value[index])
}

///|
pub fn FieldCursor::required(
  self : FieldCursor,
  index : Int,
) -> Result[String, NmeaError] {
  let value = match self.at(index) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  if value.length() == 0 {
    return Err(field_error(FieldEmpty, index, "required field is empty"))
  }
  Ok(value)
}

///|
pub fn FieldCursor::optional(
  self : FieldCursor,
  index : Int,
) -> Result[String?, NmeaError] {
  if index < 0 || index >= self.fields_value.length() {
    return Ok(None)
  }
  let value = self.fields_value[index]
  if value.length() == 0 {
    Ok(None)
  } else {
    Ok(Some(value))
  }
}

///|
fn parse_integer_text(value : String, index : Int) -> Result[Int, NmeaError] {
  if value.length() == 0 {
    return Err(field_error(FieldEmpty, index, "integer field is empty"))
  }
  let mut cursor = 0
  let mut negative = false
  if value[0].to_int() == 45 {
    negative = true
    cursor = 1
  } else if value[0].to_int() == 43 {
    cursor = 1
  }
  if cursor >= value.length() {
    return Err(field_error(FieldInvalidInteger, index, "integer has no digits"))
  }
  let mut result = 0
  while cursor < value.length() {
    let code = value[cursor].to_int()
    if code < 48 || code > 57 {
      return Err(
        field_error(FieldInvalidInteger, index, "integer contains a non-digit"),
      )
    }
    result = result * 10 + code - 48
    cursor = cursor + 1
  }
  Ok(if negative { -result } else { result })
}

///|
pub fn FieldCursor::integer(
  self : FieldCursor,
  index : Int,
  min? : Int = -2147483648,
  max? : Int = 2147483647,
) -> Result[Int, NmeaError] {
  let value = match self.required(index) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let result = match parse_integer_text(value, index) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  if result < min {
    return Err(
      field_error(
        FieldInvalidInteger,
        index,
        "integer is below the lower bound",
      ),
    )
  }
  if result > max {
    return Err(
      field_error(FieldInvalidInteger, index, "integer exceeds the upper bound"),
    )
  }
  Ok(result)
}

///|
pub fn FieldCursor::decimal(
  self : FieldCursor,
  index : Int,
  max_scale? : Int = 9,
) -> Result[ExactDecimal, NmeaError] {
  let value = match self.required(index) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  match ExactDecimal::parse(value, max_scale~) {
    Ok(result) => Ok(result)
    Err(_) =>
      Err(
        field_error(FieldInvalidDecimal, index, "field is not a valid decimal"),
      )
  }
}