use eframe::egui;
use std::fs::File;
use std::io::{self, Write};
use std::path::PathBuf;

// Additional imports
use crossbeam_channel::{unbounded, Receiver, Sender};
use serde::{Deserialize, Serialize};
use rayon::prelude::*;
use memchr::memchr_iter;
use memmap2::MmapOptions;

use egui::{ScrollArea, TextEdit};

struct StringDumperApp {
    // Data related to the application
    strings: Vec<ExtractedString>,
    file_path: Option<PathBuf>,
    search_text: String,
    filtered_strings: Vec<ExtractedString>,

    // Settings
    settings: Settings,
    settings_open: bool,

    // For asynchronous loading
    is_loading: bool,
    progress: f32,
    status_message: String,
    update_channel: Receiver<AppMessage>,
    sender_channel: Sender<AppMessage>,

    // UI State
    selected_strings: Vec<usize>,
    sort_order: SortOrder,
    filter_regex: Option<regex::Regex>,
    preview_data: Option<String>,
}

#[derive(Clone)]
struct ExtractedString {
    value: String,
    offset: usize,
    encoding: String,
}

#[derive(Serialize, Deserialize, Clone)]
struct Settings {
    min_length: usize,
    include_ascii: bool,
    include_utf16le: bool,
    include_utf16be: bool,
    include_utf8: bool,
    theme: Theme,
}

#[derive(Serialize, Deserialize, Clone, PartialEq)]
enum Theme {
    Light,
    Dark,
}
#[derive(PartialEq)]
enum SortOrder {
    OffsetAsc,
    OffsetDesc,
    EncodingAsc,
    EncodingDesc,
    StringAsc,
    StringDesc,
}

enum AppMessage {
    StringsLoaded(io::Result<Vec<ExtractedString>>),
    UpdateProgress(f32),
}

impl Default for StringDumperApp {
    fn default() -> Self {
        let (sender_channel, update_channel) = unbounded();

        let mut app = Self {
            strings: Vec::new(),
            file_path: None,
            search_text: String::new(),
            filtered_strings: Vec::new(),
            settings: Settings {
                min_length: 4,
                include_ascii: true,
                include_utf16le: true,
                include_utf16be: true,
                include_utf8: true,
                theme: Theme::Dark,
            },
            settings_open: false,
            is_loading: false,
            progress: 0.0,
            status_message: String::new(),
            update_channel,
            sender_channel,
            selected_strings: Vec::new(),
            sort_order: SortOrder::OffsetAsc,
            filter_regex: None,
            preview_data: None,
        };

        // Load settings from file
        app.load_settings();

        app
    }
}

impl eframe::App for StringDumperApp {
    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
        self.apply_theme(ctx);
        self.handle_messages();

        self.ui_menu(ctx);

        if self.settings_open {
            self.open_settings(ctx);
        }

        if self.is_loading {
            self.show_loading(ctx);
        }

        egui::SidePanel::left("side_panel").show(ctx, |ui| {
            self.ui_side_panel(ui);
        });

        egui::CentralPanel::default().show(ctx, |ui| {
            self.ui_central_panel(ui);
        });

        egui::TopBottomPanel::bottom("status_bar").show(ctx, |ui| {
            self.ui_status_bar(ui);
        });

