// A streaming pretty-printer.
//
// Ported from wax/src/lib-utils/printer.ml. The imperative builder API
// (string, space, box, ...) emits a FLAT TOKEN STREAM straight into a layout
// engine; nothing is ever materialised as a document tree. That is what gives
// genuine hard breaks, column awareness and break coalescing under our own
// control, with bounded memory -- the engine buffers only the ~width of
// lookahead a break decision actually needs.
//
// Ported rather than replaced by moonbit-community/prettyprinter: byte-exact
// output is the whole point here, and an engine with its own opinions about
// grouping and fitting would drift from the reference in ways no amount of
// tuning would close.

///|
/// How strongly a break wants to end the line.
///
/// Cut and Space are SOFT: they flatten inside a group that fits. Newline and
/// BlankLine always break. Two adjacent breaks coalesce into the stronger.
pub(all) enum BreakStrength {
  Cut
  Space
  Newline
  BlankLine
} derive(Eq, Debug)

///|
pub fn BreakStrength::strength(self : BreakStrength) -> Int {
  match self {
    Cut => 0
    Space => 1
    Newline => 2
    BlankLine => 3
  }
}

///|
/// How a group lays its soft breaks out.
pub(all) enum GKind {
  /// Fill: each soft break wraps independently, as needed.
  GBox
  /// Same fill semantics; kept distinct for parity with OCaml's Format.
  GHov
  /// All-or-nothing: the whole group flat, or every soft break wraps.
  GHv
  /// Every soft break wraps.
  GV
  /// Soft breaks never wrap; hard ones still do.
  GH
} derive(Eq, Debug)

///|
/// One token of the flat stream the builder feeds the engine.
///
/// A group, nest or if-broken opens with its own token and closes with a
/// matching `TEnd`.
pub(all) enum Token {
  /// Display width, then the payload. The width is carried rather than
  /// recomputed because it is not the string's length: see unicode/.
  TText(Int, String)
  TBreak(BreakStrength)
  TBegin(GKind)
  TNest(Int)
  /// Content emitted only when the enclosing group is laid out BROKEN -- the
  /// trailing comma after the last element of a list that wraps. It never
  /// counts toward the fit decision.
  TIfBroken
  TEnd
} derive(Eq, Debug)

///|
/// A live layout mode.
pub(all) enum Mode {
  Flat
  Brkm
  Fill
} derive(Eq, Debug)

///|
/// A live layout frame: where breaks indent to, and how they behave.
struct Frame {
  findent : Int
  fmode : Mode
} derive(Eq, Debug)

///|
/// The outcome of a fit scan.
///
/// `Susp` means the scan ran out of buffered input and parked its state, to be
/// resumed on the next fed token -- so each buffered token is scanned once
/// rather than re-scanned from scratch.
enum ScanResult {
  Fits
  Nofit
  Susp
} derive(Eq, Debug)