///|
/// Colors baked into the markdown rich-text projection.
///
/// The projection is shared by the editor, the session snapshot export, and
/// the markdown preview surfaces. Colors are decided at conversion time and
/// stored on runs/blocks, so every role the converter paints has to be
/// represented here rather than resolved later from the editor foreground.
///
/// `light()` reproduces the historical colors exactly, which keeps existing
/// callers (and their golden tests) unchanged. Dark surfaces pass a palette
/// built from their theme; `dark()` is a readable default for callers that do
/// not have one.
#derive(Eq, Debug, ToJson)
pub(all) struct MarkdownRichTextPalette {
  /// Link and autolink text.
  link : @core.Color
  /// Inline code (`code`) background.
  inline_code_background : @core.Color
  /// Image placeholder background.
  image_background : @core.Color
  /// Footnote reference (`[^x]`) background.
  footnote_reference_background : @core.Color
  /// Raw inline HTML background.
  html_inline_background : @core.Color
  /// Highlight (`==text==`) background.
  highlight_background : @core.Color
  /// Blockquote text, background, and left border.
  quote_text : @core.Color
  quote_background : @core.Color
  quote_border : @core.Color
  /// Code block background and language label text.
  code_background : @core.Color
  code_language_text : @core.Color
  /// Table block background, border, and header background.
  table_background : @core.Color
  table_border : @core.Color
  table_header_background : @core.Color
  /// Footnote definition text, label, label background, and left border.
  footnote_text : @core.Color
  footnote_label_text : @core.Color
  footnote_label_background : @core.Color
  footnote_border : @core.Color
  /// Front matter background and text.
  front_matter_background : @core.Color
  front_matter_text : @core.Color
  /// Raw HTML block background and text.
  html_background : @core.Color
  html_text : @core.Color
  /// Code syntax token colors.
  syntax_keyword : @core.Color
  syntax_string : @core.Color
  syntax_comment : @core.Color
  syntax_number : @core.Color
  syntax_function : @core.Color
  syntax_type : @core.Color
  syntax_operator : @core.Color
  syntax_annotation : @core.Color
  syntax_escape : @core.Color
}

///|
pub extend MarkdownRichTextPalette with Debug::{to_repr}

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

///|
pub extend MarkdownRichTextPalette with ToJson::{to_json}

///|
/// The historical light palette. Values match the colors that were previously
/// hard-coded in `markdown_model_rich_text.mbt` and `code_highlight.mbt`.
pub fn MarkdownRichTextPalette::light() -> MarkdownRichTextPalette {
  {
    link: @core.Color::rgba(r=0.13, g=0.45, b=0.94, a=1.0),
    inline_code_background: @core.Color::black().multiply_alpha(0.08),
    image_background: @core.Color::rgba(r=0.18, g=0.4, b=0.7, a=0.08),
    footnote_reference_background: @core.Color::rgba(
      r=0.18,
      g=0.4,
      b=0.7,
      a=0.08,
    ),
    html_inline_background: @core.Color::black().multiply_alpha(0.06),
    highlight_background: @core.Color::rgba(r=1.0, g=0.83, b=0.2, a=0.4),
    quote_text: @core.Color::black().multiply_alpha(0.82),
    quote_background: @core.Color::rgba(r=0.12, g=0.13, b=0.15, a=0.05),
    quote_border: @core.Color::black().multiply_alpha(0.32),
    code_background: @core.Color::rgba(r=0.12, g=0.13, b=0.15, a=0.06),
    code_language_text: @core.Color::black().multiply_alpha(0.56),
    table_background: @core.Color::rgba(r=0.08, g=0.12, b=0.16, a=0.05),
    table_border: @core.Color::black().multiply_alpha(0.16),
    table_header_background: @core.Color::rgba(r=0.08, g=0.12, b=0.16, a=0.06),
    footnote_text: @core.Color::black().multiply_alpha(0.82),
    footnote_label_text: @core.Color::black().multiply_alpha(0.58),
    footnote_label_background: @core.Color::rgba(r=0.18, g=0.4, b=0.7, a=0.08),
    footnote_border: @core.Color::black().multiply_alpha(0.22),
    front_matter_background: @core.Color::rgba(r=0.20, g=0.28, b=0.36, a=0.07),
    front_matter_text: @core.Color::black().multiply_alpha(0.74),
    html_background: @core.Color::rgba(r=0.12, g=0.12, b=0.12, a=0.06),
    html_text: @core.Color::black().multiply_alpha(0.70),
    syntax_keyword: @core.Color::rgba(r=0.36, g=0.32, b=0.78, a=1.0),
    syntax_string: @core.Color::rgba(r=0.20, g=0.62, b=0.36, a=1.0),
    syntax_comment: @core.Color::rgba(r=0.45, g=0.48, b=0.52, a=1.0),
    syntax_number: @core.Color::rgba(r=0.78, g=0.52, b=0.18, a=1.0),
    syntax_function: @core.Color::rgba(r=0.27, g=0.42, b=0.70, a=1.0),
    syntax_type: @core.Color::rgba(r=0.22, g=0.77, b=0.62, a=1.0),
    syntax_operator: @core.Color::rgba(r=0.87, g=0.33, b=0.33, a=1.0),
    syntax_annotation: @core.Color::rgba(r=0.76, g=0.32, b=0.62, a=1.0),
    syntax_escape: @core.Color::rgba(r=0.67, g=0.29, b=0.91, a=1.0),
  }
}