        ctx.request_repaint(); // Ensure the UI keeps updating
    }
}

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

    fn handle_messages(&mut self) {
        // Handle messages from background threads
        while let Ok(message) = self.update_channel.try_recv() {
            match message {
                AppMessage::StringsLoaded(result) => {
                    self.is_loading = false;
                    match result {
                        Ok(strings) => {
                            self.strings = strings;
                            self.filter_strings();
                            self.status_message = "Strings loaded successfully.".to_owned();
                        }
                        Err(e) => {
                            self.status_message = format!("Error loading strings: {}", e);
                        }
                    }
                }
                AppMessage::UpdateProgress(progress) => {
                    self.progress = progress;
                }
            }
        }
    }

    fn ui_menu(&mut self, ctx: &egui::Context) {
        use egui::menu;

        egui::TopBottomPanel::top("menu_bar").show(ctx, |ui| {
            menu::bar(ui, |ui| {
                ui.menu_button("File", |ui| {
                    if ui.button("Open File...").clicked() {
                        self.open_file();
                        ui.close_menu();
                    }
                    if ui.button("Save Selected Strings...").clicked() {
                        self.save_strings_dialog();
                        ui.close_menu();
                    }
                    if ui.button("Exit").clicked() {
                        #[cfg(not(target_arch = "wasm32"))]
                        std::process::exit(0);
                    }
                });

                ui.menu_button("Edit", |ui| {
                    if ui.button("Settings").clicked() {
                        self.settings_open = true;
                        ui.close_menu();
                    }
                });

                ui.menu_button("Help", |ui| {
                    if ui.button("About").clicked() {
                        self.show_about(ctx);
                        ui.close_menu();
                    }
                });
            });
        });
    }

    fn ui_side_panel(&mut self, ui: &mut egui::Ui) {
        ui.heading("Controls");
        ui.separator();
        if ui.button("Open File...").clicked() {
            self.open_file();
        }
        if ui.button("Save Selected Strings...").clicked() {
            self.save_strings_dialog();
        }
        ui.separator();
        if ui.button("Settings").clicked() {
            self.settings_open = true;
        }
    }

    fn ui_central_panel(&mut self, ui: &mut egui::Ui) {
        ui.vertical(|ui| {
            ui.heading("Advanced String Dumper");
            ui.separator();

            if let Some(path) = &self.file_path {
                ui.horizontal_wrapped(|ui| {
                    ui.label("File:");
                    ui.monospace(format!("{}", path.display()));
                });
            } else {
                ui.label("No file loaded.");
            }

            ui.horizontal(|ui| {
                ui.label("Search (supports regex):");
                if ui
                    .add_sized(
                        [ui.available_width() * 0.5, 25.0],
                        TextEdit::singleline(&mut self.search_text)
                            .hint_text("Type to search...")
                            .desired_width(f32::INFINITY),
                    )
                    .changed()
                {
                    self.update_filter();
                }
                if ui.button("Clear").clicked() {
                    self.search_text.clear();
                    self.update_filter();
                }
            });

            ui.separator();

            // Display strings in a table with sorting and selection
            self.ui_string_table(ui);
        });
    }

    fn ui_string_table(&mut self, ui: &mut egui::Ui) {
        use egui_extras::{Column, TableBuilder};

        let text_height = ui.text_style_height(&egui::TextStyle::Body);
        let row_height = text_height + 4.0;

        let table = TableBuilder::new(ui)
            .striped(true)
            .column(Column::auto()) // Checkbox column
            .column(Column::initial(200.0).resizable(true)) // Offset
            .column(Column::initial(100.0).resizable(true)) // Encoding
            .column(Column::remainder()); // String value

        table
            .header(20.0, |mut header| {
                header.col(|ui| {
                    // Empty header for checkbox column
                });
                header.col(|ui| {
                    if ui
                        .button(if matches!(self.sort_order, SortOrder::OffsetAsc) {
                            "Offset ^"
                        } else if matches!(self.sort_order, SortOrder::OffsetDesc) {
                            "Offset v"
                        } else {
                            "Offset"
                        })
                        .clicked()
                    {
                        self.toggle_sort_order(SortOrder::OffsetAsc, SortOrder::OffsetDesc);
                    }
                });
                header.col(|ui| {
                    if ui
                        .button(if matches!(self.sort_order, SortOrder::EncodingAsc) {
                            "Encoding v"
                        } else if matches!(self.sort_order, SortOrder::EncodingDesc) {
                            "Encoding ^"
                        } else {
                            "Encoding"
                        })
                        .clicked()
                    {
                        self.toggle_sort_order(SortOrder::EncodingAsc, SortOrder::EncodingDesc);
                    }
                });
                header.col(|ui| {
                    if ui
                        .button(if matches!(self.sort_order, SortOrder::StringAsc) {
                            "String ^"
                        } else if matches!(self.sort_order, SortOrder::StringDesc) {
                            "String v"
                        } else {
                            "String"
                        })
                        .clicked()
                    {
                        self.toggle_sort_order(SortOrder::StringAsc, SortOrder::StringDesc);
                    }
                });
            })
            .body(|body| {
                body.rows(row_height, self.filtered_strings.len(), |mut row| {
                    let idx = row.index();
                    let s = &self.filtered_strings[idx];
                    row.col(|ui| {
                        let selected = self.selected_strings.contains(&idx);
                        if ui
                            .selectable_label(selected, "")
                            .clicked()
                        {
                            if selected {
                                self.selected_strings.retain(|&i| i != idx);
                            } else {
                                self.selected_strings.push(idx);
                            }
                        }
                    });
                    row.col(|ui| {
                        ui.label(format!("0x{:08X}", s.offset));
                    });
                    row.col(|ui| {
                        ui.label(&s.encoding);
                    });
                    row.col(|ui| {
                        if ui
                            .selectable_label(false, &s.value)
                            .clicked()
                        {
                            self.preview_data = Some(s.value.clone());
                        }
                    });
                });
            });
    }

    fn ui_status_bar(&mut self, ui: &mut egui::Ui) {
        ui.horizontal(|ui| {
            if self.is_loading {
                ui.add(egui::ProgressBar::new(self.progress).show_percentage());
                ui.label(&self.status_message);
            } else {
                ui.label(&self.status_message);
            }
        });
    }

    fn open_file(&mut self) {
        if let Some(path) = rfd::FileDialog::new().pick_file() {
            self.file_path = Some(path.clone());
            self.load_strings_async(path);
        }
    }

    fn save_strings_dialog(&mut self) {
        if let Some(path) = rfd::FileDialog::new().save_file() {
            self.save_selected_strings(&path);
            self.status_message = "Strings saved successfully.".to_string();
        }
    }

    fn load_strings_async(&mut self, path: PathBuf) {
        let settings = self.settings.clone();
        let sender = self.sender_channel.clone();
        self.is_loading = true;
        self.progress = 0.0;
        self.status_message = "Loading strings...".to_string();

        std::thread::spawn(move || {
            let result = extract_strings_from_file(&path, &settings, sender.clone());
            sender
                .send(AppMessage::StringsLoaded(result))
                .expect("Failed to send message");
        });
    }

    fn filter_strings(&mut self) {
        self.filtered_strings = if let Some(ref regex) = self.filter_regex {
            self.strings
                .iter()
                .filter(|s| regex.is_match(&s.value))
                .cloned()
                .collect()
        } else {
            self.strings.clone()
        };

        self.apply_sorting();
    }

    fn update_filter(&mut self) {
        self.filter_regex = if !self.search_text.is_empty() {
            match regex::Regex::new(&self.search_text) {
                Ok(regex) => Some(regex),
                Err(e) => {
                    self.status_message = format!("Invalid regex: {}", e);
                    None
                }
            }
        } else {
            None
        };
        self.filter_strings();
    }

    fn toggle_sort_order(&mut self, asc: SortOrder, desc: SortOrder) {
        self.sort_order = match self.sort_order {
            _ if self.sort_order == asc => desc,
            _ if self.sort_order == desc => asc,
            _ => asc,
        };
        self.apply_sorting();
    }

    fn apply_sorting(&mut self) {
        match self.sort_order {
            SortOrder::OffsetAsc => {
                self.filtered_strings.sort_by_key(|s| s.offset);
            }
            SortOrder::OffsetDesc => {
                self.filtered_strings
                    .sort_by_key(|s| std::cmp::Reverse(s.offset));
            }
            SortOrder::EncodingAsc => {
                self.filtered_strings
                    .sort_by_key(|s| s.encoding.clone());
            }
            SortOrder::EncodingDesc => {
                self.filtered_strings
                    .sort_by_key(|s| std::cmp::Reverse(s.encoding.clone()));
            }
            SortOrder::StringAsc => {
                self.filtered_strings.sort_by_key(|s| s.value.clone());
            }
            SortOrder::StringDesc => {
                self.filtered_strings
                    .sort_by_key(|s| std::cmp::Reverse(s.value.clone()));
            }
        }
    }

    fn save_selected_strings(&self, path: &std::path::Path) {
        if let Ok(mut file) = File::create(path) {
            for &idx in &self.selected_strings {
                let s = &self.filtered_strings[idx];
                if let Err(e) = writeln!(file, "{}", s.value) {
                    eprintln!("Error writing to file: {}", e);
                    break;
                }
            }
        } else {
            eprintln!("Failed to create file {}", path.display());
        }
    }

    fn open_settings(&mut self, ctx: &egui::Context) {

        egui::Window::new("Settings")
            .resizable(false)
            .show(ctx, |ui| {
                let mut settings = self.settings.clone(); // Clone inside the closure
                ui.label("Minimum String Length:");
                ui.add(egui::Slider::new(&mut settings.min_length, 1..=20));

                ui.separator();
                ui.label("String Encodings to Include:");
                ui.checkbox(&mut settings.include_ascii, "ASCII");
                ui.checkbox(&mut settings.include_utf8, "UTF-8");
                ui.checkbox(&mut settings.include_utf16le, "UTF-16 Little Endian");
                ui.checkbox(&mut settings.include_utf16be, "UTF-16 Big Endian");

                ui.separator();
                ui.label("Theme:");
                ui.horizontal(|ui| {
                    ui.selectable_value(&mut settings.theme, Theme::Light, "Light");
                    ui.selectable_value(&mut settings.theme, Theme::Dark, "Dark");
                });

                ui.separator();

                if ui.button("Apply").clicked() {
                    self.settings = settings.clone(); // Update self after modifications
                    self.save_settings();
                    if let Some(path) = &self.file_path {
                        self.load_strings_async(path.clone());
                    }
                    self.settings_open = false;
                }
                if ui.button("Cancel").clicked() {
                    self.settings_open = false;
                }
            });
    }

    fn show_loading(&mut self, ctx: &egui::Context) {
        egui::Window::new("Loading")
            .anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0])
            .collapsible(false)
            .resizable(false)
            .show(ctx, |ui| {
                ui.label("Please wait while the strings are being loaded...");
                ui.add(
                    egui::ProgressBar::new(self.progress)
                        .desired_width(300.0)
                        .show_percentage(),
                );
            });
    }

    fn show_about(&self, ctx: &egui::Context) {
        egui::Window::new("About")
            .resizable(false)
            .show(ctx, |ui| {
                ui.label("Advanced String Dumper");
                ui.label("Version 2.0");
                ui.label("Developed with Rust and egui.");
            });
    }

    fn save_settings(&self) {
        if let Ok(json) = serde_json::to_string(&self.settings) {
            std::fs::write("settings.json", json).ok();
        }
    }

    fn load_settings(&mut self) {
        if let Ok(json) = std::fs::read_to_string("settings.json") {
            if let Ok(settings) = serde_json::from_str::<Settings>(&json) {
                self.settings = settings;
            }
        }
    }
}

