use std::any::Any;
use egui::{Color32, FontId, Ui, Vec2, WidgetText};
use egui_tiles::TileId;
use emulator_core::{memory::ram::{MemoryBank, Page}, memory::PhysAddr, EmulatorHandle};
use crate::ui::Action;
use crate::ui::panels::TilePane;

pub struct Display {
    width: u32,
    height: u32,
    addr: PhysAddr,

    emulator: EmulatorHandle,
    buffer: Vec<u8>,
}

impl Display {
    pub fn new(width: u32, height: u32, addr: PhysAddr, emulator: EmulatorHandle) -> Self {
        Self {
            width,
            height,
            addr,
            emulator,
            buffer: Vec::new(),
        }
    }

    pub fn width(&self) -> usize {
        self.width as usize
    }

    pub fn height(&self) -> usize {
        self.height as usize
    }


    pub fn changed(&mut self) -> bool {
        let temp = self.read();
        if temp != self.buffer {
            self.buffer = temp;
            return true;
        }
        false
    }

    pub fn read(&mut self) -> Vec<u8> {
        self.emulator.mem_read(self.addr, self.width * self.height)
    }
}


impl TilePane for Display {
    fn title(&self) -> WidgetText {
        "Display".into()
    }

    fn ui(&mut self, ui: &mut Ui, _tile_id: TileId) -> Option<Action> {
        let display_w = self.width();
        let display_h = self.height();
        let data = self.read();

        let font_id = FontId::monospace(12.0);

        let char_width = ui.fonts_mut(|f| f.glyph_width(&font_id, 'W'));
        let line_height = ui.fonts_mut(|f| f.row_height(&font_id));

        #[expect(clippy::cast_precision_loss)]
        let display_size = Vec2::new(
            char_width * display_w as f32,
            line_height * display_h as f32,
        );

        let (rect, _response) = ui.allocate_exact_size(display_size, egui::Sense::all());

        // ui.painter().rect_filled(rect, 0.0, Color32::BLACK);

        // Draw text
        for y in 0..display_h {
            let mut row_text = String::with_capacity(display_w);
            for x in 0..display_w {
                let index = y * display_w + x;
                if index < data.len() {
                    let byte = data[index];
                    let ch = if (32..=126).contains(&byte) {
                        byte as char
                    } else {
                        ' '
                    };
                    row_text.push(ch);
                } else {
                    row_text.push(' ');
                }
            }

            #[expect(clippy::cast_precision_loss)]
            let text_pos = rect.min + Vec2::new(0.0, y as f32 * line_height);

            ui.painter().text(
                text_pos,
                egui::Align2::LEFT_TOP,
                row_text,
                font_id.clone(),
                Color32::WHITE,
            );
        }
        None
    }

    fn as_any_mut(&mut self) -> &mut dyn Any {
        self
    }
}
