use std::io::{self, Write};

use tui::{buffer, layout::Rect, style::Color};

use crate::{Color as TColor, RawTerminal, Term};

fn to_attributes(color: Color) -> Option<(TColor, bool)> {
    Some(match color {
        Color::Reset | Color::Indexed(_) | Color::Rgb(_, _, _) => return None,
        Color::Red => (TColor::Red, false),
        Color::LightRed => (TColor::Red, true),
        Color::Green => (TColor::Green, false),
        Color::LightGreen => (TColor::Green, true),
        Color::Blue => (TColor::Blue, false),
        Color::LightBlue => (TColor::Blue, true),
        Color::Yellow => (TColor::Yellow, false),
        Color::LightYellow => (TColor::Yellow, true),
        Color::Magenta => (TColor::Magenta, false),
        Color::LightMagenta => (TColor::Magenta, true),
        Color::Cyan => (TColor::Cyan, false),
        Color::LightCyan => (TColor::Cyan, true),
        Color::Gray => (TColor::White, false),
        Color::White => (TColor::White, true),
        Color::Black => (TColor::Black, false),
        Color::DarkGray => (TColor::Black, true),
    })
}

impl tui::backend::Backend for Term {
    fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error>
    where
        I: Iterator<Item = (u16, u16, &'a buffer::Cell)>,
    {
        let mut fg = Color::Reset;
        let mut bg = Color::Reset;
        let mut last_pos: Option<(u16, u16)> = None;

        for (x, y, cell) in content {
            if !matches!(last_pos, Some(p) if x == p.0 + 1 && y == p.1) {
                self.stdout.raw_move_cursor(y as usize, x as usize)?;
            }
            last_pos = Some((x, y));
            if cell.fg != fg {
                if let Some((color, bold)) = to_attributes(cell.fg) {
                    if bold {
                        self.stdout.raw_set_style(1)?;
                    } else {
                        self.stdout.raw_set_style(22)?;
                    }
                    self.stdout.raw_set_color(3, color)?;
                } else {
                    self.stdout.raw_set_style(0)?;
                }
            }
            if cell.bg != bg || cell.fg != fg {
                if let Some((color, _)) = to_attributes(cell.bg) {
                    self.stdout.raw_set_color(4, color)?;
                } else {
                    self.stdout.raw_set_color(4, TColor::Black)?;
                }
            }

            fg = cell.fg;
            bg = cell.bg;

            self.stdout.write_all(cell.symbol.as_bytes())?;
        }

        self.stdout.raw_set_style(0)?;

        Ok(())
    }

    fn clear(&mut self) -> Result<(), io::Error> {
        self.stdout.raw_clear_all()
    }

    fn flush(&mut self) -> Result<(), io::Error> {
        self.stdout.flush()
    }

    fn size(&self) -> Result<Rect, io::Error> {
        let (w, h) = self.stdout.raw_size()?;
        Ok(Rect {
            x: 0,
            y: 0,
            width: w as u16,
            height: h as u16 - 1,
        })
    }

    fn show_cursor(&mut self) -> Result<(), io::Error> {
        self.stdout.raw_set_cursor_visible(true)
    }

    fn hide_cursor(&mut self) -> Result<(), io::Error> {
        self.stdout.raw_set_cursor_visible(false)
    }

    fn set_cursor(&mut self, x: u16, y: u16) -> Result<(), io::Error> {
        self.stdout.raw_move_cursor(y as usize, x as usize)
    }

    fn get_cursor(&mut self) -> Result<(u16, u16), io::Error> {
        Ok((0, 0))
    }
}