fn extract_strings_from_file(
    path: &std::path::Path,
    settings: &Settings,
    sender: Sender<AppMessage>,
) -> io::Result<Vec<ExtractedString>> {
    let file = File::open(path)?;
    let metadata = file.metadata()?;
    let file_size = metadata.len();

    let data = unsafe { MmapOptions::new().map(&file)? };

    let strings = extract_strings(&data, settings, file_size, sender);

    Ok(strings)
}

fn extract_strings(
    data: &[u8],
    settings: &Settings,
    file_size: u64,
    sender: Sender<AppMessage>,
) -> Vec<ExtractedString> {
    let mut strings = Vec::new();

    let data_chunks: Vec<&[u8]> = data.chunks(data.len() / num_cpus::get()).collect();

    let extracted_strings: Vec<Vec<ExtractedString>> = data_chunks
        .into_par_iter()
        .map(|chunk| {
            let mut local_strings = Vec::new();
            let mut local_processed: u64 = 0;

            if settings.include_ascii {
                local_strings.extend(extract_ascii_strings(
                    chunk,
                    settings.min_length,
                    &mut local_processed,
                    file_size,
                    &sender,
                ));
            }

            if settings.include_utf8 {
                local_strings.extend(extract_utf8_strings(
                    chunk,
                    settings.min_length,
                    &mut local_processed,
                    file_size,
                    &sender,
                ));
            }

            if settings.include_utf16le {
                local_strings.extend(extract_unicode_strings(
                    chunk,
                    settings.min_length,
                    true,
                    &mut local_processed,
                    file_size,
                    &sender,
                ));
            }

            if settings.include_utf16be {
                local_strings.extend(extract_unicode_strings(
                    chunk,
                    settings.min_length,
                    false,
                    &mut local_processed,
                    file_size,
                    &sender,
                ));
            }

            local_strings
        })
        .collect();

    for vec in extracted_strings {
        strings.extend(vec);
    }

    strings
}

