use std::num::ParseIntError;

use common::prelude::Instruction;

use crate::emulator::{
    system::model::{Command, State},
    ui::interface::Component,
};

#[derive(Default)]
pub struct MemoryInspector {
    view_size: u32,
    view_addr: u32,
    visible: bool,
    addr_input: String,
}

impl MemoryInspector {
    #[must_use]
    pub const fn new() -> Self {
        Self {
            view_size: 256,
            view_addr: 0,
            visible: false,
            addr_input: String::new(),
        }
    }
}

impl Component for MemoryInspector {
    fn category(&self) -> super::interface::Category {
        super::interface::Category::Memory
    }

    fn name(&self) -> &'static str {
        "Memory Inspector"
    }

    fn visible(&mut self) -> &mut bool {
        &mut self.visible
    }

    fn render(&mut self, state: &mut State, ui: &mut egui::Ui, ctx: &egui::Context) {
        // Right column - Memory
        ui.vertical(|ui| {
            ui.heading("Memory Inspector");
            ui.add_space(10.0);

            // Address input section
            ui.horizontal(|ui| {
                ui.label("Address:");

                let address_response = ui.add(
                    egui::TextEdit::singleline(&mut self.addr_input)
                        .hint_text("0x1000 or 4096")
                        .desired_width(150.0),
                );

                ui.add_space(10.0);

                // Search button
                let search_clicked = ui.button("🔍 Search").clicked();

                // Handle Enter key in text field
                let enter_pressed = address_response.lost_focus()
                    && ctx.input(|i| i.key_pressed(egui::Key::Enter));

                if search_clicked || enter_pressed {
                    if let Ok(new) = parse_address(&self.addr_input) {
                        self.view_addr = new;
                    } else {
                        state.error_log.push("Invalid address".to_string());
                    }
                }

                let _ = state
                    .cmd_sender
                    .send(Command::MemRequest(self.view_addr, self.view_size));

                ui.label("(hex or decimal)");
            });

            // Show input error if any
            if let Some(error) = state.error_log.last() {
                ui.colored_label(egui::Color32::RED, format!("Error: {error}"));
            }

            ui.add_space(10.0);

            // Memory table
            egui::ScrollArea::vertical()
                .auto_shrink(true)
                .id_salt("memory_inspector_scroll")
                .show(ui, |ui| {
                    egui::Grid::new("memory_grid")
                        .spacing([12.0, 2.0])
                        .min_col_width(5.0)
                        .striped(true)
                        .show(ui, |ui| {
                            // Header
                            ui.strong("Address");

                            for i in 0..4 {
                                ui.strong(format!("{i:X}"));
                            }

                            ui.strong("Decimal");
                            ui.strong("Instruction");

                            ui.end_row();

                            // Memory data (8 bytes per row)
                            for (row, chunk) in (0u32..).zip(state.memory_view.chunks(4))
                            {
                                let row_address = self.view_addr + (row * 4);
                                ui.monospace(format!(
                                    "0x{row_address:08X} ({row_address})"
                                ));
                                for &byte in chunk {
                                    ui.monospace(format!("{byte:02X}"));
                                }

                                // Fill remaining columns if last row is incomplete
                                for _ in chunk.len()..4 {
                                    ui.label("");
                                }

                                // combine all 4 bytes in the chunk into a u32
                                let combined = chunk.iter().fold(0u32, |acc, &byte| {
                                    (acc << 8) | u32::from(byte)
                                });

                                ui.monospace(format!("{combined}"));
                                ui.monospace(format!(
                                    "{}",
                                    Instruction::decode(combined)
                                        .unwrap_or(Instruction::Nop)
                                ));

                                ui.end_row();
                            }
                        });
                });
        });
    }
}

fn parse_address(address: &str) -> Result<u32, ParseIntError> {
    if let Some(hex_part) = address.strip_prefix("0x") {
        return u32::from_str_radix(hex_part, 16);
    }

    if let Some(bin_part) = address.strip_prefix("0b") {
        return u32::from_str_radix(bin_part, 2);
    }

    if let Some(oct_part) = address.strip_prefix("0o") {
        return u32::from_str_radix(oct_part, 8);
    }

    address.parse::<u32>()
}
