use std::io::{self, Write};
use rocketsplash_formats::Rgb;
use crate::{rgb_to_16_bg, rgb_to_16_fg, rgb_to_256, ColorMode, RenderCell, TextStyle};
const STYLE_CODES: [(TextStyle, u8, u8); 4] = [
(TextStyle::BOLD, 1, 22),
(TextStyle::ITALIC, 3, 23),
(TextStyle::UNDERLINE, 4, 24),
(TextStyle::REVERSE, 7, 27),
];
#[derive(Clone, Copy, Debug, Default)]
pub(super) struct AnsiState {
fg: Option<Rgb>,
bg: Option<Rgb>,
style: TextStyle,
}
impl AnsiState {
pub(super) fn is_default(&self) -> bool {
self.fg.is_none() && self.bg.is_none() && self.style.is_empty()
}
pub(super) fn apply<W: Write>(
&mut self,
writer: &mut W,
cell: &RenderCell,
mode: ColorMode,
) -> io::Result<()> {
let mut first = true;
macro_rules! write_sep {
() => {
if first {
writer.write_all(b"\x1b[")?;
first = false;
} else {
writer.write_all(b";")?;
}
};
}
let next_style = cell.style;
if self.style != next_style {
for (flag, on_code, off_code) in STYLE_CODES {
let had = self.style.contains(flag);
let has = next_style.contains(flag);
if had && !has {
write_sep!();
write!(writer, "{}", off_code)?;
} else if !had && has {
write_sep!();
write!(writer, "{}", on_code)?;
}
}
self.style = next_style;
}
if self.fg != cell.fg {
write_sep!();
push_color_code(writer, cell.fg, mode, true)?;
self.fg = cell.fg;
}
if self.bg != cell.bg {
write_sep!();
push_color_code(writer, cell.bg, mode, false)?;
self.bg = cell.bg;
}
if !first {
writer.write_all(b"m")?;
}
Ok(())
}
}
fn push_color_code<W: Write>(
writer: &mut W,
color: Option<Rgb>,
mode: ColorMode,
is_fg: bool,
) -> io::Result<()> {
match (color, mode) {
(None, _) => write!(writer, "{}", if is_fg { "39" } else { "49" }),
(Some(rgb), ColorMode::TrueColor) => {
let prefix = if is_fg { "38" } else { "48" };
write!(writer, "{};2;{};{};{}", prefix, rgb.r, rgb.g, rgb.b)
}
(Some(rgb), ColorMode::Color256) => {
let prefix = if is_fg { "38" } else { "48" };
write!(writer, "{};5;{}", prefix, rgb_to_256(rgb))
}
(Some(rgb), ColorMode::Color16) => {
let code = if is_fg {
rgb_to_16_fg(rgb)
} else {
rgb_to_16_bg(rgb)
};
write!(writer, "{}", code)
}
(Some(_), ColorMode::NoColor) => Ok(()),
}
}