use std::time::Instant;
use log::debug;
use crate::constants::constants_via6522::*;

#[derive(Default)]
pub struct Mos6522 {
    /// data direction
    pub(crate) dda: u8,
    pub(crate) ddb: u8,

    /// bottom 4 address bits
    pub(crate) rs0: u8,
    pub(crate) rs1: u8,
    pub(crate) rs2: u8,
    pub(crate) rs3: u8,

    /// external data bus
    pub(crate) data_bus: u8,

    pub(crate) cs1: bool,
    pub(crate) cs2: bool,
    // when true CPU is reading
    pub(crate) rw: bool,

    /// reset circuit - true when reset inited
    pub(crate) reset: bool,

    /// IRQ - true when interrupt waiting
    pub(crate) irq: bool,

    pub(crate) ira: u8,
    pub(crate) ora: u8,
    pub(crate) porta: u8,
    pub(crate) irb: u8,
    pub(crate) orb: u8,
    pub(crate) portb: u8,

    pub(crate) ca1: bool,
    pub(crate) ca2: bool,
    pub(crate) cb1: bool,
    pub(crate) cb2: bool,

    // memory offset for where in the computers memory map this fits
    pub(crate) offset: u16,
    pub(crate) address_bus: u16,
}

impl Mos6522 {
    pub fn max_offset(&self) -> u16 {
        self.offset + 0x10
    }

    pub fn start_clocks(&mut self) {
        loop {
            let cycle_start = Instant::now();
            // let duration = cycle_start.duration_since(self.clock);
            // set the time to the new time.
            // self.clock = cycle_start;
        }
    }
}

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

    #[test]
    fn smoke() { assert!(true); }

    #[test]
    fn registers() {
        let mut x = Mos6522::new();
        x.tick(VIA6522_DDRA as u16, 0b0000_0000, false, true);
        assert_eq!(x.dda, 0b0000_0000);
        x.tick(VIA6522_DDRA as u16, 0b1111_1111, false, true);
        assert_eq!(x.dda, 0b1111_1111);

        x.tick(VIA6522_DDRB as u16, 0b0000_0000, false, true);
        assert_eq!(x.ddb, 0b0000_0000);
        x.tick(VIA6522_DDRB as u16, 0b1111_1111, false, true);
        assert_eq!(x.ddb, 0b1111_1111);

        x.tick(VIA6522_ORA as u16, 0b0000_0000, false, true);
        assert_eq!(x.ora, 0b0000_0000);
        x.tick(VIA6522_ORA as u16, 0b1111_1111,  false, true);
        assert_eq!(x.ora, 0b1111_1111);

        x.tick(VIA6522_ORB as u16, 0b0000_0000, false, true);
        assert_eq!(x.orb, 0b0000_0000);
        x.tick(VIA6522_ORB as u16, 0b1111_1111, false, true);
        assert_eq!(x.orb, 0b1111_1111);
    }

    #[test]
    fn partial_output_porta() {
        let mut x = Mos6522::new();
        x.tick(VIA6522_DDRA as u16, 0b1010_1010, false, true);
        x.tick(VIA6522_ORA as u16,0b1111_1111, false, true);
        assert_eq!(x.porta, 0b1010_1010);
    }

    #[test]
    fn partial_output_portb() {
        let mut x = Mos6522::new();
        x.tick(VIA6522_DDRB as u16, 0b0101_0101, false, true);
        x.tick(VIA6522_ORB as u16, 0b1111_1111, false, true);
        assert_eq!(x.portb, 0b0101_0101);
    }
}
