mod registers;
mod instructions;
mod lcd;

use glob::glob;
use serde_json::Value;
use crate::instructions::{Condition, Target, LoadTarget, TargetRegister, TargetU16Register, Instruction, parse_instruction};
use crate::registers::FlagsRegister;
use crate::registers::Registers;
use crate::lcd::{GPU, LY_ADDR, LCDC_ADDR, SCREEN_WIDTH, SCREEN_HEIGHT, BGP_ADDR, LCDMode};

use sdl2::pixels::Color;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::rect::Rect;
use std::time::{Duration, Instant};


#[derive(Debug)]
struct GameRom {
    data: Vec<u8>,
    title: String,
    cgb_flag: u8,
    licensee: [u8; 2],
    sgb_flag: u8,
    cartridge_type: CartridgeType,
    rom_size: u16,
    rom_bank_low: u8,
    rom_bank_high: u8,
    ram_size: u8,
    ram_enable: bool,
    banking_mode: bool,
}
#[derive(Debug)]
enum CartridgeType{
    RomOnly,
    MBC1,
    MBC2,
    MMM01,
    MBC3,
    MBC5,
    MBC6,
    MBC7,
    PocketCamera,
    BandaiTama5,
    HuC3,
    HuC1RamBattery,
}

impl GameRom {
    fn load(filename: &str) -> GameRom {

        let data = std::fs::read(filename).unwrap();
        let title_end = data[0x134..0x143].iter().position(|b| *b == 0x0).unwrap();
        let title = data[0x134..0x134 + title_end].to_vec().escape_ascii().to_string();
        let cgb_flag = data[0x143];
        if cgb_flag == 0xC0 {
            panic!("CGB not supported");
        }
        let licensee = [data[0x144], data[0x145]];
        let sgb_flag = data[0x146];
        println!("{:x}", data[0x147]);

        let cartridge_type = match data[0x147] {
            0x00 => CartridgeType::RomOnly,
            0x01 => CartridgeType::MBC1,
            _ => {panic!("not a supported cartridge")}
        };
        let rom_size = match data[0x148] {
            0x00 => 2,  // 0b1
            0x01 => 4,  // 0b11 
            0x02 => 8,  // 0b111
            0x03 => 16, // 0b1111
            0x04 => 32, // 0b1111 1
            0x05 => 64, // 0b1111 11
            0x06 => 128,// 0b1111 111
            0x07 => 256,// 0b1111 1111
            0x08 => 512,// 0b1111 1111 1
            _ => { panic!("not a supported rom size"); }
        };
        let ram_size = data[0x149];
        println!("{rom_size}");
        GameRom{data, title, cgb_flag, licensee, sgb_flag, cartridge_type, rom_size, ram_size, rom_bank_low: 0, rom_bank_high: 0, ram_enable: false, banking_mode: false, }
    }
    fn read_byte(&self, addr: usize) -> u8 {
        match self.cartridge_type {
            CartridgeType::RomOnly => {self.data[addr]}
            CartridgeType::MBC1 => {
                match addr {
                    0x0000..=0x3FFF => self.data[addr],
                    0x4000..=0x7FFF => {
                        let bank: u8;
                        if self.rom_size > 32 {
                            bank = self.rom_bank_low & 0b11111 | self.rom_bank_high << 5
                        } else {
                            bank = self.rom_bank_low & 0b11111;
                        }
                        let offset = (0x4000 * bank as usize) + (addr - 0x4000);
                        self.data[offset]
                    },
                    _ => {panic!("unsupported memory access")}
                }
            }
            _ => {panic!("not a supported cartridge")}
        }
    }
    fn write_byte(&mut self, addr: usize, data: u8) {
        match self.cartridge_type {
            CartridgeType::RomOnly => {}
            CartridgeType::MBC1 => {
                match addr {
                    0x0000..=0x1FFF => { self.ram_enable = data & 0xF == 0x0A;}
                    0x2000..=0x3FFF => { self.rom_bank_low = data }
                    0x4000..=0x5FFF => { self.rom_bank_high = data }
                    0x6000..=0x7FFF => { self.banking_mode = data &0x1 == 0x1 }
                    0xA000..=0xBFFF => { if self.ram_enable {
                        self.data[addr] = data;
                    } }
                    _ => {panic!("unsupported Cartridge access")}
                }
            }
            _ => {panic!("unsupported Cartridge type")}
        }


    }
}

#[derive(Debug)]
struct CPU {
    registers: Registers,
    pc: u16,
    bus: MemoryBus,
    sp: u16,
    interrupts_enabled: bool,
}

const BOOT_BEGIN: usize = 0x0000;
const BOOT_END: usize = 0x00FF;
const CART_BEGIN: usize = 0x0000;
const CART_END: usize = 0x7FFF;
const VRAM_BEGIN: usize = 0x8000;
const VRAM_END: usize = 0x9FFF;

// GPU implementation moved to lcd.rs

struct ExecutionReturn {
    pc: u16,
    cycles: u8,
}
#[derive(Debug)]
struct MemoryBus {
    memory: [u8; 0xFFFF+1],
    gpu: GPU,
    rom: GameRom,
    boot: Vec<u8>,
    flat_ram: bool,
}
impl MemoryBus {
    fn read_byte(&self, address: u16) -> u8 {
        let address = address as usize;
        if self.flat_ram {
            return self.memory[address]
        }

        // Special logging for reads from 0xFF50 (boot ROM control)
        if address == 0xFF50 {
            println!("READ from 0xFF50: 0x{:02X} (Boot ROM is {})", 
                     self.memory[0xFF50], if self.memory[0xFF50] == 0 { "enabled" } else { "disabled" });
        }

        let result = match address {
            VRAM_BEGIN ..= VRAM_END => {
                self.gpu.read_vram(address - VRAM_BEGIN)
            }
            CART_BEGIN ..= CART_END => {
                if address < 0x100 && self.memory[0xFF50] == 0x00 {
                    // Special logging for bootloader reads
                    if address == 0 {
                        println!("Reading from boot ROM at address 0x0000");
                    }
                    self.boot[address]
                } else {
                    // Special logging for game ROM reads that would be bootloader if enabled
                    if address < 0x100 && self.memory[0xFF50] != 0x00 {
                        println!("Reading from game ROM at address 0x{:04X} (boot ROM disabled)", address);
                    }
                    self.rom.read_byte(address)
                }
            }
            LCDC_ADDR => {u8::from(self.gpu.lcd_control)}
            LY_ADDR => {self.gpu.ly}
            _ => self.memory[address]
        };

        result
    }
    fn write_byte(&mut self, address: u16, value: u8) {
        if self.flat_ram {
            self.memory[address as usize] = value;
            return;
        }
        let address = address as usize;

        // Special logging for writes to 0xFF50 (boot ROM control)
        if address == 0xFF50 {
            println!("WRITE to 0xFF50: 0x{:02X} (Boot ROM will be {}) at address 0x{:04X}", 
                     value, if value == 0 { "enabled" } else { "disabled" }, address);
        }

        match address {
            VRAM_BEGIN ..= VRAM_END => {
                self.gpu.write_vram(address - VRAM_BEGIN, value)
            }
            CART_BEGIN ..= CART_END => {
                self.rom.write_byte(address, value)
            }
            _ => self.memory[address] = value
        }
    }
    fn read_u16(&self, address: u16) -> u16 {
        let low = self.read_byte(address) as u16;
        let high = self.read_byte(address.wrapping_add(1)) as u16;
        high << 8 | low
    }
    fn write_u16(&mut self, address: u16, value: u16) {
        let low = (value & 0xFF) as u8;
        let high = ((value >> 8) & 0xFF) as u8;
        self.write_byte(address, low);
        self.write_byte(address.wrapping_add(1), high);
    }
}

