Advance everything to the next level. Make everything massively more advanced.
use std::fs;
use std::path::PathBuf;

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

struct BinaryAnalyzerApp {
    file_path: Option<PathBuf>,
    disassembly: Vec<InstructionEntry>,
    strings: Vec<StringEntry>,
    sections: Vec<SectionEntry>,
    theme: Theme,
    setup_done: bool,
    error_message: Option<String>,
}

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

// Struct to hold disassembly entries with separate fields
struct InstructionEntry {
    address: u64,
    mnemonic: String,
    operands: String,
}

// Struct to hold string entries
struct StringEntry {
    address: u64,
    string: String,
}

// Struct to hold section entries
struct SectionEntry {
    name: String,
    address: u64,
    size: u64,
}

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

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);

            if let Some(error) = &self.error_message {
                ui.colored_label(Color32::RED, error);
            } else if self.disassembly.is_empty() && self.strings.is_empty() {
                ui.label(
                    "No analysis to display. Please select a binary file to analyze.",
                );
            } else {
                egui::CollapsingHeader::new("🔎 Sections")
                    .default_open(true)
                    .show(ui, |ui| {
                        egui::ScrollArea::vertical()
                            .max_height(150.0)
                            .show(ui, |ui| {
                                for section in &self.sections {
                                    ui.horizontal(|ui| {
                                        ui.label(
                                            RichText::new(format!("Name: {}", section.name))
                                                .monospace()
                                                .color(Color32::LIGHT_BLUE),
                                        );
                                        ui.label(
                                            RichText::new(format!(
                                                "Address: 0x{:016X}",
                                                section.address
                                            ))
                                            .monospace()
                                            .color(Color32::WHITE),
                                        );
                                        ui.label(
                                            RichText::new(format!(
                                                "Size: {} bytes",
                                                section.size
                                            ))
                                            .monospace()
                                            .color(Color32::WHITE),
                                        );
                                    });
                                }
                            });
                    });

                ui.separator();

                egui::CollapsingHeader::new("🕷 Strings")
                    .default_open(true)
                    .show(ui, |ui| {
                        egui::ScrollArea::vertical()
                            .max_height(150.0)
                            .show(ui, |ui| {
                                for string_entry in &self.strings {
                                    ui.horizontal_wrapped(|ui| {
                                        ui.label(
                                            RichText::new(format!(
                                                "0x{:016X}: ",
                                                string_entry.address
                                            ))
                                            .monospace()
                                            .color(Color32::LIGHT_BLUE),
                                        );
                                        ui.label(
                                            RichText::new(format!("{:?}", string_entry.string))
                                                .monospace()
                                                .color(Color32::GREEN),
                                        );
                                    });
                                }
                            });
                    });

                ui.separator();

                egui::CollapsingHeader::new("📝 Disassembly")
                    .default_open(true)
                    .show(ui, |ui| {
                        egui::ScrollArea::vertical()
                            .auto_shrink([false, false])
                            .show(ui, |ui| {
                                for instr in &self.disassembly {
                                    ui.horizontal_wrapped(|ui| {
                                        ui.label(
                                            RichText::new(format!("0x{:016X}: ", instr.address))
                                                .monospace()
                                                .color(Color32::LIGHT_BLUE),
                                        );
                                        ui.label(
                                            RichText::new(format!("{:8}", instr.mnemonic))
                                                .monospace()
                                                .color(Color32::YELLOW),
                                        );
                                        ui.label(
                                            RichText::new(&instr.operands)
                                                .monospace()
                                                .color(Color32::WHITE),
                                        );
                                    });
                                }
                            });
                    });
            }
        });
    }

    fn analyze(&mut self) {
        // Clear previous results
        self.disassembly.clear();
        self.strings.clear();
        self.sections.clear();
        self.error_message = None;

        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 = obj_file.is_64();

                        // Collect sections
                        for section in obj_file.sections() {
                            let name = section
                                .name()
                                .unwrap_or("Unknown")
                                .to_string();
                            let address = section.address();
                            let size = section.size();
                            self.sections.push(SectionEntry { name, address, size });
                        }

                        // Extract strings from .rodata and .data sections
                        for section in obj_file.sections() {
                            let name = section.name().unwrap_or("");
                            if name == ".rodata" || name == ".data" {
                                if let Ok(data) = section.data() {
                                    self.extract_strings(data, section.address());
                                }
                            }
                        }

                        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("");

                                                self.disassembly.push(InstructionEntry {
                                                    address: i.address(),
                                                    mnemonic: mnemonic.to_string(),
                                                    operands: op_str.to_string(),
                                                });
                                            }
                                        }
                                        Err(err) => {
                                            self.error_message =
                                                Some(format!("Disassembly failed: {}", err));
                                        }
                                    };
                                }
                                Err(err) => {
                                    self.error_message =
                                        Some(format!("Could not get section data: {}", err));
                                }
                            }
                        } else {
                            self.error_message =
                                Some("No .text section found in the binary.".to_string());
                        }
                    }
                    Err(err) => {
                        self.error_message = Some(format!("Failed to parse binary: {}", err));
                    }
                },
                Err(err) => {
                    self.error_message = Some(format!("Failed to read binary file: {}", err));
                }
            }
        } else {
            self.error_message = Some("No file selected.".to_string());
        }
    }

    fn extract_strings(&mut self, data: &[u8], base_address: u64) {
        let mut index = 0;
        let mut current_string = Vec::new();
        let mut current_address = 0;

        while index < data.len() {
            let byte = data[index];

            if byte.is_ascii_graphic() || byte.is_ascii_whitespace() {
                if current_string.is_empty() {
                    current_address = base_address + index as u64;
                }
                current_string.push(byte);
            } else {
                if current_string.len() >= 4 {
                    if let Ok(string) = String::from_utf8(current_string.clone()) {
                        self.strings.push(StringEntry {
                            address: current_address,
                            string,
                        });
                    }
                }
                current_string.clear();
            }
            index += 1;
        }

        // Check if there's a string at the end
        if current_string.len() >= 4 {
            if let Ok(string) = String::from_utf8(current_string.clone()) {
                self.strings.push(StringEntry {
                    address: current_address,
                    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();
    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))),
    )
}