use eframe::{egui, Theme};
use egui::{Color32, RichText};
use std::fs::File;
use std::io::Read;

use capstone::prelude::*;
use goblin::Object;
use rfd::FileDialog;

#[derive(PartialEq, Clone, Debug)]
enum Architecture {
    X86,
    X86_64,
    ARM,
    ARM64,
}

#[derive(Clone)]
struct BinaryAnalyzerApp {
    // Existing fields
    instructions: Vec<Instruction>,
    error_message: Option<String>,
    arch: Architecture,
    file_path: Option<String>,
    search_query: String,
    filtered_instructions: Vec<Instruction>,
    // New fields
    sections: Vec<Section>,
    symbols: Vec<Symbol>,
    selected_instruction: Option<Instruction>,
    disassembly_options: DisassemblyOptions,
}

#[derive(Clone)]
struct Instruction {
    address: u64,
    bytes: Vec<u8>,
    mnemonic: String,
    op_str: String,
}

#[derive(Clone)]
struct Section {
    name: String,
    start: u64,
    size: u64,
    bytes: Vec<u8>,
}

#[derive(Clone)]
struct Symbol {
    name: String,
    address: u64,
}

#[derive(Default, Clone)]
struct DisassemblyOptions {
    show_bytes: bool,
    syntax: SyntaxPreference,
}

#[derive(PartialEq, Clone, Default)]
enum SyntaxPreference {
    #[default]
    Intel,
    Att,
}

impl Default for BinaryAnalyzerApp {
    fn default() -> Self {
        Self {
            instructions: Vec::new(),
            error_message: None,
            arch: Architecture::X86, // Default to x86
            file_path: None,
            search_query: String::new(),
            filtered_instructions: Vec::new(),
            sections: Vec::new(),
            symbols: Vec::new(),
            selected_instruction: None,
            disassembly_options: DisassemblyOptions::default(),
        }
    }
}

impl eframe::App for BinaryAnalyzerApp {
    fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
        // Top panel for header
        egui::TopBottomPanel::top("header").show(ctx, |ui| {
            ui.horizontal(|ui| {
                ui.heading("Advanced Binary Analyzer");
            });
        });

        // Left panel for controls
        egui::SidePanel::left("controls_panel")
            .resizable(true)
            .default_width(250.0)
            .show(ctx, |ui| {
                ui.group(|ui| {
                    ui.heading("Controls");

                    if ui.button("Load Binary").clicked() {
                        self.load_binary();
                    }

                    ui.separator();

                    ui.label("Architecture:");
                    egui::ComboBox::from_id_source("arch_select")
                        .selected_text(format!("{:?}", self.arch))
                        .show_ui(ui, |ui| {
                            ui.selectable_value(&mut self.arch, Architecture::X86, "x86 (32-bit)");
                            ui.selectable_value(&mut self.arch, Architecture::X86_64, "x86_64 (64-bit)");
                            ui.selectable_value(&mut self.arch, Architecture::ARM, "ARM (32-bit)");
                            ui.selectable_value(&mut self.arch, Architecture::ARM64, "ARM64 (64-bit)");
                        });

                    if ui.button("Update Disassembly").clicked() {
                        self.update_disassembly();
                    }

                    ui.separator();

                    ui.label("Search Instructions:");
                    if ui.text_edit_singleline(&mut self.search_query).changed() {
                        self.update_filtered_instructions();
                    }

                    ui.separator();

                    ui.collapsing("Disassembly Options", |ui| {
                        ui.checkbox(&mut self.disassembly_options.show_bytes, "Show Bytes");
                        ui.label("Syntax:");
                        ui.horizontal(|ui| {
                            ui.radio_value(
                                &mut self.disassembly_options.syntax,
                                SyntaxPreference::Intel,
                                "Intel",
                            );
                            ui.radio_value(
                                &mut self.disassembly_options.syntax,
                                SyntaxPreference::Att,
                                "AT&T",
                            );
                        });
                    });

                    ui.separator();

                    if ui.button("Export Disassembly").clicked() {
                        self.export_disassembly();
                    }
                });

                if let Some(error) = &self.error_message {
                    ui.add_space(10.0);
                    ui.group(|ui| {
                        ui.colored_label(Color32::RED, error);
                    });
                }
            });

        // Right panel for sections and symbols
        egui::SidePanel::right("side_panel")
            .resizable(true)
            .default_width(250.0)
            .show(ctx, |ui| {
                ui.heading("Sections");
                egui::ScrollArea::vertical().show(ui, |ui| {
                    for section in &self.sections {
                        if ui.collapsing(&section.name, |ui| {
                            ui.label(format!("Address: 0x{:08x}", section.start));
                            ui.label(format!("Size: {} bytes", section.size));
                            if ui.button("View Hex Dump").clicked() {
                                self.view_hex_dump(&section.bytes);
                            }
                        }).header_response.clicked() {
                            // Handle section clicks if needed
                        }
                    }
                });

                ui.separator();

                ui.heading("Symbols");
                egui::ScrollArea::vertical().show(ui, |ui| {
                    for symbol in &self.symbols {
                        ui.label(format!(
                            "{} - 0x{:08x}",
                            symbol.name, symbol.address
                        ));
                    }
                });
            });