///|
/// A readable dark-surface palette. The editor passes its own theme tokens;
/// this is the default for previews and callers without a theme.
pub fn MarkdownRichTextPalette::dark() -> MarkdownRichTextPalette {
  {
    link: @core.Color::rgba(r=0.48, g=0.70, b=1.0, a=1.0),
    inline_code_background: @core.Color::white().multiply_alpha(0.10),
    image_background: @core.Color::rgba(r=0.45, g=0.65, b=0.95, a=0.14),
    footnote_reference_background: @core.Color::rgba(
      r=0.45,
      g=0.65,
      b=0.95,
      a=0.14,
    ),
    html_inline_background: @core.Color::white().multiply_alpha(0.08),
    highlight_background: @core.Color::rgba(r=1.0, g=0.80, b=0.25, a=0.28),
    quote_text: @core.Color::rgba(r=0.86, g=0.88, b=0.91, a=1.0),
    quote_background: @core.Color::white().multiply_alpha(0.06),
    quote_border: @core.Color::white().multiply_alpha(0.30),
    code_background: @core.Color::white().multiply_alpha(0.06),
    code_language_text: @core.Color::white().multiply_alpha(0.60),
    table_background: @core.Color::white().multiply_alpha(0.05),
    table_border: @core.Color::white().multiply_alpha(0.16),
    table_header_background: @core.Color::white().multiply_alpha(0.07),
    footnote_text: @core.Color::rgba(r=0.86, g=0.88, b=0.91, a=1.0),
    footnote_label_text: @core.Color::white().multiply_alpha(0.65),
    footnote_label_background: @core.Color::rgba(r=0.45, g=0.65, b=0.95, a=0.16),
    footnote_border: @core.Color::white().multiply_alpha(0.24),
    front_matter_background: @core.Color::white().multiply_alpha(0.07),
    front_matter_text: @core.Color::white().multiply_alpha(0.75),
    html_background: @core.Color::white().multiply_alpha(0.07),
    html_text: @core.Color::white().multiply_alpha(0.75),
    syntax_keyword: @core.Color::rgba(r=0.78, g=0.57, b=0.93, a=1.0),
    syntax_string: @core.Color::rgba(r=0.63, g=0.84, b=0.65, a=1.0),
    syntax_comment: @core.Color::rgba(r=0.50, g=0.54, b=0.60, a=1.0),
    syntax_number: @core.Color::rgba(r=0.95, g=0.77, b=0.42, a=1.0),
    syntax_function: @core.Color::rgba(r=0.51, g=0.72, b=0.98, a=1.0),
    syntax_type: @core.Color::rgba(r=0.50, g=0.86, b=0.85, a=1.0),
    syntax_operator: @core.Color::rgba(r=0.95, g=0.62, b=0.56, a=1.0),
    syntax_annotation: @core.Color::rgba(r=0.90, g=0.81, b=0.48, a=1.0),
    syntax_escape: @core.Color::rgba(r=0.78, g=0.57, b=0.93, a=1.0),
  }
}

///|
fn markdown_rich_text_palette_mix_color(
  hash : UInt64,
  color : @core.Color,
) -> UInt64 {
  let mut hash = rich_text_digest_mix(
    hash,
    (color.r * 255.0).to_int().to_uint64(),
  )
  hash = rich_text_digest_mix(hash, (color.g * 255.0).to_int().to_uint64())
  hash = rich_text_digest_mix(hash, (color.b * 255.0).to_int().to_uint64())
  rich_text_digest_mix(hash, (color.a * 255.0).to_int().to_uint64())
}

///|
/// Stable digest of every role. Block conversions cache their baked colors,
/// so this participates in the conversion cache key; a theme switch therefore
/// cannot reuse a block projected with the old palette.
pub fn MarkdownRichTextPalette::cache_key(
  self : MarkdownRichTextPalette,
) -> String {
  let colors : Array[@core.Color] = [
    self.link,
    self.inline_code_background,
    self.image_background,
    self.footnote_reference_background,
    self.html_inline_background,
    self.highlight_background,
    self.quote_text,
    self.quote_background,
    self.quote_border,
    self.code_background,
    self.code_language_text,
    self.table_background,
    self.table_border,
    self.table_header_background,
    self.footnote_text,
    self.footnote_label_text,
    self.footnote_label_background,
    self.footnote_border,
    self.front_matter_background,
    self.front_matter_text,
    self.html_background,
    self.html_text,
    self.syntax_keyword,
    self.syntax_string,
    self.syntax_comment,
    self.syntax_number,
    self.syntax_function,
    self.syntax_type,
    self.syntax_operator,
    self.syntax_annotation,
    self.syntax_escape,
  ]
  let mut hash = 0xcbf29ce484222325UL
  for color in colors {
    hash = markdown_rich_text_palette_mix_color(hash, color)
  }
  "palette:" + hash.to_string()
}

///|
/// Resolve an optional palette at a conversion entry point. `None` keeps the
/// historical light output for every existing caller.
fn markdown_rich_text_palette_or_light(
  palette : MarkdownRichTextPalette?,
) -> MarkdownRichTextPalette {
  match palette {
    Some(palette) => palette
    None => MarkdownRichTextPalette::light()
  }
}