zte 0.1.1

A buffer/window-driven text editor that takes inspiration from Howl
use std::{
    io::{Write, stdout, Stdout},
    fmt,
};
use vek::*;
use termion::{
    screen::AlternateScreen,
    input::MouseTerminal,
    raw::{RawTerminal, IntoRawMode},
    clear,
    cursor,
    color,
    terminal_size,
};

#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Color {
    Rgb(Rgb<u8>),
    Reset,
}

struct Fg<T>(T);
struct Bg<T>(T);

impl fmt::Display for Fg<Color> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.0 {
            Color::Rgb(rgb) => write!(f, "{}", color::Rgb(rgb.r, rgb.g, rgb.b).fg_string())?,
            Color::Reset => write!(f, "{}", color::Reset.fg_str())?,
        }
        Ok(())
    }
}

impl fmt::Display for Bg<Color> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.0 {
            Color::Rgb(rgb) => write!(f, "{}", color::Rgb(rgb.r, rgb.g, rgb.b).bg_string())?,
            Color::Reset => write!(f, "{}", color::Reset.bg_str())?,
        }
        Ok(())
    }
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Attr {
    Reset,
}

impl fmt::Display for Attr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        Ok(())
    }
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Cell(pub char, pub Color, pub Color, pub Attr);

impl From<char> for Cell {
    fn from(c: char) -> Self {
        Self(c, Color::Reset, Color::Reset, Attr::Reset)
    }
}

impl Default for Cell {
    fn default() -> Self {
        Self::from(' ')
    }
}

#[derive(Clone)]
pub struct Grid {
    size: Extent2<usize>,
    cells: Vec<Cell>,
}

impl Grid {
    pub fn new(size: Extent2<usize>) -> Self {
        Self {
            size,
            cells: vec![Cell::default(); size.w * size.h],
        }
    }

    pub fn size(&self) -> Extent2<usize> {
        self.size
    }

    fn idx_of(&self, pos: Vec2<usize>) -> Option<usize> {
        if pos.map2(self.size.into(), |e, sz| e < sz).reduce_and() {
            Some(self.size.w * pos.y + pos.x)
        } else {
            None
        }
    }

    pub fn get(&self, pos: impl Into<Vec2<usize>>) -> Cell {
        match self.idx_of(pos.into()) {
            Some(idx) => self.cells
                .get(idx)
                .copied()
                .unwrap_or(Cell::default()),
            None => Cell::default(),
        }
    }

    pub fn set(&mut self, pos: impl Into<Vec2<usize>>, cell: Cell) {
        match self.idx_of(pos.into()) {
            Some(idx) => {
                self.cells
                    .get_mut(idx)
                    .map(|c| *c = cell);
            },
            None => {},
        }
    }
}

pub struct Display {
    size: Extent2<usize>,
    cursor_pos: Option<Vec2<usize>>,
    grids: (Grid, Grid),
    screen: AlternateScreen<MouseTerminal<RawTerminal<Stdout>>>,
}

impl Display {
    pub fn new() -> Self {
        let screen = AlternateScreen::from(MouseTerminal::from(stdout().into_raw_mode().unwrap()));

        let size = Extent2::from(terminal_size().unwrap()).map(|e: u16| e as usize);
        let grid = Grid::new(size);
        let mut this = Self {
            size,
            cursor_pos: None,
            grids: (grid.clone(), grid),
            screen,
        };
        this.init();
        this
    }

    fn init(&mut self) {
        write!(self.screen, "{}", clear::All).unwrap();
        write!(self.screen, "{}", cursor::Save).unwrap();
        write!(self.screen, "{}", cursor::Hide).unwrap();
        write!(self.screen, "{}", cursor::BlinkingBar).unwrap();
        for row in 0..self.size.h {
            write!(self.screen, "{}", cursor::Goto(1, row as u16 + 1)).unwrap();
            for col in 0..self.size.w {
                let Cell(c, fg, bg, attr) = self.grids.0.get((col, row));
                write!(self.screen, "{}", c).unwrap();
            }
        }
    }

    #[allow(dead_code)]
    pub fn grid_mut(&mut self) -> &mut Grid {
        &mut self.grids.1
    }

    #[allow(dead_code)]
    pub fn size(&self) -> Extent2<usize> {
        self.size
    }

    #[allow(dead_code)]
    pub fn set_cursor(&mut self, pos: Option<Vec2<usize>>) {
        self.cursor_pos = pos;
    }

    #[allow(dead_code)]
    pub fn render(&mut self) {
        write!(self.screen, "{}", cursor::Goto(1, 1)).unwrap();
        let mut last_pos = Vec2::zero();

        for row in 0..self.size.h {
            for col in 0..self.size.w {
                let (front, back) = (self.grids.0.get((col, row)), self.grids.1.get((col, row)));

                if front != back {
                    if last_pos != Vec2::new(col.saturating_sub(1), row) {
                        write!(self.screen, "{}", cursor::Goto(col as u16 + 1, row as u16 + 1)).unwrap();
                    }

                    let Cell(c, fg, bg, attr) = back;
                    write!(self.screen, "{}{}{}", Fg(fg), Bg(bg), attr).unwrap();
                    write!(self.screen, "{}", c).unwrap();
                    last_pos = Vec2::new(col, row);
                }
            }
        }

        self.grids.0 = self.grids.1.clone();

        if let Some(cursor_pos) = self.cursor_pos {
            write!(self.screen, "{}", cursor::Show).unwrap();
            write!(self.screen, "{}", cursor::Goto(cursor_pos.x as u16 + 1, cursor_pos.y as u16 + 1)).unwrap();
        } else {
            write!(self.screen, "{}", cursor::Hide).unwrap();
        }

        self.screen.flush().unwrap();
    }
}

impl Drop for Display {
    fn drop(&mut self) {
        write!(self.screen, "{}", cursor::Show).unwrap();
        write!(self.screen, "{}", cursor::Restore).unwrap();
    }
}