        // Central panel for disassembly
        egui::CentralPanel::default().show(ctx, |ui| {
            if let Some(path) = &self.file_path {
                ui.horizontal(|ui| {
                    ui.label("Loaded File:");
                    ui.monospace(path);
                });
            }

            ui.separator();

            ui.heading("Disassembled Instructions");
            egui::ScrollArea::both().show(ui, |ui| {
                egui::Grid::new("disassembly_grid")
                    .striped(true)
                    .show(ui, |ui| {
                        for instruction in &self.filtered_instructions {
                            ui.horizontal(|ui| {
                                let addr = format!("0x{:08x}", instruction.address);
                                if ui
                                    .selectable_label(
                                        self.selected_instruction
                                            .as_ref()
                                            .map_or(false, |instr| instr.address == instruction.address),
                                        &addr,
                                    )
                                    .clicked()
                                {
                                    self.selected_instruction = Some(instruction.clone());
                                }

                                if self.disassembly_options.show_bytes {
                                    let bytes_str = instruction
                                        .bytes
                                        .iter()
                                        .map(|b| format!("{:02x}", b))
                                        .collect::<Vec<_>>()
                                        .join(" ");
                                    ui.label(bytes_str);
                                }

                                // Syntax Highlighting
                                let mnemonic = RichText::new(&instruction.mnemonic)
                                    .color(Color32::LIGHT_BLUE);
                                ui.label(mnemonic);
                                ui.label(&instruction.op_str);
                            });
                            ui.end_row();
                        }
                    });
            });

            ui.separator();

            if let Some(instruction) = &self.selected_instruction {
                ui.group(|ui| {
                    ui.heading("Instruction Details");
                    ui.label(format!("Address: 0x{:08x}", instruction.address));
                    ui.label(format!("Bytes: {:?}", instruction.bytes));
                    ui.label(format!("Mnemonic: {}", instruction.mnemonic));
                    ui.label(format!("Operands: {}", instruction.op_str));
                    // More detailed information can be added here
                });
            }
        });

        // Control Flow Graph Visualization (simplified example)
        egui::Window::new("Control Flow Graph")
            .open(&mut true)
            .show(ctx, |ui| {
                ui.label("Basic Control Flow Graph visualization would go here.");
                // Placeholder for actual CFG visualization
            });
    }
}

impl BinaryAnalyzerApp {
    fn load_binary(&mut self) {
        self.error_message = None; // Clear previous errors
        self.instructions.clear();
        self.filtered_instructions.clear();
        self.sections.clear();
        self.symbols.clear();

        if let Some(path) = FileDialog::new()
            .add_filter("Executable Files", &["exe", "bin", "out", "elf"])
            .pick_file()
        {
            self.file_path = Some(path.display().to_string());
            match File::open(&path) {
                Ok(mut file) => {
                    let mut buffer = Vec::new();
                    if let Err(e) = file.read_to_end(&mut buffer) {
                        self.error_message = Some(format!("Failed to read file: {}", e));
                        return;
                    }

                    // Use goblin to parse the binary format
                    match Object::parse(&buffer) {
                        Ok(object) => {
                            match object {
                                Object::Elf(elf) => {
                                    self.arch = if elf.is_64 {
                                        Architecture::X86_64
                                    } else {
                                        Architecture::X86
                                    };
                                    let entry = elf.entry;
                                    self.sections = elf
                                        .section_headers
                                        .iter()
                                        .filter(|s| s.sh_size > 0)
                                        .map(|s| Section {
                                            name: elf.shdr_strtab.get_at(s.sh_name).unwrap_or("").to_string(),
                                            start: s.sh_addr,
                                            size: s.sh_size,
                                            bytes: buffer[s.file_range().unwrap()].to_vec(),
                                        })
                                        .collect::<Vec<_>>();
                                    self.symbols = elf
                                        .syms
                                        .iter()
                                        .map(|sym| Symbol {
                                            name: elf.strtab.get_at(sym.st_name).unwrap_or("").to_string(),
                                            address: sym.st_value,
                                        })
                                        .collect();

                                    self.disassemble(&buffer, entry);
                                }
                                Object::PE(pe) => {
                                    self.arch = if pe.is_64 {
                                        Architecture::X86_64
                                    } else {
                                        Architecture::X86
                                    };
                                    let entry = pe.entry as u64;
                                    self.sections = pe
                                    .sections
                                    .iter()
                                    .map(|s| Section {
                                        name: String::from_utf8_lossy(&s.name[..]).to_string(),
                                        start: s.virtual_address as u64,
                                        size: s.virtual_size as u64,
                                        bytes: buffer[s.pointer_to_raw_data as usize..(s.pointer_to_raw_data + s.size_of_raw_data) as usize].to_vec(),
                                    })
                                    .collect::<Vec<_>>();
                                    self.symbols = vec![]; // PE symbol parsing can be added here

                                    self.disassemble(&buffer, entry);
                                }
                                Object::Mach(mach) => {
                                    self.error_message = Some("Mach-O binaries are not supported yet.".to_string());
                                }
                                _ => {
                                    self.error_message = Some("Unsupported binary format".to_string());
                                }
                            }
                        }
                        Err(e) => {
                            self.error_message = Some(format!("Failed to parse binary: {}", e));
                        }
                    }
                }
                Err(e) => {
                    self.error_message = Some(format!("Failed to open file: {}", e));
                }
            }
        }
    }

