// Low level *structure* of a symbol: row indicators, patterns and geometry.
//
// This module deliberately stops before bar and space widths.  It provides
// what a renderer needs to place codewords and what a renderer test needs to
// check geometry, with the start pattern, the stop pattern and the row
// indicator formulas of ISO/IEC 15438 annex Q expressed as computation
// instead of a 928 by 3 data table.

///|
/// Start pattern, 17 modules, most significant bit first (0 = space).
pub let start_pattern : Int = 0x1fea8

///|
/// Stop pattern, 18 modules, most significant bit first.
pub let stop_pattern : Int = 0x3fa29

///|
/// Width of the start pattern in modules.
pub let start_pattern_modules : Int = 17

///|
/// Width of the stop pattern in modules.
pub let stop_pattern_modules : Int = 18

///|
/// Number of data codewords in one row: one left indicator, `columns` data
/// codewords and one right indicator.
pub fn row_codeword_count(columns : Int) -> Int {
  columns + 2
}

///|
/// Total module width of a symbol with `columns` columns.
///
/// One row is the start pattern (17 modules), the left row indicator, one
/// symbol character per data column, the right row indicator (17 modules each)
/// and the stop pattern (18 modules):
///
///     17 + 17 * (columns + 2) + 18 = 69 + 17 * columns
pub fn symbol_modules(columns : Int) -> Int {
  69 + 17 * columns
}

///|
/// Bar and space cluster of row `row`, i.e. `row mod 3`.
pub fn row_cluster(row : Int) -> Int {
  row % 3
}

///|
/// Left row indicator codeword for a row, from annex Q.
///
/// The three clusters carry different information: cluster 0 puts the row
/// count in the low position, cluster 1 the error correction level and the
/// row count modulo three, cluster 2 the column count.
pub fn left_row_indicator(
  row : Int,
  rows : Int,
  columns : Int,
  ec_level : Int,
) -> Int {
  match row_cluster(row) {
    0 => 30 * (row / 3) + (rows - 1) / 3
    1 => 30 * (row / 3) + ec_level * 3 + (rows - 1) % 3
    _ => 30 * (row / 3) + (columns - 1)
  }
}

///|
/// Right row indicator codeword for a row, from annex Q.
pub fn right_row_indicator(
  row : Int,
  rows : Int,
  columns : Int,
  ec_level : Int,
) -> Int {
  match row_cluster(row) {
    0 => 30 * (row / 3) + (columns - 1)
    1 => 30 * (row / 3) + (rows - 1) / 3
    _ => 30 * (row / 3) + ec_level * 3 + (rows - 1) % 3
  }
}

///|
/// Codewords of one row in stored order: left indicator, data codewords,
/// right indicator.
pub fn row_codewords(
  codewords : Array[Int],
  row : Int,
  columns : Int,
  rows : Int,
  ec_level : Int,
) -> Array[Int] {
  let result : Array[Int] = [left_row_indicator(row, rows, columns, ec_level)]
  let base = row * columns
  for offset in 0.. Array[Array[Int]]? {
  if symbol.codewords.length() != symbol.columns * symbol.rows {
    return None
  }
  let rows : Array[Array[Int]] = []
  for row in 0.. Array[Int] {
  let widths : Array[Int] = []
  if modules <= 0 {
    return widths
  }
  let mut mask = 1 << (modules - 1)
  let mut last = (pattern & mask) != 0
  let mut width = 0
  for _ in 0..> 1
  }
  widths.push(width)
  widths
}

///|
/// `true` if a pattern describes exactly `modules` modules and alternates
/// between bars and spaces with every width in 1..8.  PDF417 patterns are 17
/// modules wide except the stop pattern, which is 18 modules and starts with
/// an eight module bar (the start pattern).
pub fn is_valid_pattern(pattern : Int, modules : Int) -> Bool {
  let widths = pattern_widths(pattern, modules)
  if widths.length() == 0 {
    return false
  }
  let mut total = 0
  for width in widths {
    if width < 1 || width > 8 {
      return false
    }
    total += width
  }
  total == modules
}

///|
/// Geometry of a symbol in modules.
pub(all) struct Geometry {
  width_modules : Int
  height_rows : Int
  data_columns : Int
  data_rows : Int
  ec_level : Int
}

///|
/// Geometry of a finalized symbol.
pub fn geometry_of(symbol : Symbol) -> Geometry {
  {
    width_modules: symbol_modules(symbol.columns),
    height_rows: symbol.rows,
    data_columns: symbol.columns,
    data_rows: symbol.rows,
    ec_level: symbol.ec_level,
  }
}

///|
/// Total codeword capacity of a `columns` by `rows` data area, including the
/// row indicators of every row.
pub fn symbol_codeword_capacity(columns : Int, rows : Int) -> Int {
  if columns < min_columns ||
    columns > max_columns ||
    rows < min_rows ||
    rows > max_rows {
    return 0
  }
  columns * rows
}

///|
/// Smallest legal symbol that holds `codewords` codewords, searching column
/// counts from one to thirty and preferring the geometry closest to the
/// standard's 3:1 ratio.
pub fn smallest_symbol(codewords : Int, ec_level : Int) -> (Int, Int)? {
  if codewords <= 0 {
    return None
  }
  if ec_level < 0 || ec_level > max_error_correction_level {
    return None
  }
  let ec_count = error_correction_codeword_count(ec_level)
  let mut best : (Int, Int)? = None
  let mut best_area = 0
  let mut columns = min_columns
  while columns <= max_columns {
    let rows = rows_for(codewords, ec_count, columns)
    if rows >= min_rows && rows <= max_rows {
      let area = columns * rows
      let take = match best {
        None => true
        Some(_) => area < best_area
      }
      if take {
        best = Some((columns, rows))
        best_area = area
      }
    }
    columns += 1
  }
  best
}