use std::fmt::Write;
use std::fs::{self};
use std::io::Cursor;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::{
    ffi::OsStr,
    path::{Path, PathBuf},
};

pub mod syntax;

use super::Component;
use assembler::prelude::Assembler;
use common::formats::binary_dse::DseExecutable;
use common::prelude::Instruction;
use egui::{Align, Context, Key, Layout, Ui};
use egui_code_editor::{CodeEditor, ColorTheme, Completer, Syntax};
use egui_extras::{Column, TableBuilder};
use egui_file::FileDialog;
use emulator_core::{
    memory::ram::{MemoryBank, Page},
    processor::state::SharedState
};
use crate::ui::components::memory::MemoryViewer;

pub struct Editor {
    state: Arc<SharedState>,
    memory: MemoryBank,

    // editor state
    path: Option<PathBuf>,
    unsaved: bool,
    text: String,
    buffer: String,

    // editor widget
    editor: CodeEditor,
    completer: Completer,
    syntax: Syntax,

    // output / loading
    output: Vec<u8>,
    viewer: MemoryViewer,
    load_offset: u32,
    offset_str: String,

    // file dialogs
    open_file_dialog: Option<FileDialog>,
    save_file_dialog: Option<FileDialog>,

    // other
    visible: bool,
    show_output: bool,
    error: Option<String>,
}

impl Component for Editor {
    fn title(&self) -> &'static str {
        "Editor"
    }

    fn visible(&mut self) -> &mut bool {
        &mut self.visible
    }

    fn ui(&mut self, ui: &mut Ui) {
        if self.buffer != self.text {
            self.unsaved = true;
        }

        ui.vertical(|ui| {
            // Top bar

            if ui.input(|i| i.key_pressed(Key::S) && i.modifiers.ctrl) {
                self.save();
            }

            self.render_toolbar(ui);

            ui.add_space(4.0); // Add some spacing instead of just a separator
            ui.separator();

            let remaining_height = f32::max(ui.available_height() - 100.0, 100.0);

            ui.allocate_ui_with_layout(
                egui::Vec2::new(ui.available_width(), remaining_height),
                Layout::left_to_right(Align::Min),
                |ui| {
                    if self.show_output {
                        egui::Panel::right("Editor Output").resizable(false).show_inside(ui, |ui| {
                            self.viewer.ui(ui, self.output.as_slice(), 0);
                        });
                    }
                    self.render_editor(ui);
                },
            );

            self.render_bottom_bar(ui);
        });
    }
}

impl Editor {
    #[must_use]
    pub fn new(state: Arc<SharedState>, memory: MemoryBank) -> Self {
        Self {
            memory,
            state,
            path: None,
            text: String::new(),
            buffer: String::new(),
            completer: Completer::new_with_syntax(&Syntax::default()).with_user_words(),
            syntax: Syntax::default(),
            editor: CodeEditor::default()
                .id_source("editor")
                .with_fontsize(12.0)
                .with_rows(0)
                .with_theme(THEME)
                .with_numlines(true),
            output: Vec::new(),
            viewer: MemoryViewer::new(),
            unsaved: true,
            visible: false,
            load_offset: 0,
            offset_str: String::new(),
            error: None,
            open_file_dialog: None,
            save_file_dialog: None,
            show_output: false,
        }
    }

    fn filename(&self) -> &str {
        if let Some(path) = &self.path {
            return path
                .file_name()
                .unwrap_or_else(|| OsStr::new("Unnamed!"))
                .to_str()
                .unwrap_or_else(|| unreachable!("File name should be valid UTF-8."));
        }
        "Unnamed!"
    }

    fn extension(&self) -> &str {
        if let Some(path) = &self.path {
            return path
                .extension()
                .unwrap_or_else(|| OsStr::new("Unknown!"))
                .to_str()
                .unwrap_or_else(|| unreachable!("File name should be valid UTF-8."));
        }
        "Unknown!"
    }

