// Port of rich/rule.py.

///|
/// A console renderable to draw a horizontal rule (line).
pub struct Rule {
  title : TextType
  characters : String
  style : StyleType
  end : String
  align : AlignMethod
}

///|
pub fn Rule::new(
  title? : &IntoText,
  characters? : String = "─",
  style? : &IntoStyle,
  end? : String = "\n",
  align? : AlignMethod = Center,
) -> Rule raise RichError {
  if @cells.cell_len(characters) < 1 {
    raise ValueError(
      "'characters' argument must have a cell width of at least 1",
    )
  }
  {
    title: match title {
      Some(t) => t.into_text_type()
      None => Str("")
    },
    characters,
    style: match style {
      Some(s) => s.into_style_type()
      None => Def("rule.line")
    },
    end,
    align,
  }
}

///|
pub fn Rule::repr(self : Rule) -> String {
  let title = match self.title {
    Str(s) => py_repr(s)
    Text(t) => t.repr()
  }
  "Rule(\{title}, \{py_repr(self.characters)})"
}

///|
fn is_ascii(s : String) -> Bool {
  for i in 0.. 127 {
      return false
    }
  }
  true
}

///|
fn Rule::rule_line(self : Rule, chars_len : Int, width : Int) -> Text {
  let rule_text = Text::new(
    text=self.characters.repeat(floor_div(width, chars_len) + 1),
    style=self.style,
  )
  rule_text.truncate(width)
  rule_text.set_plain(@cells.set_cell_size(rule_text.plain(), width))
  rule_text
}

///|
pub impl Renderable for Rule with fn rich_console(self, console, options) {
  let width = options.max_width
  let characters = if options.ascii_only() && !is_ascii(self.characters) {
    "-"
  } else {
    self.characters
  }
  let chars_len = @cells.cell_len(characters)
  if !self.title.to_bool() {
    return [Child(self.rule_line(chars_len, width))]
  }
  let title_text = match self.title {
    Text(t) => t
    Str(s) => console.render_str(s, style="rule.text")
  }
  title_text.set_plain(title_text.plain().replace_all(old="\n", new=" "))
  title_text.expand_tabs()
  let required_space = if self.align == Center { 4 } else { 2 }
  let truncate_width = @cmp.maximum(0, width - required_space)
  if truncate_width == 0 {
    return [Child(self.rule_line(chars_len, width))]
  }
  let rule_text = Text::new(end=self.end)
  match self.align {
    Center => {
      title_text.truncate(truncate_width, overflow=Ellipsis)
      let side_width = floor_div(width - @cells.cell_len(title_text.plain()), 2)
      let left = Text::new(
        text=characters.repeat(floor_div(side_width, chars_len) + 1),
      )
      left.truncate(side_width - 1)
      let right_length = width -
        @cells.cell_len(left.plain()) -
        @cells.cell_len(title_text.plain())
      let right = Text::new(
        text=characters.repeat(floor_div(side_width, chars_len) + 1),
      )
      right.truncate(right_length)
      rule_text.append_str(left.plain() + " ", style=self.style) |> ignore
      rule_text.append_text(title_text) |> ignore
      rule_text.append_str(" " + right.plain(), style=self.style) |> ignore
    }
    Left => {
      title_text.truncate(truncate_width, overflow=Ellipsis)
      rule_text.append_text(title_text) |> ignore
      rule_text.append_str(" ") |> ignore
      rule_text.append_str(
        characters.repeat(@cmp.maximum(0, width - rule_text.cell_len())),
        style=self.style,
      )
      |> ignore
    }
    Right => {
      title_text.truncate(truncate_width, overflow=Ellipsis)
      rule_text.append_str(
        characters.repeat(@cmp.maximum(0, width - title_text.cell_len() - 1)),
        style=self.style,
      )
      |> ignore
      rule_text.append_str(" ") |> ignore
      rule_text.append_text(title_text) |> ignore
    }
  }
  rule_text.set_plain(@cells.set_cell_size(rule_text.plain(), width))
  [Child(rule_text)]
}

///|
pub impl Renderable for Rule with fn rich_measure(_self, _console, _options) {
  Some({ minimum: 1, maximum: 1, })
}