Add advanced coloring and advanced viewing
What I mean

Like syntax highlighting for instructions
Advanced viewing can mean anything
Also make a complex system that makes display of disassembly way easier on the PC/memory/cpu usage. This is meant to be a minimal program.
use std::fs;
use std::path::PathBuf;

use capstone::prelude::*;
use eframe::egui::{self, FontData, FontDefinitions, FontFamily, TextStyle};
use eframe::egui::RichText;
use object::{Object, ObjectSection};

struct BinaryAnalyzerApp {
    file_path: Option<PathBuf>,
    disassembly: Vec<String>,
    theme: Theme,
    setup_done: bool,
}

#[derive(PartialEq)]
enum Theme {
    Dark,
    Light,
}

impl Default for BinaryAnalyzerApp {
    fn default() -> Self {
        Self {
            file_path: None,
            disassembly: Vec::new(),
            theme: Theme::Dark,
            setup_done: false,
        }
    }
}


impl eframe::App for BinaryAnalyzerApp {
    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
        if !self.setup_done {
            self.set_theme(ctx);
            self.set_custom_fonts(ctx);
            self.setup_done = true;
        }

        self.top_panel(ctx);
        self.central_panel(ctx);
    }
}

impl BinaryAnalyzerApp {
    fn top_panel(&mut self, ctx: &egui::Context) {
        egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
            ui.add_space(5.0);
            ui.horizontal(|ui| {
                ui.heading("🔍 x86 Binary Analyzer");

                ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
                    if ui.button(match self.theme {
                        Theme::Dark => "🌞 Light Theme",
                        Theme::Light => "🌙 Dark Theme",
                    })
                    .clicked()
                    {
                        self.toggle_theme(ctx);
                    }
                });
            });
            ui.add_space(5.0);
        });
    }

    fn central_panel(&mut self, ctx: &egui::Context) {
        egui::CentralPanel::default().show(ctx, |ui| {
            ui.add_space(10.0);

            ui.horizontal(|ui| {
                if ui.button("📂 Select Binary File").clicked() {
                    if let Some(path) = rfd::FileDialog::new()
                        .add_filter("Executable Files", &["exe", "bin", "elf"])
                        .pick_file()
                    {
                        self.file_path = Some(path);
                        self.analyze();
                    }
                }

                if let Some(path) = &self.file_path {
                    ui.label(RichText::new(format!("Selected File: {}", path.display())).italics());
                }

                if ui.button("🔄 Refresh").clicked() {
                    if self.file_path.is_some() {
                        self.analyze();
                    }
                }
            });

            ui.separator();
            ui.add_space(5.0);

            egui::ScrollArea::both().show(ui, |ui| {
                if self.disassembly.is_empty() {
                    ui.label("No disassembly to display. Please select a binary file to analyze.");
                } else {
                    for line in &self.disassembly {
                        ui.monospace(line);
                    }
                }
            });
        });
    }

    fn analyze(&mut self) {
        // Clear previous results
        self.disassembly.clear();

        if let Some(path) = &self.file_path {
            // Read and analyze the binary file
            match fs::read(path) {
                Ok(binary_data) => match object::File::parse(&*binary_data) {
                    Ok(obj_file) => {
                        // Detect architecture (32-bit or 64-bit)
                        let is_64 = match obj_file.is_64() {
                            true => true,
                            false => false,
                        };

                        if let Some(section) = obj_file.section_by_name(".text") {
                            match section.data() {
                                Ok(code) => {
                                    let address = section.address();

                                    // Initialize Capstone
                                    let cs = Capstone::new()
                                        .x86()
                                        .mode(if is_64 {
                                            arch::x86::ArchMode::Mode64
                                        } else {
                                            arch::x86::ArchMode::Mode32
                                        })
                                        .syntax(arch::x86::ArchSyntax::Intel)
                                        .detail(true)
                                        .build()
                                        .expect("Failed to create Capstone object");

                                    // Disassemble instructions
                                    match cs.disasm_all(code, address) {
                                        Ok(instructions) => {
                                            for i in instructions.iter() {
                                                let mnemonic = i.mnemonic().unwrap_or("");
                                                let op_str = i.op_str().unwrap_or("");
                                                let line = format!(
                                                    "0x{:016X}: {:6} {}",
                                                    i.address(),
                                                    mnemonic,
                                                    op_str
                                                );
                                                self.disassembly.push(line);
                                            }
                                        }
                                        Err(err) => {
                                            self.disassembly.push(format!("Disassembly failed: {}", err));
                                        }
                                    };
                                }
                                Err(err) => {
                                    self.disassembly.push(format!("Could not get section data: {}", err));
                                }
                            }
                        } else {
                            self.disassembly.push("No .text section found.".to_string());
                        }
                    }
                    Err(err) => {
                        self.disassembly.push(format!("Failed to parse binary: {}", err));
                    }
                },
                Err(err) => {
                    self.disassembly.push(format!("Failed to read binary file: {}", err));
                }
            }
        } else {
            self.disassembly.push("No file selected.".to_string());
        }
    }

    fn toggle_theme(&mut self, ctx: &egui::Context) {
        self.theme = if self.theme == Theme::Dark {
            Theme::Light
        } else {
            Theme::Dark
        };
        self.set_theme(ctx);
    }

    fn set_theme(&self, ctx: &egui::Context) {
        match self.theme {
            Theme::Dark => {
                ctx.set_visuals(egui::Visuals::dark());
            }
            Theme::Light => {
                ctx.set_visuals(egui::Visuals::light());
            }
        }
    }

    fn set_custom_fonts(&self, ctx: &egui::Context) {
        let mut fonts = FontDefinitions::default();

        // Add a custom font (e.g., "Fira Code")
        fonts.font_data.insert(
            "FiraCode".to_string(),
            FontData::from_static(include_bytes!("../assets/FiraCode-Regular.ttf")),
        );

        // Use the font for monospaced texts
        fonts.families.entry(FontFamily::Monospace).or_default().insert(0, "FiraCode".to_string());

        // Set the modified font definitions
        ctx.set_fonts(fonts);

        // Adjust text sizes
        let mut style = (*ctx.style()).clone();
        style.text_styles = [
            (TextStyle::Heading, egui::FontId::new(24.0, FontFamily::Proportional)),
            (TextStyle::Body, egui::FontId::new(18.0, FontFamily::Proportional)),
            (TextStyle::Monospace, egui::FontId::new(16.0, FontFamily::Monospace)),
            (TextStyle::Button, egui::FontId::new(18.0, FontFamily::Proportional)),
            (TextStyle::Small, egui::FontId::new(14.0, FontFamily::Proportional)),
        ]
        .into();
        ctx.set_style(style);
    }
}

fn main() -> Result<(), eframe::Error> {
    let app = BinaryAnalyzerApp::default(); // 900 700
    let native_options = eframe::NativeOptions {
        viewport: egui::ViewportBuilder::default().with_inner_size([900.0, 700.0]),
        ..Default::default()
    };
    eframe::run_native(
        "Binary Analyzer",
        native_options,
        Box::new(|_cc| Ok(Box::new(app)))
    )
}