// Terminal output (upstream `typst-cli/src/terminal.rs`): optionally
// colored output on stderr.

///|
/// The color choice of the command line (set once arguments are parsed).
let color_choice : Ref[ColorChoice] = { val: Auto, }

///|
/// Whether terminal output is colored (`TermOutInner::new`).
fn use_color() -> Bool {
  match color_choice.val {
    Auto if @platform.stderr_is_terminal() => @kit.env_allows_color()
    Always => true
    _ => false
  }
}

///|
/// Returns a handle to the optionally colored terminal output (stderr).
/// Its contents are written with [`flush_terminal`].
fn terminal_out() -> @kit.ColorWriter {
  @kit.ColorWriter::new(use_color())
}

///|
/// Writes buffered terminal output to stderr.
fn flush_terminal(out : @kit.ColorWriter) -> Unit {
  @platform.print_stderr(out.take())
  @platform.flush_stderr()
}

///|
/// Clears the entire screen.
fn clear_screen(out : @kit.ColorWriter) -> Unit {
  // We don't want to clear anything that is not a TTY.
  if out.supports_color() {
    // Clear the screen and then move the cursor to the top left corner.
    out.write("\u{1B}[2J\u{1B}[1;1H")
  }
}

///|
/// The style of an error header (`Styles::header_error`).
fn header_error_style() -> @kit.ColorSpec {
  @kit.ColorSpec::new().with_bold().with_intense().with_fg(Red)
}

///|
/// The style of a warning header (`Styles::header_warning`).
fn header_warning_style() -> @kit.ColorSpec {
  @kit.ColorSpec::new().with_bold().with_intense().with_fg(Yellow)
}

///|
/// The style of a note header (`Styles::header_note`).
fn header_note_style() -> @kit.ColorSpec {
  @kit.ColorSpec::new().with_bold().with_intense().with_fg(Green)
}

///|
/// The style of a hint header (`Styles::header_help`).
fn header_help_style() -> @kit.ColorSpec {
  @kit.ColorSpec::new().with_bold().with_intense().with_fg(Cyan)
}

///|
/// Print an application-level error (independent from a source file).
fn print_error(msg : String) -> Unit {
  let output = terminal_out()
  output.set_color(header_error_style())
  output.write("error")
  output.reset()
  output.writeln(": \{msg}")
  flush_terminal(output)
}

///|
/// Print an application-level hint (independent from a source file).
fn print_hint(msg : String) -> Unit {
  let output = terminal_out()
  output.set_color(header_help_style())
  output.write("hint")
  output.reset()
  output.writeln(": \{msg}")
  flush_terminal(output)
}

///|
/// Aborts like a Rust panic: prints the message to stderr and exits with
/// code 101.
fn panic_exit(message : String) -> Unit {
  @platform.print_stderr(
    "\nthread 'main' panicked:\n\{message}\nnote: run with `RUST_BACKTRACE=1` environment variable to display a backtrace\n",
  )
  @platform.exit(101)
}

///|
/// Prints text to stdout like Rust's `print!`, which panics if writing
/// fails (writing to a closed stdout succeeds).
fn print_out(text : String) -> Unit {
  @platform.print_stdout(text) catch {
    err => panic_exit("failed printing to stdout: \{err}")
  }
}

///|
/// Prints text to stdout ignoring errors (like clap's help output).
fn print_out_lossy(text : String) -> Unit {
  @platform.print_stdout(text) catch {
    _ => ()
  }
}