///|
/// Replace {placeholder} tokens in a template string with log entry fields.
pub fn lazy_format(template : String, entry : LogEntry) -> String {
  let mut result = template
  result = result.replace(
    old="{timestamp}"[:],
    new=entry.timestamp.to_string()[:],
  )
  result = result.replace(old="{level}"[:], new=entry.level.to_string()[:])
  result = result.replace(old="{message}"[:], new=entry.message[:])
  result = result.replace(old="{module}"[:], new=entry.mod_name[:])
  result = result.replace(old="{file}"[:], new=entry.file[:])
  result = result.replace(old="{line}"[:], new=entry.line.to_string()[:])
  result = result.replace(old="{thread}"[:], new=entry.thread_id.to_string()[:])
  result = result.replace(old="{seq}"[:], new=entry.sequence.to_string()[:])
  let n = entry.fields.length()
  let mut i = 0
  while i < n {
    let (k, v) = entry.fields[i]
    let placeholder = "{" + k + "}"
    result = result.replace(old=placeholder[:], new=v[:])
    i = i + 1
  }
  result
}

///|
/// Create a formatter that applies the given template pattern to every entry.
pub fn pattern_formatter(pattern : String) -> (LogEntry) -> String {
  fn(entry : LogEntry) -> String { lazy_format(pattern, entry) }
}

///|
/// Compact formatter with optional fields and source location display.
pub fn stats_formatter(
  show_fields : Bool,
  show_location : Bool,
) -> (LogEntry) -> String {
  fn(entry : LogEntry) -> String {
    let sb = StringBuilder()
    sb.write_string("[")
    sb.write_string(entry.level.to_string())
    sb.write_string("] ")
    sb.write_string(entry.message)
    if show_location && entry.file.length() > 0 {
      sb.write_string(" (")
      sb.write_string(entry.file)
      sb.write_string(":")
      sb.write_string(entry.line.to_string())
      sb.write_string(")")
    }
    if show_fields {
      let n = entry.fields.length()
      if n > 0 {
        sb.write_string(" {")
        let mut i = 0
        while i < n {
          let (k, v) = entry.fields[i]
          if i > 0 {
            sb.write_string(", ")
          }
          sb.write_string(k)
          sb.write_string(": ")
          sb.write_string(v)
          i = i + 1
        }
        sb.write_string("}")
      }
    }
    sb.to_string()
  }
}

///|
/// Multi-line formatter that prints each field on its own line with indentation.
pub fn multi_line_formatter(indent : Int) -> (LogEntry) -> String {
  fn(entry : LogEntry) -> String {
    let pad = String::make(indent, ' ')
    let sb = StringBuilder()
    sb.write_string("LogEntry {\n")
    sb.write_string(pad)
    sb.write_string("timestamp: ")
    sb.write_string(entry.timestamp.to_string())
    sb.write_string("\n")
    sb.write_string(pad)
    sb.write_string("level: ")
    sb.write_string(entry.level.to_string())
    sb.write_string("\n")
    sb.write_string(pad)
    sb.write_string("message: ")
    sb.write_string(entry.message)
    sb.write_string("\n")
    sb.write_string(pad)
    sb.write_string("module: ")
    sb.write_string(entry.mod_name)
    sb.write_string("\n")
    sb.write_string(pad)
    sb.write_string("file: ")
    sb.write_string(entry.file)
    sb.write_string("\n")
    sb.write_string(pad)
    sb.write_string("line: ")
    sb.write_string(entry.line.to_string())
    sb.write_string("\n")
    sb.write_string(pad)
    sb.write_string("fields: [")
    let n = entry.fields.length()
    let mut i = 0
    while i < n {
      if i > 0 {
        sb.write_string(", ")
      }
      let (k, v) = entry.fields[i]
      sb.write_string(k)
      sb.write_string("=")
      sb.write_string(v)
      i = i + 1
    }
    sb.write_string("]\n}")
    sb.to_string()
  }
}