    fn save(&mut self) {
        if self.open_file_dialog.is_some() {
            // TODO: Flash an error stating you can only have one menu open at once.
            self.open_file_dialog = None;
        }

        if let Some(path) = &self.path {
            // Save to existing path
            self.buffer = self.text.clone();

            let text = if path.extension().is_some_and(|ext| ext == "dsb") {
                let mut res = Vec::new();
                for line in self.text.lines() {
                    for line in line.split_whitespace() {
                        match u32::from_str_radix(line, 16) {
                            Ok(num) => res.push(num),
                            Err(e) => {
                                self.error = Some(format!("Failed to parse file: {e}"));
                                return;
                            }
                        }
                    }
                }
                res.into_iter()
                    .flat_map(u32::to_be_bytes)
                    .collect::<Vec<u8>>()
            } else {
                self.text.as_bytes().to_vec()
            };

            if let Err(why) = std::fs::write(path, text) {
                self.error = Some(format!("Failed to save file: {why}"));
            } else {
                self.unsaved = false;
            }
        } else {
            // Open the save dialog.
            let work_dir = std::env::current_dir().unwrap_or_else(|_| {
                dirs::home_dir()
                    .expect("Couldn't get your current working directory or your home directory.")
            });

            if self.save_file_dialog.is_none() {
                let mut dialog = FileDialog::save_file().initial_path(work_dir);
                dialog.open();
                self.save_file_dialog = Some(dialog);
            }
        }
    }

    fn open(&mut self) {
        let work_dir = std::env::current_dir().unwrap_or_else(|_| {
            dirs::home_dir()
                .expect("Couldn't get your current working directory or your home directory.")
        });

        if self.save_file_dialog.is_some() {
            // TODO: Flash an error stating you can only have one menu open at once.
            self.save_file_dialog = None;
        }

        if self.open_file_dialog.is_none() {
            let mut dialog = FileDialog::open_file();
            if let Some(p) = &self.path
                && let Some(path) = p.parent().map(Path::to_path_buf)
            {
                dialog.set_path(path);
            } else {
                dialog.set_path(work_dir);
            }

            dialog.open();
            self.open_file_dialog = Some(dialog);
        }

         self.syntax = match self.extension() {
            "dsa" => syntax::dsa(),
            "dsc" => syntax::dsc(),
            "rs" => Syntax::rust(),
            _ => Syntax::default(),
        };

        self.completer = Completer::new_with_syntax(&self.syntax).with_user_words();
    }

    fn handle_file_dialogs(&mut self, ui: &Ui) {
        // Handle open dialog
        if let Some(dialog) = &mut self.open_file_dialog
            && dialog.show(ui).selected()
        {
            if let Some(file) = dialog.path() {
                // check if the file is a binary file
                if file.extension().is_some_and(|ext| ext == "dsb") {
                    match std::fs::read(file) {
                        Ok(content) => {
                            let mut res = String::new();
                            for (i, b) in content.iter().enumerate() {
                                _ = write!(res, "{b:02x}");
                                if i % 4 == 3 {
                                    res.push('\n');
                                }
                            }
                            self.text = res.clone();
                            self.buffer = res;
                            self.path = Some(file.to_path_buf());
                            self.unsaved = false;
                            self.error = None;
                        }
                        Err(e) => {
                            self.error = Some(format!("Failed to read file: {e}"));
                        }
                    }
                } else {
                    match std::fs::read_to_string(file) {
                        Ok(content) => {
                            self.text = content.clone();
                            self.buffer = content;
                            self.path = Some(file.to_path_buf());
                            self.unsaved = false;
                            self.error = None;
                        }
                        Err(e) => {
                            self.error = Some(format!("Failed to read file: {e}"));
                        }
                    }
                }
            }
            self.open_file_dialog = None;
        }

        // Handle save dialog
        if let Some(dialog) = &mut self.save_file_dialog
            && dialog.show(ui).selected()
        {
            if let Some(file) = dialog.path() {
                self.buffer = self.text.clone();

                let content = if file.extension().is_some_and(|ext| ext == "dsb") {
                    let mut res = Vec::new();
                    for line in self.text.lines() {
                        for line in line.split_whitespace() {
                            match u32::from_str_radix(line, 16) {
                                Ok(num) => res.push(num),
                                Err(e) => {
                                    self.error = Some(format!("Failed to parse file: {e}"));
                                    return;
                                }
                            }
                        }
                    }
                    res.into_iter()
                        .flat_map(u32::to_be_bytes)
                        .collect::<Vec<u8>>()
                } else {
                    self.text.clone().as_bytes().to_vec()
                };

                match std::fs::write(file, content) {
                    Ok(()) => {
                        self.path = Some(file.to_path_buf());
                        self.unsaved = false;
                        self.error = None;
                    }
                    Err(e) => {
                        self.error = Some(format!("Failed to save file: {e}"));
                    }
                }
            }
            self.save_file_dialog = None;
        }
    }

