use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};
use std::thread::sleep;
use std::time::Duration;
use imgui::{Condition, ImColor32, Ui};
use log::debug;
use gemmaemu::chip8::computer::Chip8Computer;
use gemmaemu::chip8::system_memory::Chip8SystemMemory;
use gemmaemu::constants::{CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_WIDTH};
use crate::UiState;


pub struct EmmaGui {}

const CELL_WIDTH: i32 = 5i32;
const CELL_HEIGHT: i32 = 5i32;

struct GuiFileList {}

impl GuiFileList {
    pub fn display_path(root: PathBuf, selected_filename: &String, ui: &Ui) -> String {
        let mut working_filename = selected_filename.clone();
        ui.text(format!("Displaying {}", root.to_str().unwrap_or("Unable to parse path")));
        for (index, entry) in std::fs::read_dir(root.as_path()).unwrap().enumerate() {
            let filename = entry.unwrap().file_name();
            let filename = filename.to_str().unwrap();
            let mut working_select = ui.selectable_config(format!("{}", filename));
            if filename == selected_filename {
                working_select = working_select.selected(true);
            }
            if working_select.build() {
                debug!("SELECTED {index} / {filename}");
                working_filename = filename.to_string();
            };
        }
        working_filename
    }
}


impl EmmaGui {
    pub fn video_display(system_to_control: &Chip8Computer, gui_state: &UiState, ui: &Ui) {
        // draw area size
        let draw_area_size = ui.io().display_size;
        let cell_width = ((draw_area_size[0] as i32 / 64) * 6) / 10;
        let cell_height = ((draw_area_size[1] as i32 / 32) * 6) / 10;

        ui.window(format!("Display {cell_width}x{cell_height}"))
            .size([300.0, 300.0], Condition::FirstUseEver)
            .build(|| {
                let origin = ui.cursor_screen_pos();
                let fg = ui.get_window_draw_list();
                ui.text("This is the video display.");
                for current_row in 0..=31 {
                    for current_column in 0..=63 {
                        let x_offset = origin[0] as i32 + (current_column * cell_width);
                        let y_offset = origin[1] as i32 + (current_row * cell_height);
                        let current_origin = [x_offset as f32, y_offset as f32];
                        let current_limit = [(x_offset + cell_width) as f32, (y_offset + cell_height) as f32];
                        let memory_offset = (current_row * 64 + current_column) as u16;
                        let to_render = system_to_control.video_memory.peek(memory_offset);
                        let color: ImColor32 = if to_render {
                            gui_state.on_colour
                        } else {
                            gui_state.off_colour
                        };
                        fg.add_rect_filled_multicolor(current_origin, current_limit, color, color, color, color);
                    }
                }
            });
    }
    pub fn system_controls(system_to_control: &mut Chip8Computer, gui_state: &mut UiState, ui: &Ui) {
        ui.window("!!!! CONTROLS !!!!")
            .size([345.0, 200.0], Condition::FirstUseEver)
            .build(|| {
                /* ROM Lister */
                let new_filename = GuiFileList::display_path(PathBuf::from("resources/roms"), &gui_state.filename_to_load, ui);
                if !new_filename.is_empty() {
                    if new_filename != gui_state.filename_to_load {
                        println!("NEW FILENAME SELECTED -> {new_filename}");
                        gui_state.filename_to_load = new_filename;
                    }
                    if ui.button("Load Program") {
                        let mut buffer = Vec::new();

                        println!("PREPARING TO LOAD {}", gui_state.filename_to_load);
                        // let mut input_file = File::open(Path::new("./1-chip8-logo.ch8")).expect("put 1-chip8-logo.ch8 in this directory");
                        let mut input_file = File::open(Path::new(&("resources/roms/".to_string() + &gui_state.filename_to_load))).expect("put 1-chip8-logo.ch8 in this directory");
                        input_file.read_to_end(&mut buffer).expect("unable to read file");
                        system_to_control.load_bytes_to_memory(0x200, buffer.into());
                    }
                }
                ui.separator();
                if ui.button("Step") {
                    system_to_control.step_system();
                };
                if ui.button("Run") {
                    gui_state.is_running = true;
                    println!("STARTING THE SYSTEM");
                }
                if ui.button("Stop") {
                    gui_state.is_running = false;
                    println!("STOPPING THE SYSTEM");
                }
                ui.same_line();
                if ui.button("Reset") {
                    *system_to_control = Chip8Computer::new();
                }
                if ui.button("Dump Video Memory") {
                    println!("{}", system_to_control.dump_video_to_string());
                }
                ui.same_line();
                if ui.button("Dump Keypad State") {
                    println!("{}", system_to_control.dump_keypad_to_string());
                }
                ui.same_line();
                if ui.button("Dump Registers") {
                    println!("{}", system_to_control.dump_registers_to_string());
                }
                ui.separator();

                ui.checkbox("Show Memory", &mut gui_state.show_memory);
                ui.same_line();
                ui.checkbox("Show Video", &mut gui_state.show_video);
                ui.same_line();
                ui.checkbox("Show Registers", &mut gui_state.show_registers);
            });
    }