fn extract_ascii_strings(
    data: &[u8],
    min_length: usize,
    processed: &mut u64,
    file_size: u64,
    sender: &Sender<AppMessage>,
) -> Vec<ExtractedString> {
    let mut strings = Vec::new();
    let mut i = 0;

    while i < data.len() {
        if data[i].is_ascii_graphic() || data[i] == b' ' {
            let start = i;
            while i < data.len() && (data[i].is_ascii_graphic() || data[i] == b' ') {
                i += 1;
            }
            if i - start >= min_length {
                let s = String::from_utf8_lossy(&data[start..i]).to_string();
                strings.push(ExtractedString {
                    value: s,
                    offset: start,
                    encoding: "ASCII".to_string(),
                });
            }
        } else {
            i += 1;
        }

        *processed += 1;
        if *processed % 1_000_000 == 0 {
            let progress = *processed as f32 / file_size as f32;
            sender.send(AppMessage::UpdateProgress(progress)).ok();
        }
    }

    strings
}

fn extract_utf8_strings(
    data: &[u8],
    min_length: usize,
    processed: &mut u64,
    file_size: u64,
    sender: &Sender<AppMessage>,
) -> Vec<ExtractedString> {
    let mut strings = Vec::new();
    let mut i = 0;

    while i < data.len() {
        if data[i] >= 0x20 && data[i] != 0x7F {
            let start = i;
            while i < data.len() && data[i] >= 0x20 && data[i] != 0x7F {
                i += 1;
            }
            if i - start >= min_length {
                if let Ok(s) = std::str::from_utf8(&data[start..i]) {
                    strings.push(ExtractedString {
                        value: s.to_string(),
                        offset: start,
                        encoding: "UTF-8".to_string(),
                    });
                }
            }
        } else {
            i += 1;
        }

        *processed += 1;
        if *processed % 1_000_000 == 0 {
            let progress = *processed as f32 / file_size as f32;
            sender.send(AppMessage::UpdateProgress(progress)).ok();
        }
    }

    strings
}