    fn render_editor(&mut self, ui: &mut Ui) {
        let available_width = ui.available_width();
        let mut editor = self.editor.clone().desired_width(available_width);
        editor.show_with_completer(ui, &mut self.text, &self.syntax, &mut self.completer);
    }

    fn render_bottom_bar(&self, ui: &mut Ui) {
        ui.horizontal(|ui| {
            // error display
            ui.label(
                egui::RichText::new(self.error.clone().unwrap_or_default())
                    .color(egui::Color32::RED),
            );
        });
    }

    fn build(&mut self) {
        if let Some(path) = &self.path {
            match path.extension().and_then(|ext| ext.to_str()) {
                Some("dsa") => {
                    let mut assembler = Assembler::new(path);
                    assembler.start(());

                    // Or block until done
                    self.output = match assembler.output() {
                        Ok(instructions) => instructions,
                        Err(e) => {
                            self.error = Some(e.to_string());
                            return;
                        }
                    };
                }
                // Some("dsc") => {
                //     let dsa_path = Path::new(path).with_extension("dsa");
                //     let mut compiler = Compiler::new(path);

                //     let is_lib = false;
                //     compiler.start(is_lib);

                //     if let Err(e) = compiler.write_result(&dsa_path) {
                //         self.error = Some(e.to_string());
                //         return;
                //     }

                //     let mut assembler = Assembler::new(&dsa_path);
                //     assembler.start(());

                //     // Or block until done
                //     self.output = match assembler.output() {
                //         Ok(instructions) => instructions,
                //         Err(e) => {
                //             self.error = Some(format!("Assembler error: {e}"));
                //             return;
                //         }
                //     };
                // }
                Some("dsb") => {
                    if let Ok(bytes) = fs::read(path) {
                        self.output = bytes;
                    } else {
                        self.error = Some("Failed to read file".to_string());
                    }
                }
                _ => {
                    self.error = Some(format!("Invalid file type: {}", self.filename()));
                }
            }
        }
    }

    fn render_toolbar(&mut self, ui: &mut Ui) {
        self.handle_file_dialogs(ui);

        ui.horizontal(|ui| {
            ui.label(format!("File type: {}", self.extension()));
            ui.label(format!("Filename: {}", self.filename()));
            ui.label(format!("Unsaved: {}", self.unsaved));

            // number of lines in the file
            ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
                let line_count = self.text.lines().count();
                ui.label(format!("Lines: {line_count}"));
            });
        });

        ui.horizontal(|ui| {
            ui.spacing_mut().button_padding = egui::vec2(8.0, 4.0);
            ui.spacing_mut().item_spacing.x = 6.0;

            // Opens a file
            // ui.add_enabled(false, egui::Button::new("Open"));
            if ui.button("Open").clicked() {
                self.open();
            }

            // Saves the current file
            // ui.add_enabled(false, egui::Button::new("Save"));
            if ui.button("Save").clicked() {
                self.save();
            }

            // // builds the current file
            // ui.add_enabled(false, egui::Button::new("Build"));
            if ui.button("Build").clicked() && !self.unsaved {
                self.build();
            }

            // // Loads the generated binary into the assembler at the provided offset
            // ui.add_enabled(false, egui::Button::new("Load"));
            if ui.button("Load").clicked() {
                if self.error.is_some() {
                    self.error = Some("Can't load program at invalid offset!".to_string());
                }

                let mut reader = Cursor::new(self.output.clone());
                let Ok(executable) = DseExecutable::read(&mut reader) else {
                    self.error = Some("Failed to read program!".to_string());
                    return
                };

                let image: Vec<Page> = Page::paginate(&executable.to_memory_image()).collect();

                self.state.reseting.store(true, Ordering::Relaxed);

                self.memory.clear();
                for (i, page) in image.iter().enumerate() {
                    self.memory.write_page(i as u32 * 4096, &page);
                }
                self.state.reseting.store(false, Ordering::Relaxed);
            }

            // Entry widget to enter a load offset
            if ui.text_edit_singleline(&mut self.offset_str).changed() {
                if let Some(offset) = parse_address(&self.offset_str) {
                    self.load_offset = offset;
                    self.error = None;
                } else {
                    self.error = Some("Invalid offset".to_string());
                }
            }

            ui.checkbox(&mut self.show_output, "Show Output");
        });
    }
}

