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

use common::{instructions::Register, prelude::Instruction};

pub struct ControlPanel {
    visible: bool,
    step_amount_input: String,
    step_amount: usize,
}

impl ControlPanel {
    #[allow(clippy::must_use_candidate)]
    pub fn new() -> Self {
        Self {
            visible: false,
            step_amount_input: String::from("1"),
            step_amount: 1,
        }
    }
}

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

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

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

    fn name(&self) -> &'static str {
        "Control Panel"
    }

    fn render(&mut self, state: &mut State, ui: &mut egui::Ui, ctx: &egui::Context) {
        ui.horizontal(|ui| {
            // Pause / Run
            if ui
                .button(if state.running == Running::Running {
                    "Pause"
                } else {
                    "Run"
                })
                .clicked()
            {
                if state.running == Running::Running {
                    state.cmd_sender.send(Command::Stop).unwrap_or_else(|_| {
                        state.error_log.push("Failed to send command".to_string());
                    });
                } else {
                    state.cmd_sender.send(Command::Start).unwrap_or_else(|_| {
                        state.error_log.push("Failed to send command".to_string());
                    });
                }
            }

            // Step
            if ui.button("Step").clicked() {
                state
                    .cmd_sender
                    .send(Command::Step(self.step_amount))
                    .unwrap_or_else(|_| {
                        state.error_log.push("Failed to send command".to_string());
                    });
            }

            // Resets the emulator and all attached devices
            if ui.button("Reset All").clicked() {
                state
                    .cmd_sender
                    .send(Command::Reset(0))
                    .unwrap_or_else(|_| {
                        state.error_log.push("Failed to send command".to_string());
                    });
            }

            // Resets the emulator and all attached devices
            if ui.button("Clear Registers").clicked() {
                state
                    .cmd_sender
                    .send(Command::Reset(1))
                    .unwrap_or_else(|_| {
                        state.error_log.push("Failed to send command".to_string());
                    });
            }

            // Resets the emulator and all attached devices
            if ui.button("Clear RAM").clicked() {
                state
                    .cmd_sender
                    .send(Command::Reset(2))
                    .unwrap_or_else(|_| {
                        state.error_log.push("Failed to send command".to_string());
                    });
            }

            ui.separator();

            state.send(Command::RegisterRequest);
            state.send(Command::RunningRequest);
            state.send(Command::InstructionCountRequest);

            if ui
                .text_edit_singleline(&mut self.step_amount_input)
                .changed()
            {
                self.step_amount = if let Ok(amount) = self.step_amount_input.parse() {
                    amount
                } else {
                    state
                        .error_log
                        .push("Unable to parse step amount".to_string());
                    1
                }
            }

            // Status info
            ui.label(format!(
                "Status: {}",
                match state.running {
                    Running::Running => "Running",
                    Running::Paused => "Paused",
                    Running::Halted => "Halted",
                }
            ));

            let pcx = state
                .reg_file
                .get(Register::Pcx)
                .expect("PCX should never be invalid");
            let instructions = state.instructions;

            ui.label(format!("Instructions: {instructions}"));
            ui.label(format!("PC: 0x{pcx:08X}"));

            let instruction = Instruction::decode(
                state
                    .reg_file
                    .get(Register::Cir)
                    .expect("CIR should never be invalid"),
            )
            .map_or_else(
                |_| "Invalid Instruction".to_string(),
                |instruction| instruction.to_string(),
            );

            ui.label(format!("Instruction: {instruction}"));
        });

        render_register_table(state, ui, ctx);
    }
}

fn render_register_table(state: &State, ui: &mut egui::Ui, _ctx: &egui::Context) {
    // Left column - Registers
    ui.vertical(|ui| {
        ui.heading("Registers");

        egui::ScrollArea::vertical()
            .id_salt("register_inspector_scroll")
            .show(ui, |ui| {
                egui::Grid::new("registers_grid")
                    .num_columns(8)
                    .spacing([40.0, 4.0])
                    .striped(true)
                    .show(ui, |ui| {
                        ui.label("Register");
                        ui.label("Value");
                        ui.label("Register");
                        ui.label("Value");
                        ui.label("Register");
                        ui.label("Value");
                        ui.label("Register");
                        ui.label("Value");
                        ui.end_row();

                        // iterate over state.reg_file.iter() in chunks of 4 registers
                        for chunk in state.reg_file.all().chunks(4) {
                            for reg in chunk {
                                ui.label(reg.0.to_string());
                                ui.label(format!("0x{:08X} ({})", reg.1, reg.1,));
                            }
                            ui.end_row();
                        }
                    });
            })
    });
}
