use crate::parts::display_matrix::HD44780;
use crate::parts::mos6522_peripheral::Mos6522Peripheral;
use core::constants::constants_system::*;
#[derive(Default)]
pub struct VIA6522 {
    // Data registers
    ora: u8,
    orb: u8,

    // Data direction
    ddra: u8,
    ddrb: u8,

    // Control lines (external pins)
    ca1: bool,
    ca2: bool,
    cb1: bool,
    cb2: bool,

    // Timers
    t1_counter: u16,
    t1_latch: u16,
    t1_enabled: bool,

    t2_counter: u16,
    t2_latch: u16,
    t2_enabled: bool,

    // Interrupt flags
    ifr: u8,
    ier: u8,

    // Peripheral (e.g., LCD)
    pub lcd: Option<Box<dyn Mos6522Peripheral>>,
}

impl VIA6522 {
    pub fn new() -> Self {
        Self {
            ora: 0,
            orb: 0,
            ddra: 0,
            ddrb: 0,
            ca1: false,
            ca2: false,
            cb1: false,
            cb2: false,
            t1_counter: 0,
            t1_latch: 0,
            t1_enabled: false,
            t2_counter: 0,
            t2_latch: 0,
            t2_enabled: false,
            ifr: 0,
            ier: 0,
            lcd: Some(Box::new(HD44780::new())),
        }
    }

    pub fn read(&self, addr: u8) -> u8 {
        match (addr & 0x0F) {
            VIA6522_ORB => self.orb,
            VIA6522_ORA => self.ora,
            VIA6522_DDRB => self.ddrb,
            VIA6522_DDRA => self.ddra,
            0x4 => (self.t1_counter & 0xFF) as u8,
            0x5 => (self.t1_counter >> 8) as u8,
            0x6 => (self.t1_latch & 0xFF) as u8,
            0x7 => (self.t1_latch >> 8) as u8,
            0x8 => (self.t2_counter & 0xFF) as u8,
            0x9 => (self.t2_latch & 0xFF) as u8,
            0xD => self.ifr,
            0xE => self.ier | 0x80,
            _ => 0,
        }
    }

    pub fn write(&mut self, addr: u8, value: u8) {
        println!("VIA6522 write value 0x{value:02x} to address 0x{addr:02x}");
        match addr & 0x0F {
            VIA6522_ORB => self.orb = value,
            VIA6522_ORA => self.ora = value,
            VIA6522_DDRB => self.ddrb = value,
            VIA6522_DDRA => self.ddra = value,
            0x4 => {
                self.t1_latch = (self.t1_latch & 0xFF00) | value as u16;
                self.t1_counter = self.t1_latch;
            }
            0x5 => {
                self.t1_latch = (value as u16) << 8 | (self.t1_latch & 0x00FF);
                self.t1_counter = self.t1_latch;
                self.t1_enabled = true;
            }
            0x6 => self.t1_latch = (self.t1_latch & 0xFF00) | value as u16,
            0x7 => self.t1_latch = (value as u16) << 8 | (self.t1_latch & 0x00FF),
            0x8 => self.t2_counter = value as u16,
            0x9 => self.t2_latch = value as u16,
            0xD => self.ifr &= !value, // Clear interrupt flags
            0xE => {
                if value & 0x80 != 0 {
                    self.ier |= value & 0x7F;
                } else {
                    self.ier &= !(value & 0x7F);
                }
            }
            _ => {}
        }
    }

    pub fn tick(&mut self) {
        if self.t1_enabled {
            if self.t1_counter > 0 {
                self.t1_counter -= 1;
                if self.t1_counter == 0 {
                    self.ifr |= 0x40; // Set T1 interrupt
                }
            }
        }

        if self.t2_enabled {
            if self.t2_counter > 0 {
                self.t2_counter -= 1;
                if self.t2_counter == 0 {
                    self.ifr |= 0x20; // Set T2 interrupt
                }
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_initialization() {
        let via = VIA6522::new();
        assert_eq!(via.ora, 0);
        assert_eq!(via.orb, 0);
        assert_eq!(via.ddra, 0);
        assert_eq!(via.ddrb, 0);
        assert_eq!(via.t1_counter, 0);
        assert_eq!(via.t2_counter, 0);
        assert!(via.lcd.is_some());
    }

    #[test]
    fn test_read_write_registers() {
        let mut via = VIA6522::new();

        via.write(0x0, 0xAA);
        assert_eq!(via.read(0x0), 0xAA);

        via.write(0x1, 0xBB);
        assert_eq!(via.read(0x1), 0xBB);

        via.write(0x2, 0xCC);
        assert_eq!(via.read(0x2), 0xCC);

        via.write(0x3, 0xDD);
        assert_eq!(via.read(0x3), 0xDD);
    }

    #[test]
    #[ignore]
    fn test_timer1_write_and_tick() {
        let mut via = VIA6522::new();

        via.write(0x4, 0x34); // Low byte
        via.write(0x5, 0x12); // High byte, also enables timer
        assert_eq!(via.t1_latch, 0x1234);
        assert_eq!(via.t1_counter, 0x1234);
        assert!(via.t1_enabled);

        for _ in 0..0x1234 {
            via.tick();
        }

        assert_eq!(via.t1_counter, 0);
        assert_eq!(via.ifr & 0x40, 0x40); // T1 interrupt flag set
    }

    #[test]
    #[ignore]
    fn test_timer2_write_and_tick() {
        let mut via = VIA6522::new();
        via.t2_enabled = true;

        via.write(0x8, 0x05); // Counter
        for _ in 0..5 {
            via.tick();
        }

        assert_eq!(via.t2_counter, 0);
        assert_eq!(via.ifr & 0x20, 0x20); // T2 interrupt flag set
    }

    #[test]
    #[ignore]
    fn test_interrupt_enable_disable() {
        let mut via = VIA6522::new();

        via.write(0xE, 0x82); // Enable bit 0x02
        assert_eq!(via.ier & 0x02, 0x02);

        via.write(0xE, 0x02); // Disable bit 0x02
        assert_eq!(via.ier & 0x02, 0x00);
    }

    #[test]
    #[ignore]
    fn test_clear_interrupt_flags() {
        let mut via = VIA6522::new();
        via.ifr = 0xFF;

        via.write(0xD, 0x40); // Clear T1 interrupt
        assert_eq!(via.ifr & 0x40, 0x00);
    }
}