fn parse_address(address: &str) -> Option<u32> {
    address.strip_prefix("0x").map_or_else(
        || {
            address.strip_prefix("0b").map_or_else(
                || {
                    address.strip_prefix("0o").map_or_else(
                        || address.parse::<u32>().ok(),
                        |oct| u32::from_str_radix(oct, 8).ok(),
                    )
                },
                |bin| u32::from_str_radix(bin, 2).ok(),
            )
        },
        |hex| u32::from_str_radix(hex, 16).ok(),
    )
}

pub const THEME: ColorTheme = ColorTheme {
    name: "Theme",
    dark: true,
    bg: "1b1b1b",
    cursor: "de5252",      // fg4
    selection: "28323B",   // bg2
    comments: "444444",    // gray1
    functions: "7CCCC7",   // green1
    keywords: "6C81E0",    // red1
    literals: "A3ABFF",    // fg1
    numerics: "8A46CF",    // purple1
    punctuation: "99C9C9", // orange1
    strs: "618c84",        // aqua1
    types: "B8B9D4",       // yellow1
    special: "de5252",     // blue1
};
//
// struct Output {
//     pub data: Vec<u8>,
//
//     show_bytes: bool,
//     show_words: bool,
//     show_ascii: bool,
//     show_instruction: bool,
//     show_decimal: bool,
// }
//
// impl Output {
//     fn new() -> Self {
//         Self {
//             data: vec![],
//             show_bytes: true,
//             show_words: true,
//             show_ascii: true,
//             show_instruction: true,
//             show_decimal: true,
//         }
//     }
//
//     fn mut_data(&mut self) -> &mut Vec<u8> {
//         &mut self.data
//     }
//
//     fn data(&self) -> &Vec<u8> {
//         &self.data
//     }
//
//     fn ui(&mut self, ui: &mut Ui) {
//         // Output area with synchronized scrolling
//         egui::ScrollArea::vertical()
//             .id_salt("output_scroll")
//             .max_width(400.0)
//             .show(ui, |ui| {
//                 if self.data.is_empty() {
//                     ui.label(
//                         egui::RichText::new("No output data")
//                             .font(egui::FontId::monospace(12.0))
//                             .color(egui::Color32::GRAY),
//                     );
//                     return;
//                 }
//
//                 ui.horizontal(|ui| {
//                     ui.toggle_value(&mut self.show_bytes, "Bytes");
//                     ui.toggle_value(&mut self.show_words, "Words");
//                     ui.toggle_value(&mut self.show_decimal, "Decimal");
//                     ui.toggle_value(&mut self.show_ascii, "Ascii");
//                     ui.toggle_value(&mut self.show_instruction, "Instructions");
//                 });
//
//                 ui.separator();
//
//                 let mut table = TableBuilder::new(ui)
//                     .resizable(false) // makes all columns resizable at once
//                     .column(Column::initial(55.0)); // addr
//
//                 if self.show_bytes {
//                     table = table.column(Column::initial(90.0));
//                 }
//                 if self.show_words {
//                     table = table.column(Column::initial(75.0));
//                 }
//                 if self.show_decimal {
//                     table = table.column(Column::initial(80.0));
//                 }
//                 if self.show_ascii {
//                     table = table.column(Column::initial(50.0));
//                 }
//                 if self.show_instruction {
//                     table = table.column(Column::initial(200.0).clip(true));
//                 }
//
//                 table
//                     .header(20.0, |mut header| {
//                         header.col(|ui| {
//                             ui.label("Addr");
//                         });
//                         if self.show_bytes {
//                             header.col(|ui| {
//                                 ui.label("Bytes");
//                             });
//                         }
//                         if self.show_words {
//                             header.col(|ui| {
//                                 ui.label("Word");
//                             });
//                         }
//                         if self.show_decimal {
//                             header.col(|ui| {
//                                 ui.label("Decimal");
//                             });
//                         }
//                         if self.show_ascii {
//                             header.col(|ui| {
//                                 ui.label("Ascii");
//                             });
//                         }
//                         if self.show_instruction {
//                             header.col(|ui| {
//                                 ui.label("Instruction");
//                             });
//                         }
//                     })
//                     .body(|mut body| {
//                         // Process bytes in chunks of 4
//                         for (line_num, chunk) in self.data.chunks(4).enumerate() {
//                             body.row(18.0, |mut row| {
//                                 let address = line_num * 4;
//                                 let mut buf = [0u8; 4];
//                                 buf[..chunk.len()].copy_from_slice(chunk);
//                                 let value = u32::from_le_bytes(buf);
//
//                                 // Address column
//                                 row.col(|ui| {
//                                     ui.label(
//                                         egui::RichText::new(format!("0x{address:04X}"))
//                                             .font(egui::FontId::monospace(12.0)),
//                                     );
//                                 });
//
//                                 if self.show_bytes {
//                                     row.col(|ui| {
//                                         // Individual bytes column
//                                         ui.label(
//                                             egui::RichText::new(
//                                                 chunk
//                                                     .iter()
//                                                     .map(|b| format!("{b:02X}"))
//                                                     .collect::<Vec<_>>()
//                                                     .join(" "),
//                                             )
//                                             .font(egui::FontId::monospace(12.0))
//                                             .color(egui::Color32::from_rgb(200, 200, 255)),
//                                         );
//                                     });
//                                 }
//
//                                 if self.show_words {
//                                     row.col(|ui| {
//                                         // Hex column
//                                         ui.label(
//                                             egui::RichText::new(format!("0x{value:08X}"))
//                                                 .font(egui::FontId::monospace(12.0))
//                                                 .color(egui::Color32::from_rgb(255, 100, 100)),
//                                         );
//                                     });
//                                 }
//
//                                 if self.show_decimal {
//                                     row.col(|ui| {
//                                         ui.label(
//                                             egui::RichText::new(format!("{value}"))
//                                                 .font(egui::FontId::monospace(12.0))
//                                                 .color(egui::Color32::from_rgb(100, 255, 100)),
//                                         );
//                                     });
//                                 }
//
//                                 if self.show_ascii {
//                                     let ascii = value.to_le_bytes();
//                                     row.col(|ui| {
//                                         ui.label(
//                                             egui::RichText::new(String::from_utf8_lossy(&ascii))
//                                                 .font(egui::FontId::monospace(12.0))
//                                                 .color(egui::Color32::from_rgb(100, 255, 255)),
//                                         );
//                                     });
//                                 }
//
//                                 if self.show_instruction {
//                                     row.col(|ui| {
//                                         ui.label(
//                                             egui::RichText::new(format!(
//                                                 "{:?}",
//                                                 Instruction(value)
//                                             ))
//                                             .font(egui::FontId::monospace(12.0))
//                                             .color(egui::Color32::from_rgb(255, 100, 255)),
//                                         );
//                                     });
//                                 }
//                             })
//                         }
//                     });
//             });
//     }
// }