impl CPU {

    fn init(&mut self) {
        let l = self.bus.memory.len();
        println!("{l}");
        println!("CPU init");
        self.bus.write_byte(0xFF50, 0x00);

        // Enable LCD and set all necessary LCD control flags
        self.bus.gpu.lcd_control.lcd_enabled = true;
        self.bus.gpu.lcd_control.bg_and_window_enable = true;
        self.bus.gpu.lcd_control.bg_tile_map_area = false; // Use 0x9800-0x9BFF
        self.bus.gpu.lcd_control.bg_and_window_tile_area = true; // Use 0x8000-0x8FFF
        self.bus.gpu.lcd_control.window_enable = false; // Disable window for now
        self.bus.gpu.lcd_control.object_enable = false; // Disable sprites for now

        // Write the LCD control register to memory
        self.bus.memory[LCDC_ADDR] = u8::from(self.bus.gpu.lcd_control);
        println!("Set LCDC register to: 0x{:02X}", u8::from(self.bus.gpu.lcd_control));

        // Set background palette (0b11100100)
        // 11 = Darkest (color 3)
        // 10 = Dark (color 2)
        // 01 = Light (color 1)
        // 00 = Lightest (color 0)
        self.bus.gpu.bg_palette = 0xE4;
        self.bus.memory[BGP_ADDR] = self.bus.gpu.bg_palette;
        println!("Set BGP register to: 0x{:02X}", self.bus.gpu.bg_palette);

        // Initialize VRAM with some test pattern to verify rendering
        println!("Initializing VRAM with test pattern");

        // Create a simple checkerboard pattern in the first tile
        let tile_data = [
            0xFF, 0x00, // Row 1: all pixels set to color 1
            0xFF, 0xFF, // Row 2: all pixels set to color 3
            0x00, 0xFF, // Row 3: all pixels set to color 2
            0x00, 0x00, // Row 4: all pixels set to color 0
            0xFF, 0x00, // Row 5: all pixels set to color 1
            0xFF, 0xFF, // Row 6: all pixels set to color 3
            0x00, 0xFF, // Row 7: all pixels set to color 2
            0x00, 0x00, // Row 8: all pixels set to color 0
        ];

        // Create a solid pattern for the second tile
        let tile_data2 = [
            0xFF, 0xFF, // Row 1: all pixels set to color 3
            0xFF, 0xFF, // Row 2: all pixels set to color 3
            0xFF, 0xFF, // Row 3: all pixels set to color 3
            0xFF, 0xFF, // Row 4: all pixels set to color 3
            0xFF, 0xFF, // Row 5: all pixels set to color 3
            0xFF, 0xFF, // Row 6: all pixels set to color 3
            0xFF, 0xFF, // Row 7: all pixels set to color 3
            0xFF, 0xFF, // Row 8: all pixels set to color 3
        ];

        // Create a striped pattern for the third tile
        let tile_data3 = [
            0xFF, 0x00, // Row 1: all pixels set to color 1
            0x00, 0xFF, // Row 2: all pixels set to color 2
            0xFF, 0x00, // Row 3: all pixels set to color 1
            0x00, 0xFF, // Row 4: all pixels set to color 2
            0xFF, 0x00, // Row 5: all pixels set to color 1
            0x00, 0xFF, // Row 6: all pixels set to color 2
            0xFF, 0x00, // Row 7: all pixels set to color 1
            0x00, 0xFF, // Row 8: all pixels set to color 2
        ];

        // Write the test patterns to the first three tiles in VRAM
        for (i, &byte) in tile_data.iter().enumerate() {
            self.bus.gpu.write_vram(i, byte);
        }

        for (i, &byte) in tile_data2.iter().enumerate() {
            self.bus.gpu.write_vram(16 + i, byte); // Tile 1 starts at offset 16
        }

        for (i, &byte) in tile_data3.iter().enumerate() {
            self.bus.gpu.write_vram(32 + i, byte); // Tile 2 starts at offset 32
        }

        // Fill the first 16x16 area of the background tile map with our test tiles
        for y in 0..16 {
            for x in 0..16 {
                let tile_idx = (y % 3) * 1 + (x % 3); // Use tiles 0, 1, 2 in a pattern
                let map_addr = 0x1800 + y * 32 + x; // Background map starts at 0x1800
                self.bus.gpu.write_vram(map_addr, tile_idx as u8);
            }
        }

        // Set scroll registers to center the display
        // The test pattern is 16x16 tiles (128x128 pixels)
        // The GameBoy screen is 160x144 pixels
        // To center, we offset by (pattern_size - screen_size) / 2
        self.bus.gpu.scroll_x = 0; // Center horizontally
        self.bus.gpu.scroll_y = 0; // Center vertically

        println!("VRAM initialized with test pattern");
        println!("LCD Control: {:?}", self.bus.gpu.lcd_control);
        println!("Background Palette: 0x{:02X}", self.bus.gpu.bg_palette);
    }

    fn load_test(test: &Value) -> CPU {
        let vram = vec![0xFFu8;VRAM_END-VRAM_BEGIN+1];
        let game_rom = vec![0xFFu8;CART_END+1];
        let mut memory = [0xFFu8; 0xFFFF+1];
        for mem in test["ram"].as_array().unwrap() {
            let address = mem[0].as_u64().unwrap() as usize;
            let value = mem[1].as_u64().unwrap() as u8;
            memory[address] = value;
            // println!("{:x}, {:x}", address, value);
        }


        CPU{
            registers:Registers{
                a:test["a"].as_u64().unwrap() as u8,
                b:test["b"].as_u64().unwrap() as u8,
                c:test["c"].as_u64().unwrap() as u8,
                d:test["d"].as_u64().unwrap() as u8,
                e:test["e"].as_u64().unwrap() as u8,
                f:FlagsRegister::from(test["f"].as_u64().unwrap() as u8),
                h:test["h"].as_u64().unwrap() as u8,
                l:test["l"].as_u64().unwrap() as u8},
            pc:test["pc"].as_u64().unwrap() as u16,
            sp: test["sp"].as_u64().unwrap() as u16,
            bus:MemoryBus{
                memory,
                gpu: GPU::new(vram),
                rom: GameRom{
                    data:game_rom,
                    title: "test".parse().unwrap(),
                    cgb_flag: 0,
                    licensee: [0,0],
                    sgb_flag: 0,
                    cartridge_type: CartridgeType::RomOnly,
                    rom_size: 2,
                    ram_size: 0,
                    rom_bank_low: 0,
                    rom_bank_high: 0,
                    ram_enable: false,
                    banking_mode: false,
                },
                boot: vec![],
                flat_ram: true,
            },
            interrupts_enabled: false,
        }
    }