fn extract_unicode_strings(
    data: &[u8],
    min_length: usize,
    little_endian: bool,
    processed: &mut u64,
    file_size: u64,
    sender: &Sender<AppMessage>,
) -> Vec<ExtractedString> {
    let mut strings = Vec::new();
    let mut i = 0;
    let mut current_string = Vec::new();
    let mut offset = 0;

    while i + 1 < data.len() {
        let code_unit = if little_endian {
            u16::from_le_bytes([data[i], data[i + 1]])
        } else {
            u16::from_be_bytes([data[i], data[i + 1]])
        };

        if code_unit == 0 {
            if current_string.len() >= min_length {
                let s = String::from_utf16_lossy(&current_string);
                strings.push(ExtractedString {
                    value: s,
                    offset,
                    encoding: if little_endian {
                        "UTF-16 LE".to_string()
                    } else {
                        "UTF-16 BE".to_string()
                    },
                });
            }
            current_string.clear();
        } else if (32..=126).contains(&code_unit) {
            if current_string.is_empty() {
                offset = i;
            }
            current_string.push(code_unit);
        } else {
            if current_string.len() >= min_length {
                let s = String::from_utf16_lossy(&current_string);
                strings.push(ExtractedString {
                    value: s,
                    offset,
                    encoding: if little_endian {
                        "UTF-16 LE".to_string()
                    } else {
                        "UTF-16 BE".to_string()
                    },
                });
            }
            current_string.clear();
        }

        i += 2;
        *processed += 2;
        if *processed % 1_000_000 == 0 {
            let progress = *processed as f32 / file_size as f32;
            sender.send(AppMessage::UpdateProgress(progress)).ok();
        }
    }

    // Check if there's a string at the end
    if current_string.len() >= min_length {
        let s = String::from_utf16_lossy(&current_string);
        strings.push(ExtractedString {
            value: s,
            offset,
            encoding: if little_endian {
                "UTF-16 LE".to_string()
            } else {
                "UTF-16 BE".to_string()
            },
        });
    }

    strings
}

fn main() {
    let app = StringDumperApp::default();
    let native_options = eframe::NativeOptions::default();
    eframe::run_native("Advanced String Dumper", native_options, Box::new(|_cc| {
        Ok(Box::new(app))
    }));
}

Enhance this code massively. Add new features. Add a smoother, better, more advanced UI/UX. Go beyond normal scopes.