    pub fn registers_view(system: &Chip8Computer, ui: &Ui) {
        ui.window("Registers")
            .size([400.0, 500.0], Condition::FirstUseEver)
            .build(|| {
                ui.text("Registers");
                for i in 1..0x10 {
                    ui.text(format!("V{:X}: {}", i, system.registers.peek(i)));
                    if i != 7 {
                        ui.same_line();
                    }
                }
                ui.text("");
                ui.text(format!("I: {:03X}", system.registers.peek_i()));
                ui.same_line();
                ui.text(format!("ST: {:02X}", system.sound_timer.current()));
                ui.same_line();
                ui.text(format!("DT: {:02X}", system.delay_timer.current()));
                ui.text(format!("PC: {:02X}", system.registers.peek_pc()));
                ui.text(format!("SP: {:02X}", system.stack.depth()));
            });
    }

    pub fn hex_memory_display(bytes: Chip8SystemMemory, position: (i32, i32), active: i16, ui: &Ui) {
        let rows = position.0;
        let cols = position.1;
        ui.window("System Memory")
            .size([400.0, 300.0], Condition::FirstUseEver)
            .position([500.0, 300.0], Condition::Always)
            .build(|| {
                let mut current_x_hover: i32 = 0;
                let mut current_y_hover: i32 = 0;
                // display a block of data
                for current_row in 0..rows {
                    ui.text(format!("{:02x}", current_row * cols));
                    ui.same_line();
                    for current_column in 0..cols {
                        let data_offset = current_row * cols + current_column;
                        let formatted_text = format!("{:02x}", bytes.peek(data_offset as u16));
                        let text_location = ui.cursor_screen_pos();
                        let text_size = ui.calc_text_size(formatted_text.clone());
                        let bounding_box = imgui::sys::ImVec2 {
                            x: text_location[0] + text_size[0],
                            y: text_location[1] + text_size[1],
                        };

                        let hovering = ui.is_mouse_hovering_rect(text_location, [bounding_box.x, bounding_box.y]);
                        let is_active = data_offset == active as i32;

                        ui.text_colored(if hovering {
                            [0., 1., 1., 1.]
                        } else if is_active {
                            [1., 0., 1., 1.]
                        } else {
                            [1., 1., 0., 1.]
                        }, formatted_text.clone());


                        // if we are hovering show that at the bottom...
                        if hovering {
                            // Optionally change the text color to indicate it's interactable
                            current_x_hover = current_column;
                            current_y_hover = current_row;

                            // Check if the left mouse button is clicked while hovering over the text
                            if ui.is_mouse_clicked(imgui::MouseButton::Left) {
                                println!("Offset: [{}] [0x{:02x}] Value: [{}]", data_offset, data_offset, formatted_text.clone());
                                // Perform any action here, e.g., call a function, trigger an event, etc.
                            }
                        }

                        // are we on the same line?
                        if current_column != (cols - 1) {
                            ui.same_line();
                        }
                    }
                }
                ui.text(format!("Offset 0x{:03x}", current_x_hover * cols + current_y_hover));
            });
    }

    pub fn system_memory_render(memory: Chip8SystemMemory, ui: &Ui) {
        ui.window("System Memory")
            .size([300.0, 100.0], Condition::FirstUseEver)
            .build(|| {
                ui.text("Rendering System Memory Here");
                let draw_list = ui.get_foreground_draw_list();
                let mut idx = 0;
                for row in 0..CHIP8_VIDEO_HEIGHT {
                    for column in 0..CHIP8_VIDEO_WIDTH {
                        let x_offset = column * CELL_WIDTH;
                        let y_offset = row * CELL_WIDTH;
                        let start_point = [x_offset as f32, y_offset as f32];
                        let end_point = [(x_offset + CELL_WIDTH) as f32,
                            (y_offset + CELL_HEIGHT) as f32
                        ];
                        let memory_offset = (row * CHIP8_VIDEO_WIDTH) + column;
                        let target_color = if memory.peek(memory_offset as u16) == 0 {
                            ImColor32::BLACK
                        } else {
                            ImColor32::WHITE
                        };
                        draw_list.add_rect([x_offset as f32, y_offset as f32],
                                           [(x_offset + CELL_WIDTH) as f32, (y_offset + CELL_HEIGHT) as f32],
                                           target_color).build();
                        idx += 1;
                    }
                }
            });
    }
}