    pub(crate) fn compare_state(&self, state: &Value) {
        let a = state["a"].as_u64().unwrap();
        let b = state["b"].as_u64().unwrap();
        let c = state["c"].as_u64().unwrap();
        let d = state["d"].as_u64().unwrap();
        let e = state["e"].as_u64().unwrap();
        let f = state["f"].as_u64().unwrap();
        let h = state["h"].as_u64().unwrap();
        let l = state["l"].as_u64().unwrap();
        let pc = state["pc"].as_u64().unwrap();
        let sp = state["sp"].as_u64().unwrap();
        assert_eq!(self.registers.a, a as u8);
        assert_eq!(self.registers.b, b as u8);
        assert_eq!(self.registers.c, c as u8);
        assert_eq!(self.registers.d, d as u8);
        assert_eq!(self.registers.e, e as u8);
        assert_eq!(self.registers.f, FlagsRegister::from(f as u8));
        assert_eq!(self.registers.h, h as u8);
        assert_eq!(self.registers.l, l as u8);
        assert_eq!(self.pc, pc as u16);
        assert_eq!(self.sp, sp as u16);
        for mem in state["ram"].as_array().unwrap() {
            let address = mem[0].as_u64().unwrap() as u16;
            let value = mem[1].as_u64().unwrap() as u8;
            assert_eq!(self.bus.read_byte(address), value);
        }
    }

    fn check_condition(&self, condition: Condition) -> bool {
        match condition {
            Condition::NZ => !self.registers.f.zero,
            Condition::Z => self.registers.f.zero,
            Condition::NC => !self.registers.f.carry,
            Condition::C => self.registers.f.carry,
            Condition::None => true,
        }
    }

    fn get_u8_reg_value(&mut self, target: TargetRegister) -> u8 {
        match target {
            TargetRegister::A => { self.registers.a }
            TargetRegister::B => { self.registers.b }
            TargetRegister::C => { self.registers.c }
            TargetRegister::D => { self.registers.d }
            TargetRegister::E => { self.registers.e }
            TargetRegister::H => { self.registers.h }
            TargetRegister::L => { self.registers.l }
        }
    }
    fn set_u8_reg_value(&mut self, target: TargetRegister, value: u8) {
        match target {
            TargetRegister::A => { self.registers.a = value }
            TargetRegister::B => { self.registers.b = value }
            TargetRegister::C => { self.registers.c = value }
            TargetRegister::D => { self.registers.d = value }
            TargetRegister::E => { self.registers.e = value }
            TargetRegister::H => { self.registers.h = value }
            TargetRegister::L => { self.registers.l = value }
        }
    }
    fn get_u16_reg_value(&mut self, target: TargetU16Register) -> u16 {
        match target {
            TargetU16Register::AF => {self.registers.get_af()}
            TargetU16Register::BC => {self.registers.get_bc()}
            TargetU16Register::DE => {self.registers.get_de()}
            TargetU16Register::HL => {self.registers.get_hl()}
            TargetU16Register::SP => {self.sp}
            TargetU16Register::PC => {self.pc}
        }
    }
    fn set_u16_reg_value(&mut self, target: TargetU16Register, value: u16) {
        match target {
            TargetU16Register::AF => {self.registers.set_af(value)}
            TargetU16Register::BC => {self.registers.set_bc(value)}
            TargetU16Register::DE => {self.registers.set_de(value)}
            TargetU16Register::HL => {self.registers.set_hl(value)}
            TargetU16Register::SP => {self.sp=value}
            TargetU16Register::PC => {self.pc=value}
        }
    }
    fn push_stack(&mut self, value: u16) {
        let low = (value & 0xFF) as u8;
        let high = (value >> 8) as u8;
        self.bus.write_byte(self.sp - 1, high);
        self.bus.write_byte(self.sp - 2, low);
        self.sp = self.sp.wrapping_sub(2);
    }
    fn pop_stack(&mut self) -> u16 {
        let low = self.bus.read_byte(self.sp) as u16;
        let high = (self.bus.read_byte(self.sp + 1) as u16) << 8;
        self.sp = self.sp.wrapping_add(2);
        high | low
    }

    fn get_target_value(&mut self, target: Target) -> u8 {
        match target {
            Target::U8Register(target_register) => { self.get_u8_reg_value(target_register) },
            Target::U16Register(target_register) => {
                let address = self.get_u16_reg_value(target_register);
                self.bus.read_byte(address)
            },
            Target::Immediate(value) => { value },
        }
    }
    fn set_target_value(&mut self, target: Target, value: u8) {
        match target {
            Target::U8Register(target_register) => { self.set_u8_reg_value(target_register, value) },
            Target::U16Register(target_register) => {
                let address = self.get_u16_reg_value(target_register);
                self.bus.write_byte(address, value)
            },
            Target::Immediate(_) => {  },
        }
    }



    fn execute_next_instruction(&mut self) -> u8 {
        // if self.pc == 0xC {
        //     println!("clear vram Complete");
        // }
        // if self.pc == 0x34 {
        //     println!("decompress finish");
        // }
        let inst = parse_instruction(self.bus.read_byte(self.pc), self.bus.read_byte(self.pc.wrapping_add(1)), self.bus.read_byte(self.pc.wrapping_add(2)));
        // println!("{:x} {:?} {:?}", self.pc, inst, self.registers.f);

        // Check if we're in a loop by detecting repeated PC values
        static mut LAST_PC_VALUES: [u16; 10] = [0; 10];
        static mut PC_INDEX: usize = 0;

        unsafe {
            // Check if we've seen this PC value multiple times in a row
            let mut loop_detected = true;
            for i in 0..10 {
                if LAST_PC_VALUES[i] != self.pc {
                    loop_detected = false;
                    break;
                }
            }

            if loop_detected {
                println!("LOOP DETECTED at PC: {:04X}", self.pc);
                println!("Boot ROM status: 0x{:02X}", self.bus.memory[0xFF50]);

                // Print nearby memory for debugging
                println!("Memory around 0xFF50:");
                for i in 0xFF40..=0xFF60 {
                    print!("{:04X}: {:02X} ", i, self.bus.memory[i]);
                    if (i - 0xFF40 + 1) % 4 == 0 {
                        println!();
                    }
                }
            }

            // Update the PC history
            LAST_PC_VALUES[PC_INDEX] = self.pc;
            PC_INDEX = (PC_INDEX + 1) % 10;
        }

        let result = self.execute(inst);
        self.pc = result.pc;

        // Update the LCD based on the number of cycles
        self.bus.gpu.update(result.cycles as u32);

        result.cycles
    }

