use crate::arch::x86_64::drivers::keyboard::{KeyStroke, get_keystroke_async};
use crate::resources::font::Font;
use crate::serial_print;
use crate::serial_println;
use crate::std::application::{
    Application, Error, frame::Frame, render::RenderError, window::Window,
};
use crate::std::ascii::Writer;
use crate::std::maths::geometry::Vec2;
use alloc::string::{String, ToString};
use alloc::vec::Vec;

pub struct Editor {
    cursor_line: usize,
    cursor_col: usize,

    mode: Mode,
    buffer: String,
    window: Window,
}

impl Default for Editor {
    fn default() -> Self {
        Self::new()
    }
}

impl<'a> Editor {
    const PADDING: usize = 8;

    pub const fn new() -> Self {
        Self {
            cursor_line: 0,
            cursor_col: 0,

            mode: Mode::Nor,
            buffer: String::new(),
            window: Window::new(),
        }
    }

    fn render(&'a self) -> Result<Frame<'a>, RenderError> {
        let mut frame = Frame::new(&self.window);
        let font = Font::default();
        let writer = Writer::new(&font);

        let (width, height) = writer.font_size().into();

        let mut col = 0;
        let mut line = 0;
        let mut scale = 1;

        for ch in self.buffer.chars() {
            if ch == '\n' {
                line += scale;
                col = 0;
                scale = 1;
                continue;
            }

            if width * (col + 1) > frame.dimensions().x() - 2 * Self::PADDING {
                line += scale;
                col = 0;
            }

            writer.render_glyph(
                &mut frame,
                Vec2::new(
                    col * width + Self::PADDING,
                    line * height + Self::PADDING,
                ),
                ch as u8,
                scale,
            )?;
            col += scale;
        }

        writer
            .render_glyph(
                &mut frame,
                Vec2::new(
                    self.cursor_col * width + Self::PADDING,
                    self.cursor_line * height + Self::PADDING,
                ),
                b'_',
                scale,
            )
            .expect("TODO: panic message");

        Ok(frame)
    }

    #[allow(unused_variables, dead_code, clippy::needless_pass_by_ref_mut)]
    fn get_lines(&self) -> Vec<&str> {
        self.buffer.split('\n').collect::<Vec<&str>>()
    }

    #[allow(unused_variables, dead_code, clippy::needless_pass_by_ref_mut)]
    fn move_cursor(&mut self, x: i32, y: i32) {
        self.cursor_line = self
            .cursor_line
            .checked_add_signed(y as isize)
            .unwrap_or(self.cursor_line);
        self.cursor_col = self
            .cursor_col
            .checked_add_signed(x as isize)
            .unwrap_or(self.cursor_col);
    }

    #[allow(unused_variables, dead_code, clippy::needless_pass_by_ref_mut)]
    fn delete_char(&mut self) {
        let i = self.get_char_idx();
        self.buffer.remove(i);
    }

    #[allow(unused_variables, dead_code, clippy::needless_pass_by_ref_mut)]
    fn insert_char(&mut self, c: char) {
        let i = self.get_char_idx();
        self.buffer.insert(i, c);
    }

    #[allow(unused_variables, dead_code, clippy::needless_pass_by_ref_mut)]
    fn splitline(&mut self) {
        let i = self.get_char_idx();
        self.buffer.insert(i, '\n');
    }

    fn get_char_idx(&self) -> usize {
        let frame = Frame::new(&self.window);
        let font = Font::default();
        let writer = Writer::new(&font);
        let (width, _height) = writer.font_size().into();

        let mut col = 0;
        let mut line = 0;
        let mut scale = 1;

        for (i, ch) in self.buffer.chars().enumerate() {
            if ch == '\n' {
                line += scale;
                col = 0;
                scale = 1;
                continue;
            }

            if width * (col + 1) > frame.dimensions().x() - 2 * Self::PADDING {
                line += scale;
                col = 0;
            }

            if col == self.cursor_col && line == self.cursor_line {
                return i;
            }

            col += scale;
        }

        0
    }
}

impl Application for Editor {
    type Output = ();
    async fn run(
        &mut self,
        _args: Vec<alloc::string::String>,
    ) -> Result<Self::Output, Error> {
        self.window.set_dimensions(Vec2::new(1280, 800));
        self.window.set_position(Vec2::new(0, 0));
        self.window.open();

        self.buffer = "Hello world, this is a test init. idk test \n ewntuiewi gjk gfdfg gndf ngdfgnmdfg ndfgmndfg gdfndfnkg njkdgjkndfjnkg ngnjfgnfgnfg fgn fn gfj gnfg jnfgjfngjk fgnjfgnjk jnkdgjnkdfg gfnd njkgdfgjn d fjnkgjkndfgjkndfgjn gndfjnk njkgdfng jnkfgdjknd jnfkgnjk".to_string();

        loop {
            if let Err(_err) = self.render().and_then(|frame| frame.render()) {
                // TODO: Handle error
                return Err(Error::ApplicationFailed(
                    "Rendering failed".to_string(),
                ));
            }

            let keystroke = get_keystroke_async().await;

            match self.mode {
                Mode::Nor => match keystroke {
                    KeyStroke::Char('i') => self.mode = Mode::Ins,
                    KeyStroke::Char('`') => return Ok(()),
                    _ => {}
                },
                Mode::Ins => {
                    match keystroke {
                        KeyStroke::Char(c) => {
                            match c {
                                // escape
                                '\x1B' => self.mode = Mode::Nor,
                                // delete
                                '\x7F' => self.delete_char(),
                                // backspace
                                '\x08' => {
                                    self.move_cursor(-1, 0);
                                    self.delete_char();
                                }
                                // enter
                                '\n' => self.splitline(),
                                _ => {
                                    self.insert_char(c);
                                    self.move_cursor(1, 0);
                                }
                            }
                        }
                        KeyStroke::Left => {
                            serial_println!("Left\n");
                            self.move_cursor(-1, 0);
                        }
                        KeyStroke::Right => {
                            serial_println!("Right\n");
                            self.move_cursor(1, 0);
                        }
                        KeyStroke::Up => {
                            serial_println!("Up\n");
                            self.move_cursor(0, -1);
                        }
                        KeyStroke::Down => {
                            serial_println!("Down\n");
                            self.move_cursor(0, 1);
                        }
                        KeyStroke::None => {}
                        _ => {}
                    }
                }
            }
        }
    }
}

pub enum Mode {
    Nor,
    Ins,
}

impl core::fmt::Display for Mode {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            Self::Nor => write!(f, "Normal"),
            Self::Ins => write!(f, "Insert"),
        }
    }
}