    fn disassemble(&mut self, code_bytes: &[u8], entry_point: u64) {
        let cs = match self.create_capstone() {
            Ok(cs) => cs,
            Err(e) => {
                self.error_message = Some(format!("Capstone error: {}", e));
                return;
            }
        };

        match cs.disasm_all(code_bytes, entry_point) {
            Ok(insns) => {
                self.instructions.clear();
                for i in insns.iter() {
                    let instruction = Instruction {
                        address: i.address(),
                        bytes: i.bytes().to_vec(),
                        mnemonic: i.mnemonic().unwrap_or("").to_string(),
                        op_str: i.op_str().unwrap_or("").to_string(),
                    };
                    self.instructions.push(instruction);
                }

                self.update_filtered_instructions();
            }
            Err(e) => {
                self.error_message = Some(format!("Disassembly error: {}", e));
            }
        };
    }

    fn create_capstone(&self) -> Result<Capstone, capstone::Error> {
        let syntax = match self.disassembly_options.syntax {
            SyntaxPreference::Intel => arch::x86::ArchSyntax::Intel,
            SyntaxPreference::Att => arch::x86::ArchSyntax::Att,
        };
        match self.arch {
            Architecture::X86 => Capstone::new()
                .x86()
                .mode(arch::x86::ArchMode::Mode32)
                .syntax(syntax)
                .detail(true)
                .build(),
            Architecture::X86_64 => Capstone::new()
                .x86()
                .mode(arch::x86::ArchMode::Mode64)
                .syntax(syntax)
                .detail(true)
                .build(),
            Architecture::ARM => Capstone::new()
                .arm()
                .mode(arch::arm::ArchMode::Arm)
                .detail(true)
                .build(),
            Architecture::ARM64 => Capstone::new()
                .arm64()
                .mode(arch::arm64::ArchMode::Arm)
                .detail(true)
                .build(),
        }
    }

    fn update_disassembly(&mut self) {
        if let Some(path) = &self.file_path {
            match File::open(path) {
                Ok(mut file) => {
                    let mut buffer = Vec::new();
                    if let Err(e) = file.read_to_end(&mut buffer) {
                        self.error_message = Some(format!("Failed to read file: {}", e));
                        return;
                    }

                    let entry_point = 0x0; // Default entry point if not parsed
                    self.disassemble(&buffer, entry_point);
                }
                Err(e) => {
                    self.error_message = Some(format!("Failed to open file: {}", e));
                }
            }
        }
    }

    fn update_filtered_instructions(&mut self) {
        if self.search_query.is_empty() {
            self.filtered_instructions = self.instructions.clone();
        } else {
            let query = self.search_query.to_lowercase();
            self.filtered_instructions = self
                .instructions
                .iter()
                .filter(|instr| {
                    instr.mnemonic.to_lowercase().contains(&query)
                        || instr.op_str.to_lowercase().contains(&query)
                })
                .cloned()
                .collect();
        }
    }

    fn view_hex_dump(&self, bytes: &[u8]) {
        // Implement a window to show the hex dump of the section's bytes
        // This is a placeholder for the actual implementation
    }

    fn export_disassembly(&mut self) {
        if let Some(path) = FileDialog::new()
            .set_directory(".")
            .set_file_name("disassembly.txt")
            .save_file()
        {
            let mut content = String::new();
            for instr in &self.filtered_instructions {
                let line = format!(
                    "0x{:08x}: {}\t{}\n",
                    instr.address, instr.mnemonic, instr.op_str
                );
                content.push_str(&line);
            }
            if let Err(e) = std::fs::write(path, content) {
                self.error_message = Some(format!("Failed to export disassembly: {}", e));
            }
        }
    }
}

fn main() {
    let app: BinaryAnalyzerApp = BinaryAnalyzerApp::default();
    let options = eframe::NativeOptions {
        default_theme: Theme::Dark,
        ..Default::default()
    };
    eframe::run_native("Advanced Binary Analyzer", options, Box::new(|_cc| {
        Ok(Box::new(app))
    }))
    .unwrap();
}

Fix the code only displaying ~7 lines of disassembly. Add real CFG. Add hex dumps and more advanced section info.