///|
pub(all) struct Limits {
  max_bytes : Int
  max_records : Int
  max_fields : Int
  max_cells : Int
} derive(Eq, Debug)

///|
pub fn default_limits() -> Limits {
  {
    max_bytes: 268435456,
    max_records: 1000000,
    max_fields: 2046,
    max_cells: 10000000,
  }
}

///|
fn check_limits(limits : Limits) -> Unit raise DbfError {
  if limits.max_bytes < 33 ||
    limits.max_records < 0 ||
    limits.max_fields < 0 ||
    limits.max_cells < 0 {
    raise Invalid(0, "invalid resource limits")
  }
}

///|
pub(all) struct UpdateDate {
  year : Int
  month : Int
  day : Int
} derive(Eq, Debug)

///|
fn check_update_date(date : UpdateDate) -> Unit raise DbfError {
  if date.year < 1900 || date.year > 2155 || date.month < 1 || date.month > 12 {
    raise Invalid(1, "update date outside dBASE range")
  }
  let days = match date.month {
    2 =>
      if date.year % 4 == 0 && (date.year % 100 != 0 || date.year % 400 == 0) {
        29
      } else {
        28
      }
    4 | 6 | 9 | 11 => 30
    _ => 31
  }
  if date.day < 1 || date.day > days {
    raise Invalid(3, "invalid update date day")
  }
}

///|
/// Checks values using the intended output encoding, including encoded byte widths.
pub fn validate_table_encoded(
  table : Table,
  encoding : Encoding,
) -> Unit raise DbfError {
  ignore(validate_fields(table.fields))
  for i, row in table.rows {
    if row.values.length() != table.fields.length() {
      raise Invalid(i, "row has wrong number of values")
    }
    for j, value in row.values {
      ignore(value_bytes(value, table.fields[j], encoding, i))
    }
  }
}

///|
/// Returns a bounded range of physical rows, including deleted records.
pub fn Reader::rows_range(
  self : Reader,
  start : Int,
  count : Int,
) -> Array[Row] raise DbfError {
  if start < 0 || count < 0 || start > self.count || count > self.count - start {
    raise Invalid(start, "record range out of bounds")
  }
  let rows : Array[Row] = []
  for i = start; i < start + count; i = i + 1 {
    rows.push(self.row_at(i))
  }
  rows
}