///|
/// Traits describing a backend (Ruby `Converter::BackendTraits`).
pub(all) struct BackendTraits {
  basebackend : String
  filetype : String
  htmlsyntax : String?
  outfilesuffix : String
  supports_templates : Bool
} derive(Debug)

///|
/// Derives backend traits from a backend name (Ruby `Converter.derive_backend_traits`).
pub fn derive_backend_traits(
  backend : String,
  basebackend? : String,
) -> BackendTraits {
  let base = match basebackend {
    Some(b) => b
    None => trailing_digits_rx.gsub(backend, "")
  }
  let (outfilesuffix, filetype) = match base {
    "html" => (".html", "html")
    "docbook" => (".xml", "xml")
    "pdf" => (".pdf", "pdf")
    "epub" => (".epub", "epub")
    "manpage" => (".man", "man")
    "asciidoc" => (".adoc", "adoc")
    _ => (".\{base}", base)
  }
  {
    basebackend: base,
    filetype,
    htmlsyntax: if filetype == "html" {
      Some("html")
    } else {
      None
    },
    outfilesuffix,
    supports_templates: false,
  }
}

///|
/// A converter turns nodes into output. `transform` is the node name
/// (`paragraph`, `inline_quoted`, ...) or `document`/`embedded`/`outline`.
pub(open) trait Converter {
  fn convert(Self, Node, String) -> String
  fn backend_traits(Self) -> BackendTraits
}

///|
/// Creates a converter for a backend: (backend, htmlsyntax, document) -> converter.
pub type ConverterFactory = (String, String?, Node) -> &Converter

///|
let converter_registry : Map[String, ConverterFactory] = Map([])

///|
/// Registers a converter factory for the given backend names.
pub fn register_converter(
  backends : Array[String],
  factory : ConverterFactory,
) -> Unit {
  for b in backends {
    converter_registry[b] = factory
  }
}

///|
/// Looks up the converter factory registered for a backend.
pub fn lookup_converter(backend : String) -> ConverterFactory? {
  converter_registry.get(backend)
}

///|
/// Names of the registered backends.
pub fn registered_backends() -> Array[String] {
  converter_registry.keys().collect()
}

///|
/// A converter that renders nothing (used when no converter is registered).
priv struct NullConverter {
  traits : BackendTraits
}

///|
impl Converter for NullConverter with fn convert(_self, _node, _transform) {
  ""
}

///|
impl Converter for NullConverter with fn backend_traits(self) {
  self.traits
}

///|
/// A converter that dispatches individual transforms to handler functions and
/// falls back to a delegate (Ruby `CompositeConverter` / converter subclassing).
pub struct CompositeConverter {
  handlers : Map[String, (Node) -> String]
  delegate : &Converter
}

///|
pub fn CompositeConverter::new(
  delegate : &Converter,
  handlers? : Map[String, (Node) -> String] = {},
) -> CompositeConverter {
  { handlers, delegate, }
}

///|
pub impl Converter for CompositeConverter with fn convert(self, node, transform) {
  match self.handlers.get(transform) {
    Some(h) => h(node)
    None => self.delegate.convert(node, transform)
  }
}

///|
pub impl Converter for CompositeConverter with fn backend_traits(self) {
  self.delegate.backend_traits()
}