use eframe::NativeOptions;
use eframe::egui;
use std::mem;
use std::sync::Arc;
use std::sync::atomic::Ordering;

mod components;
use components::{
    Component, controller::Controller, display::Display, editor::Editor, framebuffer::FrameBuffer,
    memory::MemoryInspector, registers::Registers, serial::Serial,
};

use crate::{MemoryBank, SharedState, config::VERSION};

pub fn run_app(state: Arc<SharedState>, mem_handle: MemoryBank) -> eframe::Result<()> {
    eframe::run_native(
        "Damn Simple Architecture",
        NativeOptions::default(),
        Box::new(|cc| Ok(Box::new(DsaUi::new(cc, state, mem_handle)))),
    )
}

impl eframe::App for DsaUi {
    fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
        // request an update from emulator every cycle
        self.state.update_req.store(true, Ordering::Relaxed);

        // title panel with dsa title
        egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
            ui.with_layout(
                egui::Layout::top_down_justified(egui::Align::Center)
                    .with_main_align(egui::Align::Min),
                |ui| {
                    ui.allocate_space(egui::vec2(0.0, 5.0));
                    ui.heading(format!("Damn Simple Architecture v{} 🚀", VERSION));
                    ui.allocate_space(egui::vec2(0.0, 5.0));
                },
            );
        });

        // menu bar.
        egui::TopBottomPanel::top("menu_bar").show(ctx, |ui| {
            egui::MenuBar::new().ui(ui, |ui| {
                ui.menu_button("Panels", |ui| {
                    ui.set_max_width(300.0);
                    ui.set_min_width(300.0);
                    ui.spacing_mut().button_padding = egui::vec2(10.0, 5.0);

                    for comp in &mut self.components {
                        let name = comp.title().to_string();
                        ui.toggle_value(comp.visible(), name);
                    }
                });
            });
        });

        egui::CentralPanel::default().show(ctx, |ui| {
            for c in &mut self.components {
                let mut visible = *c.visible();
                if visible {
                    egui::Window::new(c.title())
                        .open(&mut visible)
                        .show(ctx, |ui| {
                            c.ui(ui, ctx);
                        });
                }
                *c.visible() = visible;
            }
        });

        ctx.request_repaint_after(std::time::Duration::from_millis(16)); // ~60fps
    }
}

pub struct DsaUi {
    state: Arc<SharedState>,
    mem_handle: MemoryBank,
    components: Vec<Box<dyn Component>>,
}

impl DsaUi {
    pub fn new(
        cc: &eframe::CreationContext,
        state: Arc<SharedState>,
        mem_handle: MemoryBank,
    ) -> Self {
        // components
        let mut components: Vec<Box<dyn Component>> = vec![
            Box::new(Controller::new(state.clone(), mem_handle.clone())),
            Box::new(Display::new(80, 25, 0x20000, mem_handle.clone())),
            Box::new(FrameBuffer::new(320, 240, 0x30000, mem_handle.clone())),
            Box::new(Registers::new(state.clone())),
            Box::new(Serial::new(0x40000, state.clone(), mem_handle.clone())),
            Box::new(Editor::new(state.clone(), mem_handle.clone())),
            Box::new(MemoryInspector::new(mem_handle.clone())),
        ];
        components.iter_mut().for_each(|c| *c.visible() = false);

        Self::configure_appearance(&cc.egui_ctx);
        let app = Self {
            state,
            mem_handle,
            components,
        };
        app
    }

    fn configure_appearance(ctx: &egui::Context) {
        // configure appearance of UI elements
        let mut visuals = egui::Visuals::dark();

        // --- your existing settings ---
        visuals.window_fill = egui::Color32::from_rgb(20, 20, 20);
        visuals.panel_fill = egui::Color32::from_rgb(20, 20, 20);
        visuals.widgets.inactive.fg_stroke =
            egui::Stroke::from((1.0, egui::Color32::from_rgb(255, 255, 255)));
        visuals.widgets.inactive.bg_stroke =
            egui::Stroke::from((1.0, egui::Color32::from_rgb(60, 60, 60)));
        visuals.widgets.inactive.corner_radius = egui::CornerRadius::from(4);
        visuals.widgets.inactive.bg_fill = egui::Color32::from_rgb(20, 20, 20);
        visuals.widgets.inactive.weak_bg_fill = egui::Color32::from_rgb(20, 20, 20);
        visuals.widgets.inactive.expansion = 1.0;

        // tile resize handle
        visuals.window_stroke = egui::Stroke::new(1.0, egui::Color32::from_rgb(45, 45, 45));
        visuals.extreme_bg_color = egui::Color32::from_rgb(20, 20, 20);

        ctx.set_visuals(visuals);

        let mut fonts = egui::FontDefinitions::default();
        fonts.font_data.insert(
            "JetBrains Mono Nerd Font".to_string(),
            std::sync::Arc::new(egui::FontData::from_static(include_bytes!(
                "../../font/JetBrainsMonoNerdFontMono_Regular.ttf",
            ))),
        );

        fonts
            .families
            .entry(egui::FontFamily::Proportional)
            .or_default()
            .insert(0, "JetBrains Mono Nerd Font".to_string());

        fonts
            .families
            .entry(egui::FontFamily::Monospace)
            .or_default()
            .insert(0, "JetBrains Mono Nerd Font".to_string());

        ctx.set_fonts(fonts);
    }
}
