///|
/// Parse a Range field value using default resource limits.
pub fn parse_range(input : String) -> Result[RangeRequest, RangeError] {
  parse_range_with_limits(input, Limits::default())
}

///|
/// Strict RFC 9110-oriented Range parser. It preserves byte-spec order.
pub fn parse_range_with_limits(
  input : String,
  limits : Limits,
) -> Result[RangeRequest, RangeError] {
  if utf8_length(input) > limits.max_input_bytes() {
    return Err(
      range_error(
        Limit,
        LimitExceeded,
        0,
        "Range field exceeds max_input_bytes",
      ),
    )
  }
  if input.length() == 0 {
    return Err(range_error(Input, EmptyInput, 0, "Range field is empty"))
  }
  for i = 0; i < input.length(); i = i + 1 {
    let code = input[i].to_int()
    if (code < 0x20 && !is_ows(code.unsafe_to_char())) || code == 0x7F {
      return Err(
        range_error(
          Input,
          UnexpectedCharacter,
          i,
          "control character in Range field",
        ),
      )
    }
  }
  let equals = find_char(input, '=')
  if equals < 0 {
    return Err(
      range_error(
        Syntax,
        MissingEquals,
        input.length(),
        "Range field has no '='",
      ),
    )
  }
  let unit_text = trim_ows(input[:equals].to_owned())
  if unit_text.length() == 0 {
    return Err(range_error(Unit, InvalidUnit, 0, "range-unit is empty"))
  }
  if utf8_length(unit_text) > limits.max_unit_bytes() {
    return Err(
      range_error(Limit, LimitExceeded, 0, "range-unit exceeds max_unit_bytes"),
    )
  }
  for i = 0; i < unit_text.length(); i = i + 1 {
    if !is_tchar(unit_text[i].to_int().unsafe_to_char()) {
      return Err(
        range_error(Unit, InvalidUnit, i, "invalid range-unit character"),
      )
    }
  }
  let raw_set = trim_ows(input[equals + 1:].to_owned())
  if raw_set.length() == 0 {
    return Err(
      range_error(RangeSpec, EmptyRangeSet, equals + 1, "range-set is empty"),
    )
  }
  if !ascii_equal(unit_text, "bytes") {
    if utf8_length(raw_set) > limits.max_raw_other_range_bytes() {
      return Err(
        range_error(
          Limit,
          LimitExceeded,
          equals + 1,
          "unknown range-set exceeds limit",
        ),
      )
    }
    return Ok(range_request(Other(unit_text), [], raw_other_range_set=raw_set))
  }
  let specs = parse_byte_range_set(raw_set, equals + 1, limits) catch {
    error => return Err(unwrap_range_error(error))
  }
  Ok(range_request(Bytes, specs))
}

///|
fn parse_byte_range_set(
  input : String,
  base_offset : Int,
  limits : Limits,
) -> Array[ByteRangeSpec] raise RangeError {
  let specs : Array[ByteRangeSpec] = []
  let mut start = 0
  for i = 0; i <= input.length(); i = i + 1 {
    if i == input.length() || input[i] == ','.to_int().to_uint16() {
      let raw = input[start:i].to_owned()
      let part = trim_ows(raw)
      if part.length() == 0 {
        raise range_error(
          RangeSpec,
          InvalidRange,
          base_offset + start,
          "empty byte-range member",
        )
      }
      if specs.length() >= limits.max_ranges() {
        raise range_error(
          Limit,
          LimitExceeded,
          base_offset + start,
          "byte range count exceeds limit",
        )
      }
      specs.push(parse_byte_range_spec(part, base_offset + start, limits))
      start = i + 1
    }
  }
  specs
}

///|
fn parse_byte_range_spec(
  input : String,
  base_offset : Int,
  limits : Limits,
) -> ByteRangeSpec raise RangeError {
  let dash = find_char(input, '-')
  if dash < 0 {
    raise range_error(
      RangeSpec,
      InvalidRange,
      base_offset,
      "byte-range member has no '-'",
    )
  }
  if find_char_from(input, '-', dash + 1) >= 0 {
    raise range_error(
      RangeSpec,
      InvalidRange,
      base_offset + dash + 1,
      "byte-range member has multiple '-' characters",
    )
  }
  let left = trim_ows(input[:dash].to_owned())
  let right = trim_ows(input[dash + 1:].to_owned())
  if left.length() == 0 {
    let length = unwrap_decimal(
      parse_decimal_int64_at(right, base_offset + dash + 1, limits),
    )
    return Suffix(length)
  }
  let first = unwrap_decimal(parse_decimal_int64_at(left, base_offset, limits))
  if right.length() == 0 {
    return OpenEnded(first)
  }
  let last = unwrap_decimal(
    parse_decimal_int64_at(right, base_offset + dash + 1, limits),
  )
  if last < first {
    raise range_error(
      RangeSpec,
      LastBeforeFirst,
      base_offset + dash + 1,
      "last-byte-pos is before first-byte-pos",
    )
  }
  Closed(first, last)
}

///|
fn find_char(input : String, target : Char) -> Int {
  find_char_from(input, target, 0)
}

///|
fn find_char_from(input : String, target : Char, start : Int) -> Int {
  let target_code = target.to_int().to_uint16()
  for i = start; i < input.length(); i = i + 1 {
    if input[i] == target_code {
      return i
    }
  }
  -1
}

///|
fn unwrap_decimal(result : Result[Int64, RangeError]) -> Int64 raise RangeError {
  match result {
    Ok(value) => value
    Err(error) => raise error
  }
}