// Public types, shared constants and encoding errors.
//
// Everything in this module works on *codewords*: integers in the range
// 0..928, the alphabet PDF417 uses before the symbols are turned into bars
// and spaces.  The library deliberately stops at that boundary; start/stop
// patterns, row indicators and graphics belong to a renderer.

///|
/// Compaction (high level encoding) mode.
///
/// `Text` uses the packed Text Compaction sub-modes, `Numeric` the base 900
/// decimal compaction, `Byte` the raw byte compaction and `Auto` lets the
/// encoder pick a mode per segment.
pub(all) enum Compaction {
  Text
  Numeric
  Byte
  Auto
}

///|
pub impl Eq for Compaction with fn equal(self : Compaction, other : Compaction) -> Bool {
  match (self, other) {
    (Text, Text) => true
    (Numeric, Numeric) => true
    (Byte, Byte) => true
    (Auto, Auto) => true
    _ => false
  }
}

///|
/// Keep `Eq` usable by callers without promoting its methods.
pub extend Compaction with Eq::{not_equal, equal}

///|
/// Text Compaction sub-mode, as defined by ISO/IEC 15438 4.4.2.
pub(all) enum TextSubmode {
  Alpha
  Lower
  Mixed
  Punctuation
}

///|
pub impl Eq for TextSubmode with fn equal(
  self : TextSubmode,
  other : TextSubmode,
) -> Bool {
  match (self, other) {
    (Alpha, Alpha) => true
    (Lower, Lower) => true
    (Mixed, Mixed) => true
    (Punctuation, Punctuation) => true
    _ => false
  }
}

///|
pub extend TextSubmode with Eq::{not_equal, equal}

///|
/// Debug rendering of a text sub-mode, used by tests and diagnostics.
pub fn TextSubmode::name(self : TextSubmode) -> String {
  match self {
    Alpha => "Alpha"
    Lower => "Lower"
    Mixed => "Mixed"
    Punctuation => "Punctuation"
  }
}

///|
/// A complete PDF417 codeword sequence with the layout that produced it.
///
/// `codewords` is the *full* sequence that a low level encoder consumes:
/// the symbol length descriptor, the high level data codewords, the pad
/// codewords and finally the error correction codewords.
pub(all) struct Symbol {
  codewords : Array[Int]
  columns : Int
  rows : Int
  ec_level : Int
  data_count : Int
  pad_count : Int
}

///|
/// Everything that can go wrong while encoding.
///
/// `EncodeError` is a MoonBit error type, so it can be raised with `raise`
/// and caught with `catch`.  Every fallible entry point also has an `_opt`
/// companion that returns `Option` instead.
pub suberror EncodeError {
  /// The input contained no encodable character.
  EmptyInput
  /// A character cannot be represented in the requested compaction mode.
  UnrepresentableChar(Int)
  /// The data plus padding and error correction does not fit into 928
  /// codewords.
  TooManyCodewords(Int)
  /// The error correction level was outside 0..8.
  InvalidErrorCorrectionLevel(Int)
  /// The requested number of columns was outside 1..30.
  InvalidColumnCount(Int)
  /// No row count in 3..90 can hold the requested codewords for this
  /// column count.
  LayoutImpossible(Int, Int)
  /// The caller asked for more corrections than Reed-Solomon can guarantee
  /// for this codeword count.
  TooManyErasures(Int, Int)
} derive(Debug)

///|
/// Keep the derived `Debug` implementation available through `to_repr`
/// without promoting it to a regular method (which the compiler deprecates).
pub extend EncodeError with @debug.Debug::{to_repr}

///|
pub let max_codewords : Int = 928

///|
pub let max_columns : Int = 30

///|
pub let min_columns : Int = 1

///|
pub let min_rows : Int = 3

///|
pub let max_rows : Int = 90

///|
pub let max_error_correction_level : Int = 8

///|
/// Number of Reed-Solomon parity codewords for an error correction level.
/// ISO/IEC 15438 defines this as `2^(level + 1)`.
pub fn error_correction_codeword_count(level : Int) -> Int {
  1 << (level + 1)
}

///|
/// Mode latch to Text Compaction.
pub let latch_text : Int = 900

///|
/// Mode latch to Byte Compaction with an explicit length count.
pub let latch_byte_padded : Int = 901

///|
/// Mode latch to Numeric Compaction.
pub let latch_numeric : Int = 902

///|
/// Mode shift to Byte Compaction for a single byte.
pub let shift_byte : Int = 913

///|
/// Mode latch to Byte Compaction for multiples of six bytes.
pub let latch_byte : Int = 924

///|
/// Pad codeword used to fill a symbol up to `rows * columns`.
pub let pad_codeword : Int = 900

///|
/// Highest byte value that byte compaction can carry.
pub let max_byte_value : Int = 255