use std::io;
use termion::cursor;
use crate::cursor::CursorPosition;
use crate::segment::Segment;
use crate::line::Line;
use crate::result::{Result, TTYError};
pub(crate) fn move_cursor_to(writer: &mut dyn io::Write, cursor: &mut CursorPosition,
to: &CursorPosition) -> Result<()> {
move_cursor_exact(writer, cursor, to.x, to.y)?;
Ok(())
}
pub(crate) fn move_cursor_exact(writer: &mut dyn io::Write, cursor: &mut CursorPosition,
x: u16, y: u16) -> Result<()> {
let diff_x: i16 = (x as i16) - (cursor.x as i16);
let diff_y: i16 = (y as i16) - (cursor.y as i16);
move_cursor_by(writer, cursor, diff_x, diff_y)?;
Ok(())
}
pub(crate) fn move_cursor_by(writer: &mut dyn io::Write, cursor: &mut CursorPosition,
diff_x: i16, diff_y: i16) -> Result<()> {
if (diff_x < 0 && diff_x > cursor.x as i16) || (diff_y < 0 && diff_y > cursor.y as i16) {
return Err(TTYError::CursorOutOfBounds);
}
if diff_x > 0 {
write!(writer, "{}", cursor::Right(diff_x as u16))?;
cursor.x += diff_x as u16;
} else if diff_x < 0 {
write!(writer, "{}", cursor::Left(diff_x.abs() as u16))?;
cursor.x -= diff_x.abs() as u16;
}
if diff_y > 0 {
write!(writer, "{}", "\n".repeat(diff_y as usize))?;
cursor.y += diff_y as u16;
} else if diff_y < 0 {
write!(writer, "{}", cursor::Up(diff_y.abs() as u16))?;
cursor.y -= diff_y.abs() as u16;
}
Ok(())
}
pub(crate) fn clear_line(writer: &mut dyn io::Write) -> Result<()> {
write!(writer, "{}", termion::clear::CurrentLine)?;
Ok(())
}
pub(crate) fn clear_rest_of_line(writer: &mut dyn io::Write) -> Result<()> {
write!(writer, "{}", termion::clear::UntilNewline)?;
Ok(())
}
pub(crate) fn render_line(writer: &mut dyn io::Write, cursor: &mut CursorPosition,
line: &Line) -> Result<()> {
if cursor.x != 0 {
move_cursor_exact(writer, cursor, 0, cursor.y)?;
}
clear_line(writer)?;
for segment in &line.segments {
render_segment(writer, cursor, &segment)?;
}
Ok(())
}
pub(crate) fn render_segment(writer: &mut dyn io::Write, cursor: &mut CursorPosition,
segment: &Segment) -> Result<()> {
cursor.x += segment.text.len() as u16;
match &segment.format {
Some(format) => write!(writer, "{}{}{}", format.pre, segment.text, format.post)?,
None => write!(writer, "{}", segment.text)?,
}
Ok(())
}