    fn standard_inc_pc_and_timing(&mut self, target: Target) -> ExecutionReturn {
        match target {
            Target::U8Register(_) => {
                ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 1}
            }
            Target::U16Register(_) => {
                ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 2}
            }
            Target::Immediate(_) => {
                ExecutionReturn{pc: self.pc.wrapping_add(2), cycles: 2}
            },
        }
    }
    fn rotate_inc_pc_and_timing(&mut self, target: Target) -> ExecutionReturn {
        match target {
            Target::U8Register(_) => {ExecutionReturn{pc: self.pc.wrapping_add(2), cycles: 2}}
            Target::U16Register(_) => {ExecutionReturn{pc: self.pc.wrapping_add(2), cycles: 4}}
            Target::Immediate(_) => {panic!("Does not accept immediate target")}
        }
    }
    fn execute(&mut self, instruction: Instruction) -> ExecutionReturn {
        match instruction {
            Instruction::ADC(target) => {
                let value = self.get_target_value(target);
                let carry = self.registers.f.carry as u8;
                let (value_with_carry, carry_overflow) = value.overflowing_add(carry);
                let (new_value, did_overflow) = self.registers.a.overflowing_add(value_with_carry);
                self.registers.f.zero = new_value == 0;
                self.registers.f.subtract = false;
                self.registers.f.half_carry = (self.registers.a & 0xF) + (value & 0xF) + carry > 0xF;
                self.registers.f.carry = did_overflow | carry_overflow;
                self.registers.a = new_value;
                self.standard_inc_pc_and_timing(target)
            }
            Instruction::ADD(target) => {
                let value = self.get_target_value(target);
                let (new_value, did_overflow) = self.registers.a.overflowing_add(value);
                self.registers.f.zero = new_value == 0;
                self.registers.f.subtract = false;
                self.registers.f.carry = did_overflow;
                self.registers.f.half_carry = (self.registers.a & 0xF) + (value & 0xF) > 0xF;
                self.registers.a = new_value;
                self.standard_inc_pc_and_timing(target)
            }
            Instruction::ADDHL(target) => {
                let value_source = self.get_u16_reg_value(target);
                let value_dest = self.registers.get_hl();
                let (new_value, did_overflow) = value_dest.overflowing_add(value_source);
                self.registers.set_hl(new_value);
                self.registers.f.subtract = false;
                self.registers.f.carry = did_overflow;
                self.registers.f.half_carry = (value_dest & 0xFFF) + (value_source & 0xFFF) > 0xFFF;
                ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 2}
            }
            Instruction::ADDSP(value) => {
                let new_value = self.sp.wrapping_add_signed(value as i16);
                // couldn't get this working, so stole this method from
                // https://github.com/simias/gb-rs/blob/master/src/cpu/instructions.rs
                let carry = (self.sp as i32 ^ value as i32 ^ new_value as i32) & 0x100 != 0;
                let half_carry = (self.sp as i32 ^ value as i32 ^ new_value as i32) & 0x10 != 0;
                self.registers.f.carry = carry;
                self.registers.f.half_carry = half_carry;
                self.sp = new_value;
                self.registers.f.zero = false;
                self.registers.f.subtract = false;
                ExecutionReturn{pc: self.pc.wrapping_add(2), cycles: 4}
            }
            Instruction::AND(target) => {
                let value = self.get_target_value(target);
                self.registers.a = value & self.registers.a;
                self.registers.f.zero = self.registers.a == 0;
                self.registers.f.subtract = false;
                self.registers.f.carry = false;
                self.registers.f.half_carry = true;
                self.standard_inc_pc_and_timing(target)
            }
            Instruction::BIT(bit, target) => {
                let value = self.get_target_value(target);
                self.registers.f.zero = value >> bit & 0x1 == 0;
                self.registers.f.subtract = false;
                self.registers.f.half_carry = true;
                let pc = self.pc.wrapping_add(2);
                match target {
                    Target::U8Register(_) => {ExecutionReturn{pc, cycles: 2}}
                    Target::U16Register(_) => {ExecutionReturn{pc, cycles: 3}}
                    Target::Immediate(_) => {ExecutionReturn{pc, cycles: 2}}
                }
            }
            Instruction::CALL(condition, address) => {
                let (pc, cycles) = if self.check_condition(condition) {
                    let next_instruction = self.pc.wrapping_add(3);
                    self.push_stack(next_instruction);
                    (address, 6)
                } else {
                    (self.pc.wrapping_add(3), 3)
                };
                ExecutionReturn{pc, cycles}
            }
            Instruction::CCF => {
                self.registers.f.subtract = false;
                self.registers.f.half_carry = false;
                self.registers.f.carry = !self.registers.f.carry;
                ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 1}
            }
            Instruction::CP(target) => {
                let value = self.get_target_value(target);
                let (new_value, did_overflow) = self.registers.a.overflowing_sub(value);
                self.registers.f.zero = new_value == 0;
                self.registers.f.subtract = true;
                self.registers.f.carry = did_overflow;
                let (_, half_carry) = (self.registers.a & 0xF).overflowing_sub(value & 0xF);
                self.registers.f.half_carry = half_carry;
                self.standard_inc_pc_and_timing(target)
            }
            Instruction::CPL => {
                self.registers.a = !self.registers.a;
                self.registers.f.subtract = true;
                self.registers.f.half_carry = true;
                ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 1}
            }
            Instruction::DAA => {
                let new_value;
                let did_overflow;
                let mut adjustment: u8 = 0;
                if self.registers.f.subtract {
                    if self.registers.f.half_carry {
                        adjustment += 0x6;
                    }
                    if self.registers.f.carry {
                        adjustment += 0x60;
                    }
                    new_value = self.registers.a.wrapping_sub(adjustment);
                    did_overflow = self.registers.f.carry;
                } else {
                    if self.registers.f.half_carry || self.registers.a & 0xF > 0x9 {
                        adjustment += 0x6;
                    }
                    if self.registers.f.carry || self.registers.a > 0x99 {
                        adjustment += 0x60;
                        did_overflow = true;
                        new_value = self.registers.a.wrapping_add(adjustment);
                    }else {
                        (new_value, did_overflow) = self.registers.a.overflowing_add(adjustment);
                    }
                }
                self.registers.a = new_value;
                self.registers.f.zero = new_value == 0;
                self.registers.f.half_carry = false;
                self.registers.f.carry = did_overflow;
                ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 1}
            }
            Instruction::DEC(target) => {
                let value = self.get_target_value(target);
                let new_value = value.wrapping_sub(1);
                self.set_target_value(target, new_value);
                self.registers.f.zero = new_value == 0;
                self.registers.f.subtract = true;
                let (_, half_carry) = (value & 0xF).overflowing_sub(1);
                self.registers.f.half_carry = half_carry;
                match target {
                    Target::U8Register(_) => {ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 1}}
                    Target::U16Register(TargetU16Register::SP) => {ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 2}}
                    Target::U16Register(_) => {ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 3}}
                    Target::Immediate(_) => {panic!("Does not accept immediate target")}
                }
            }
            Instruction::DECU16(register) => {
                let value = self.get_u16_reg_value(register);
                let new_value = value.wrapping_sub(1);
                self.set_u16_reg_value(register, new_value);
                ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 2}
            }
            Instruction::DI => {
                self.interrupts_enabled = false;
                ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 1}
            }
            Instruction::EI => {
                self.interrupts_enabled = true;
                ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 1}
            }
            Instruction::HALT => {
                // In a real GameBoy, HALT would wait for an interrupt
                // Since we don't have proper interrupt handling yet, we'll just continue execution
                // This should prevent the bootloader from getting stuck in a loop
                println!("HALT instruction at PC: {:04X} - Continuing execution", self.pc);

                // Force the boot ROM to be disabled if we're in the bootloader
                // This is a workaround to allow the game to start
                // if self.pc < 0x100 && self.bus.memory[0xFF50] == 0x00 {
                //     println!("Forcing boot ROM disable at PC: {:04X}", self.pc);
                //     self.bus.memory[0xFF50] = 0x01;
                // }

                ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 3}
            }

            Instruction::INC(target) => {
                let value = self.get_target_value(target);
                let new_value = value.wrapping_add(1);
                self.set_target_value(target, new_value);
                self.registers.f.zero = new_value == 0;
                self.registers.f.subtract = false;
                self.registers.f.half_carry = (value & 0xF) + 1 > 0xF;
                match target {
                    Target::U8Register(_) => {ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 1}}
                    Target::U16Register(TargetU16Register::SP) => {ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 2}}
                    Target::U16Register(_) => {ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 3}}
                    Target::Immediate(_) => {panic!("Does not accept immediate target")}
                }
            }
            Instruction::INCU16(register) => {
                let value = self.get_u16_reg_value(register);
                let new_value = value.wrapping_add(1);
                self.set_u16_reg_value(register, new_value);
                ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 2}
            }
            Instruction::JP(condition, address) => {
                if self.check_condition(condition) {
                    self.pc = address;
                    ExecutionReturn{pc: address, cycles: 4}
                } else {
                    ExecutionReturn{pc: self.pc.wrapping_add(3), cycles: 3}
                }
            }
            Instruction::JPHL => {
                ExecutionReturn{pc: self.registers.get_hl(), cycles: 1}
            }
            Instruction::JR(condition, offset) => {
                if self.check_condition(condition) {
                    ExecutionReturn{pc: self.pc.wrapping_add_signed(offset as i16).wrapping_add(2), cycles: 3}
                } else {
                    ExecutionReturn{pc: self.pc.wrapping_add(2), cycles: 2}
                }
            }
            Instruction::LD(target) => {
                match target {
                    LoadTarget::CopyR8R8(dest_register, source_register) => {
                        let value = self.get_u8_reg_value(source_register);
                        self.set_u8_reg_value(dest_register, value);
                        ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 1}
                    }
                    LoadTarget::CopyR8N8(dest_register, value) => {
                        self.set_u8_reg_value(dest_register, value);
                        ExecutionReturn{pc: self.pc.wrapping_add(2), cycles: 2}
                    }
                    LoadTarget::CopyR16N16(dest_register, value) => {
                        self.set_u16_reg_value(dest_register, value);
                        ExecutionReturn{pc: self.pc.wrapping_add(3), cycles: 3}
                    }
                    LoadTarget::CopyHLR8(source_register) => {
                        let value = self.get_u8_reg_value(source_register);
                        let address = self.registers.get_hl();
                        self.bus.write_byte(address, value);
                        ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 2}
                    }
                    LoadTarget::CopyHLN8(value) => {
                        let address = self.registers.get_hl();
                        self.bus.write_byte(address, value);
                        ExecutionReturn{pc: self.pc.wrapping_add(2), cycles: 3}
                    }
                    LoadTarget::CopyR8HL(dest_register) => {
                        let address = self.registers.get_hl();
                        let value = self.bus.read_byte(address);
                        self.set_u8_reg_value(dest_register, value);
                        ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 2}
                    }
                    LoadTarget::CopyR16A(dest_register) => {
                        let address = self.get_u16_reg_value(dest_register);
                        self.bus.write_byte(address, self.registers.a);
                        ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 2}
                    }
                    LoadTarget::CopyN16A(address) => {
                        self.bus.write_byte(address, self.registers.a);
                        ExecutionReturn{pc: self.pc.wrapping_add(3), cycles: 4}
                    }
                    LoadTarget::CopyPortA(target) => {
                        let offset = self.get_target_value(target);
                        let address = 0xFF00 | offset as u16;
                        self.bus.write_byte(address, self.registers.a);
                        match target {
                            Target::U8Register(_) => {ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 2}}
                            Target::U16Register(_) => {ExecutionReturn{pc: self.pc.wrapping_add(2), cycles: 3}}
                            Target::Immediate(_) => {ExecutionReturn{pc: self.pc.wrapping_add(2), cycles: 3}}
                        }
                    }
                    LoadTarget::CopyAPort(target) => {
                        let offset = self.get_target_value(target);
                        let address = 0xFF00 | offset as u16;
                        self.registers.a = self.bus.read_byte(address);
                        match target {
                            Target::U8Register(_) => {ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 2}}
                            Target::U16Register(_) => {ExecutionReturn{pc: self.pc.wrapping_add(2), cycles: 3}}
                            Target::Immediate(_) => {ExecutionReturn{pc: self.pc.wrapping_add(2), cycles: 3}}
                        }
                    }
                    LoadTarget::CopyAR16(source_register) => {
                        let address = self.get_u16_reg_value(source_register);
                        self.registers.a = self.bus.read_byte(address);
                        ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 2}
                    }
                    LoadTarget::CopyAN16(address) => {
                        self.registers.a = self.bus.read_byte(address);
                        ExecutionReturn{pc: self.pc.wrapping_add(3), cycles: 4}
                    }
                    LoadTarget::CopyHLIA => {
                        let address = self.registers.get_hl();
                        self.bus.write_byte(address, self.registers.a);
                        self.registers.set_hl(address.wrapping_add(1));
                        ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 2}
                    }
                    LoadTarget::CopyHLDA => {
                        let address = self.registers.get_hl();
                        self.bus.write_byte(address, self.registers.a);
                        self.registers.set_hl(address.wrapping_sub(1));
                        ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 2}
                    }
                    LoadTarget::CopyAHLI => {
                        let address = self.registers.get_hl();
                        self.registers.a = self.bus.read_byte(address);
                        self.registers.set_hl(address.wrapping_add(1));
                        ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 2}
                    }
                    LoadTarget::CopyAHLD => {
                        let address = self.registers.get_hl();
                        self.registers.a = self.bus.read_byte(address);
                        self.registers.set_hl(address.wrapping_sub(1));
                        ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 2}
                    }
                    LoadTarget::CopySPN16(value) => {
                        self.sp = value;
                        ExecutionReturn{pc: self.pc.wrapping_add(3), cycles: 3}
                    }
                    LoadTarget::CopyN16SP(address) => {
                        self.bus.write_u16(address, self.sp);
                        ExecutionReturn{pc: self.pc.wrapping_add(3), cycles: 5}
                    }
                    LoadTarget::CopyHLSPE8(value) => {
                        let new_value = self.sp.wrapping_add_signed(value as i16);
                        let carry = (self.sp as i32 ^ value as i32 ^ new_value as i32) & 0x100 != 0;
                        let half_carry = (self.sp as i32 ^ value as i32 ^ new_value as i32) & 0x10 != 0;
                        self.registers.set_hl(new_value);
                        self.registers.f.zero = false;
                        self.registers.f.subtract = false;
                        self.registers.f.carry = carry;
                        self.registers.f.half_carry = half_carry;
                        ExecutionReturn{pc: self.pc.wrapping_add(2), cycles: 3}
                    }
                    LoadTarget::CopySPHL => {
                        self.sp = self.registers.get_hl();
                        ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 2}
                    }
                }
            }
            Instruction::NOP => {
                ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 1}
            }
            Instruction::OR(target) => {
                let value = self.get_target_value(target);
                self.registers.a = value | self.registers.a;
                self.registers.f.zero = self.registers.a == 0;
                self.registers.f.subtract = false;
                self.registers.f.carry = false;
                self.registers.f.half_carry = false;
                self.standard_inc_pc_and_timing(target)
            }
            Instruction::POP(target) => {
                let value = self.pop_stack();
                self.set_u16_reg_value(target, value);
                ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 3}
            }
            Instruction::PUSH(target) => {
                let value = self.get_u16_reg_value(target);
                self.push_stack(value);
                ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 4}
            }
            Instruction::RES(bit, target) => {
                let mut value = self.get_target_value(target);
                value &= !(1 << bit);
                self.set_target_value(target, value);
                match target {
                    Target::U8Register(_) => {ExecutionReturn{pc: self.pc.wrapping_add(2), cycles: 2}}
                    _ => {ExecutionReturn{pc: self.pc.wrapping_add(2), cycles: 4}}
                }
            }
            Instruction::RET(condition) => {
                if self.check_condition(condition) {
                    match condition {
                        Condition::None => {ExecutionReturn{pc: self.pop_stack(), cycles: 4}},
                        _ => {ExecutionReturn{pc: self.pop_stack(), cycles: 5}}
                    }
                } else {
                    ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 2}
                }
            }
            Instruction::RETI => {
                self.interrupts_enabled = true;
                ExecutionReturn{pc: self.pop_stack(), cycles: 4}
            }
            Instruction::RL(target) => {
                let old_value = self.get_target_value(target);
                let old_carry = self.registers.f.carry;
                self.registers.f.carry = old_value & 0x80 == 0x80;
                let new_value = (old_value << 1) | (if old_carry { 0x01 } else { 0 });
                self.set_target_value(target, new_value);
                self.registers.f.subtract = false;
                self.registers.f.half_carry = false;
                match self.bus.read_byte(self.pc) {
                    0x17 => {
                        self.registers.f.zero = false;
                        ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 1}
                    }
                    _ => {
                        self.registers.f.zero = new_value == 0;
                        self.rotate_inc_pc_and_timing(target)
                    }
                }
            }
            Instruction::RLC(target) => {
                let old_value = self.get_target_value(target);
                self.registers.f.carry = old_value & 0x80 == 0x80;
                let new_value = (old_value << 1) | (if self.registers.f.carry { 0x1 } else { 0 });
                self.set_target_value(target, new_value);
                self.registers.f.subtract = false;
                self.registers.f.half_carry = false;
                match self.bus.read_byte(self.pc) {
                    0x07 => {
                        self.registers.f.zero = false;
                        ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 1}
                    }
                    _ => {
                        self.registers.f.zero = new_value == 0;
                        self.rotate_inc_pc_and_timing(target)
                    }
                }
            }
            Instruction::RR(target) => {
                let old_value = self.get_target_value(target);
                let old_carry = self.registers.f.carry;
                self.registers.f.carry = old_value & 0x1 == 0x1;
                let new_value = (old_value >> 1) | (if old_carry { 0x80 } else { 0 });
                self.set_target_value(target, new_value);
                self.registers.f.subtract = false;
                self.registers.f.half_carry = false;
                match self.bus.read_byte(self.pc) {
                    0x1F => {
                        self.registers.f.zero = false;
                        ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 1}
                    }
                    _ => {
                        self.registers.f.zero = new_value == 0;
                        self.rotate_inc_pc_and_timing(target)
                    }
                }
            }
            Instruction::RRC(target) => {
                let old_value = self.get_target_value(target);
                self.registers.f.carry = old_value & 0x1 == 0x1;
                let new_value = (old_value >> 1) | (if self.registers.f.carry { 0x80 } else { 0 });
                self.set_target_value(target, new_value);
                self.registers.f.subtract = false;
                self.registers.f.half_carry = false;
                match self.bus.read_byte(self.pc) {
                    0x0F => {
                        self.registers.f.zero = false;
                        ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 1}
                    }
                    _ => {
                        self.registers.f.zero = new_value == 0;
                        self.rotate_inc_pc_and_timing(target)
                    }
                }
            }
            Instruction::RST(idx) => {
                self.push_stack(self.pc.wrapping_add(1));
                ExecutionReturn{pc: idx as u16 * 8, cycles: 4}
            }
            Instruction::SBC(target) => {
                let value = self.get_target_value(target);
                let carry = self.registers.f.carry as u8;
                let (new_value_with_carry, carry_overflow) = self.registers.a.overflowing_sub(carry);
                let (new_value, did_overflow) = new_value_with_carry.overflowing_sub(value);
                self.registers.f.zero = new_value == 0;
                self.registers.f.subtract = true;
                self.registers.f.carry = did_overflow | carry_overflow;
                let (temp, half_carry) = (self.registers.a & 0xF).overflowing_sub(carry);
                let (_, half_carry2) = temp.overflowing_sub(value & 0xF);
                self.registers.f.half_carry = half_carry | half_carry2;
                self.registers.a = new_value;
                self.standard_inc_pc_and_timing(target)
            }
            Instruction::SCF => {
                self.registers.f.carry = true;
                self.registers.f.subtract = false;
                self.registers.f.half_carry = false;
                ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 1}
            }
            Instruction::SET(bit, target) => {
                let mut value = self.get_target_value(target);
                value |= 1 << bit;
                self.set_target_value(target, value);
                match target {
                    Target::U8Register(_) => {ExecutionReturn{pc: self.pc.wrapping_add(2), cycles: 2}}
                    Target::U16Register(_) => {ExecutionReturn{pc: self.pc.wrapping_add(2), cycles: 4}}
                    Target::Immediate(_) => {panic!("Immediate not supported")}
                }
            }
            Instruction::SLA(target) => {
                let value = self.get_target_value(target);
                let new_value = value << 1;
                self.set_target_value(target, new_value);
                self.registers.f.zero = new_value == 0;
                self.registers.f.subtract = false;
                self.registers.f.carry = value & 0x80 == 0x80;
                self.registers.f.half_carry = false;
                self.rotate_inc_pc_and_timing(target)
            }
            Instruction::SRA(target) => {
                let value = self.get_target_value(target);
                let new_value = value >> 1 | (if value & 0x80 == 0x80 { 0x80 } else { 0 });
                self.set_target_value(target, new_value);
                self.registers.f.zero = new_value == 0;
                self.registers.f.subtract = false;
                self.registers.f.carry = value & 0x1 == 0x1;
                self.registers.f.half_carry = false;
                self.rotate_inc_pc_and_timing(target)
            }
            Instruction::SRL(target) => {
                let value = self.get_target_value(target);
                let new_value = value >> 1;
                self.set_target_value(target, new_value);
                self.registers.f.zero = new_value == 0;
                self.registers.f.subtract = false;
                self.registers.f.carry = value & 0x1 == 0x1;
                self.registers.f.half_carry = false;
                self.rotate_inc_pc_and_timing(target)
            }
            Instruction::STOP(_) => {
                ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 3}
            }
            Instruction::SUB(target) => {
                let value = self.get_target_value(target);
                let (new_value, did_overflow) = self.registers.a.overflowing_sub(value);
                self.registers.a = new_value;
                self.registers.f.zero = new_value == 0;
                self.registers.f.subtract = true;
                self.registers.f.carry = did_overflow;
                self.registers.f.half_carry = (self.registers.a & 0xF) + (value & 0xF) > 0xF;
                self.standard_inc_pc_and_timing(target)
            }
            Instruction::SWAP(target) => {
                let value = self.get_target_value(target);
                let new_value = ((value & 0xF0) >> 4) | ((value & 0x0F) << 4);
                self.set_target_value(target, new_value);
                self.registers.f.zero = new_value == 0;
                self.registers.f.subtract = false;
                self.registers.f.carry = false;
                self.registers.f.half_carry = false;
                self.rotate_inc_pc_and_timing(target)
            }
            Instruction::XOR(target) => {
                let value = self.get_target_value(target);
                self.registers.a = value ^ self.registers.a;
                self.registers.f.zero = self.registers.a == 0;
                self.registers.f.subtract = false;
                self.registers.f.carry = false;
                self.registers.f.half_carry = false;
                match target {
                    Target::U8Register(_) => {ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 1}}
                    Target::U16Register(_) => {ExecutionReturn{pc: self.pc.wrapping_add(1), cycles: 2}}
                    Target::Immediate(_) => {ExecutionReturn{pc: self.pc.wrapping_add(2), cycles: 2}}
                }
            }
        }
    }
}


fn main() {
    // run_instruction_tests()
    run_gameboy()
}

fn run_instruction_tests() {

    for entry in glob("sm83/v1/*.json").unwrap() {
        println!("{:?}", entry);
        let json_file = std::fs::read(entry.unwrap()).unwrap();
        let tests: Value = serde_json::from_slice(json_file.as_slice()).unwrap();
        for test in tests.as_array().unwrap() {
            // println!("{}", test["name"]);
            let mut gameboy = CPU::load_test(&test["initial"]);

            // Get the instruction before executing it
            let pc = gameboy.pc;
            let inst = parse_instruction(
                gameboy.bus.read_byte(pc), 
                gameboy.bus.read_byte(pc.wrapping_add(1)), 
                gameboy.bus.read_byte(pc.wrapping_add(2))
            );

            // Execute the instruction and get the cycle count
            let cycles = gameboy.execute_next_instruction();

            // Verify the cycle count
            let expected_cycles = test["cycles"].as_array().unwrap().len();
            assert_eq!(cycles, expected_cycles as u8);

            // Compare the final state
            gameboy.compare_state(&test["final"]);
        }
    }
}
fn run_gameboy() {
    // Initialize SDL2
    let sdl_context = sdl2::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();

    // Create a window larger than the scaled GameBoy screen to allow for centering
    // Add 60 pixels of padding on each side (30 pixels on each edge)
    let window_width = (SCREEN_WIDTH * 3 + 60) as u32;
    let window_height = (SCREEN_HEIGHT * 3 + 60) as u32;
    let window = video_subsystem.window("GameBoy Emulator", window_width, window_height)
        .position_centered()
        .build()
        .unwrap();

    // Create a renderer
    let mut canvas = window.into_canvas().build().unwrap();
    let texture_creator = canvas.texture_creator();
    let mut texture = texture_creator.create_texture_streaming(
        sdl2::pixels::PixelFormatEnum::RGB24,
        SCREEN_WIDTH as u32,
        SCREEN_HEIGHT as u32
    ).unwrap();

    // Define the GameBoy color palette (from lightest to darkest)
    let gb_colors = [
        Color::RGB(155, 188, 15),  // Light green
        Color::RGB(139, 172, 15),  // Medium green
        Color::RGB(48, 98, 48),    // Dark green
        Color::RGB(15, 56, 15),    // Darkest green
    ];

    // Initialize the GameBoy
    let boot_rom = std::fs::read("boot/dmg.bin").unwrap();
    let game_rom = GameRom::load("gb240p.gb");

    let mut gameboy = CPU{
        registers: Registers{a:0,b:0,c:0,d:0,e:0,f:FlagsRegister::from(0),h:0,l:0},
        pc: 0,
        bus: MemoryBus{
            memory: [0xFFu8; 0xFFFF+1],
            gpu: GPU::new(vec![0xFFu8;VRAM_END-VRAM_BEGIN+1]),
            rom: game_rom,
            boot: boot_rom,
            flat_ram: false
        },
        sp: 0,
        interrupts_enabled: false,
    };
    gameboy.init();
    // Set up event handling
    let mut event_pump = sdl_context.event_pump().unwrap();

    // Main loop
    let mut count = 0;
    let mut last_render_time = Instant::now();
    let frame_duration = Duration::from_millis(16); // ~60 FPS

    'running: loop {
        // Handle events
        for event in event_pump.poll_iter() {
            match event {
                Event::Quit {..} | 
                Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
                    break 'running;
                },
                _ => {}
            }
        }

        // Execute instructions until it's time to render again
        let now = Instant::now();

        // Execute a minimum number of instructions per frame to ensure progress
        let min_instructions_per_frame = 10000;
        let mut instructions_this_frame = 0;

        while now.duration_since(last_render_time) < frame_duration || instructions_this_frame < min_instructions_per_frame {
            gameboy.execute_next_instruction();
            count += 1;
            instructions_this_frame += 1;

            // Avoid getting stuck in this loop if it takes too long
            if instructions_this_frame > min_instructions_per_frame * 2 {
                println!("Warning: Executed {} instructions this frame, breaking loop", instructions_this_frame);
                break;
            }
        }

        // Log the number of instructions executed this frame
        if count % 60 == 0 {  // Log once per second
            println!("Executed {} instructions this frame", instructions_this_frame);
        }

        // Render the screen

        // Debug: Check if the screen buffer has any non-zero values
        let mut non_zero_pixels = 0;
        for y in 0..SCREEN_HEIGHT {
            for x in 0..SCREEN_WIDTH {
                if gameboy.bus.gpu.screen_buffer[y][x] > 0 {
                    non_zero_pixels += 1;
                }
            }
        }

        // Track changes in the screen buffer
        static mut LAST_BUFFER_HASH: u64 = 0;
        static mut FRAMES_SINCE_CHANGE: u32 = 0;
        static mut LAST_CHANGE_COUNT: u32 = 0;

        // Calculate a simple hash of the screen buffer
        let mut buffer_hash: u64 = 0;
        for y in 0..SCREEN_HEIGHT {
            for x in 0..SCREEN_WIDTH {
                buffer_hash = buffer_hash.wrapping_mul(31).wrapping_add(gameboy.bus.gpu.screen_buffer[y][x] as u64);
            }
        }

        // Check if the screen buffer has changed
        unsafe {
            if buffer_hash != LAST_BUFFER_HASH {
                println!("Screen buffer changed at count={} (after {} frames)", 
                         count, FRAMES_SINCE_CHANGE);
                LAST_BUFFER_HASH = buffer_hash;
                FRAMES_SINCE_CHANGE = 0;
                LAST_CHANGE_COUNT = count;
            } else {
                FRAMES_SINCE_CHANGE += 1;
                if FRAMES_SINCE_CHANGE % 60 == 0 {  // Log every second if no change
                    println!("No change in screen buffer for {} frames (last change at count={})", 
                             FRAMES_SINCE_CHANGE, LAST_CHANGE_COUNT);
                }
            }
        }

        // Print debug info about the screen buffer
        if count % 60 == 0 {  // Print once per second at 60 FPS
            println!("Screen buffer has {}/{} non-zero pixels", 
                     non_zero_pixels, SCREEN_WIDTH * SCREEN_HEIGHT);

            // Print a sample of the screen buffer
            println!("Sample of screen buffer:");
            for y in 70..80 {  // Print 10 rows around the middle
                let mut row_str = String::new();
                for x in 0..10 {  // Print first 10 columns
                    row_str.push_str(&format!("{} ", gameboy.bus.gpu.screen_buffer[y][x]));
                }
                println!("Row {}: {}", y, row_str);
            }

            // If no pixels are set, force some pixels to be visible for testing
            if non_zero_pixels == 0 && count < 300 {  // Only do this for the first few frames
                println!("Forcing test pattern in screen buffer");
                for y in 70..80 {
                    for x in 0..10 {
                        // Create a simple pattern: alternating colors
                        gameboy.bus.gpu.screen_buffer[y][x] = ((y + x) % 4) as u8;
                    }
                }
            }
        }

        // Debug: Print texture creation info
        if count % 60 == 0 {
            println!("Creating texture from screen buffer");
        }

        texture.with_lock(None, |buffer: &mut [u8], pitch: usize| {
            for y in 0..SCREEN_HEIGHT {
                for x in 0..SCREEN_WIDTH {
                    let color_idx = gameboy.bus.gpu.screen_buffer[y][x] as usize;
                    let color = gb_colors[color_idx];

                    let offset = y * pitch + x * 3;
                    buffer[offset] = color.r;
                    buffer[offset + 1] = color.g;
                    buffer[offset + 2] = color.b;

                    // Debug: Print color values for a few pixels
                    if count % 60 == 0 && y == 75 && x < 5 {
                        println!("Pixel at ({}, {}): color_idx={}, RGB=({}, {}, {})",
                                 x, y, color_idx, color.r, color.g, color.b);
                    }
                }
            }
        }).unwrap();

        // Clear the canvas with a dark color for the border
        canvas.set_draw_color(Color::RGB(24, 24, 24)); // Dark gray
        canvas.clear();

        // Calculate the position to center the texture in the window
        let window_width = (SCREEN_WIDTH * 3 + 60) as u32;
        let window_height = (SCREEN_HEIGHT * 3 + 60) as u32;
        let x = (window_width - (SCREEN_WIDTH * 3) as u32) / 2;
        let y = (window_height - (SCREEN_HEIGHT * 3) as u32) / 2;

        canvas.copy(&texture, None, Some(Rect::new(x as i32, y as i32, 
                                                 (SCREEN_WIDTH * 3) as u32, 
                                                 (SCREEN_HEIGHT * 3) as u32))).unwrap();
        canvas.present();

        // Update timing
        last_render_time = now;

        // Print debug info more frequently
        if count % 100_000 == 0 {
            println!("PC: {:04X}, Count: {}, Boot ROM: {}", 
                     gameboy.pc, 
                     count, 
                     if gameboy.bus.memory[0xFF50] == 0 { "enabled" } else { "disabled" });

            // Print the current instruction
            let inst = parse_instruction(
                gameboy.bus.read_byte(gameboy.pc), 
                gameboy.bus.read_byte(gameboy.pc.wrapping_add(1)), 
                gameboy.bus.read_byte(gameboy.pc.wrapping_add(2))
            );
            println!("Current instruction: {:?}", inst);

            // Print some key register values
            println!("Registers: A={:02X}, BC={:04X}, DE={:04X}, HL={:04X}, SP={:04X}", 
                     gameboy.registers.a, 
                     gameboy.registers.get_bc(), 
                     gameboy.registers.get_de(), 
                     gameboy.registers.get_hl(), 
                     gameboy.sp);
        }

        // Periodically reset the GPU state to ensure it doesn't get stuck
        static mut RESET_COUNTER: u32 = 0;
        unsafe {
            RESET_COUNTER += 1;
            if RESET_COUNTER >= 5_000_000 {  // Reset every ~5 million instructions
                println!("Periodically resetting GPU state");

                // Re-initialize the LCD control register
                gameboy.bus.gpu.lcd_control.lcd_enabled = true;
                gameboy.bus.gpu.lcd_control.bg_and_window_enable = true;
                gameboy.bus.gpu.lcd_control.bg_tile_map_area = false; // Use 0x9800-0x9BFF
                gameboy.bus.gpu.lcd_control.bg_and_window_tile_area = true; // Use 0x8000-0x8FFF

                // Reset the LCD mode and counters
                gameboy.bus.gpu.mode = LCDMode::OAMSearch;
                gameboy.bus.gpu.mode_clock = 0;

                // Write the LCD control register to memory
                gameboy.bus.memory[LCDC_ADDR] = u8::from(gameboy.bus.gpu.lcd_control);

                // Reset counter
                RESET_COUNTER = 0;
            }
        }
    }
}
