pub mod default;
pub mod rom_chip;
pub mod new;
pub mod program;
pub mod dump;
pub mod checksum;
pub mod blocks;
pub mod control;
pub mod signal_tick;
pub mod memory_chip;
pub mod bus_device;


/// At28C256
///
/// Represents a single At28C256 EEPROM Chip
///
/// 256kbit storage
/// 32kbyte storage
pub struct At28C256 {
    // Logical parts
    data_bus: u8,
    address_bus: u16,
    data: Box<[u8]>,
    // where in the computer memory map do we live?
    offset: u16,
    max_offset: u16,
    // Physical Parts
    cs: bool,
    we: bool,
    oe: bool
}

#[cfg(test)]
mod test {
    use crate::traits::rom_chip::RomChip;
    use std::io::Read;
    use std::fs;
    use crate::constants::constants_system::*;
    use crate::constants::constants_test::*;
    use crate::periph::at28c256::At28C256;
    use crate::traits::bus_device::BusDevice;
    use crate::traits::memory_chip::MemoryChip;

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

    #[test]
    fn checksum_binary_loads() {
        let path = format!("{}/{}", TEST_PERIPH_AT28C256_ROOT, "checksum.bin");
        let bytes = match fs::read(path) {
            Ok(bytes) => {
                println!("Read {} bytes.", bytes.len());
                bytes
            },
            Err(e) => {
                eprintln!("FAIL to read rom.");
                panic!("No rom no run.");
            }
        };

        let mut rom = At28C256::new(0x0000, 0x3fff, bytes);

        assert_eq!(rom.checksum(), 0x58);
    }

    #[test]
    fn full_chunks_come_back_ok() {
        let test_data = (0..255).collect();
        let mut chip = At28C256::new(0x0000, 0x3fff, test_data);

        let chunks = chip.chunks(16);
        assert_eq!(chunks.len(), 16);
    }

    #[test]
    fn partial_blocks_come_back_ok() {
        let test_data = (0..=3).collect();
        let mut chip = At28C256::new(0x0000, 0x3fff, test_data);

        let chunks = chip.chunks(16);
        assert_eq!(chunks.len(), 1);
        for chunk in chunks {
            assert_eq!(chunk.len(), 4);
        }
    }


    #[test]
    fn programmed_data_reads_back_same() {
        let mut data = At28C256::default();
        for i in 0..SIZE_32KB {
            data.data[i] = 0xeau8;
        }
        for offset in 0..(SIZE_32KB - 1) {
            assert_eq!(0xea, data.read(&(offset as u16)));
        }
    }

    #[test]
    fn checksums_calculate_correctly_for_zero() {
        let data1 = [0x00u8; SIZE_32KB];
        assert_eq!(0x00, At28C256::checksum_static(&data1));
    }

    #[test]
    fn checksums_calculate_for_1_byte() {
        let data = [0xff; 1];
        assert_eq!(0xff, At28C256::checksum_static(&data));
    }

    #[test]
    fn checksums_calculate_for_2_bytes() {
        let data = [0xff; 2];
        // 0xff + 0xff = 0x1fe
        assert_eq!(0xfe, At28C256::checksum_static(&data));
    }

    #[test]
    fn checksums_calculate_for_first_80_bytes() {
        println!("STARTING TEST");
        let mut checksum = 0x00;

        let path = format!("{}{}", TEST_PERIPH_AT28C256_ROOT, "/checksum.bin");
        println!("READING [{path}]");
        let data = fs::read(path);
        match data {
            Ok(bytes) => {
                println!("Read {} bytes", bytes.len());
                checksum = At28C256::checksum_static(&bytes);
                println!("Checksum: 0x{:02x}", checksum);
            }
            Err(e) => eprintln!("Failed to read file: {}", e),
        }
        assert_eq!(0x58, checksum);
        println!("TEST COMPLETE");
    }

    #[test]
    fn address_data_set_reads() {
        let mut x = At28C256::new(0x0000, 0x3fff, vec![0xea; SIZE_32KB]);

        // set both...
        x.set_address_bus(0x3000);
        x.set_data_bus(0xab);
        assert_eq!(0xab, x.data_bus());
        assert_eq!(0x3000, x.address_bus());

        // ...set address...
        x.set_address_bus(0x2000);
        assert_eq!(0x2000, x.address_bus());
        assert_eq!(0xab, x.data_bus());

        // ...set data.
        x.set_data_bus(0xba);
        assert_eq!(0xba, x.data_bus());
        assert_eq!(0x2000, x.address_bus());
    }

    fn programming_chip_changes_contents() {
        let mut chip = At28C256::new(0x0000, 0x3fff, vec![]);

        assert_eq!(0x00, chip.read(&0x0000));

        let new_data: Vec<u8> = vec![0xff, 0xff, 0xff, 0xff];
        chip.program(new_data.into());
        assert_eq!(0xff, chip.read(&0x0000));
        assert_eq!(0x00, chip.read(&0x05));
    }

    #[test]
    fn correct_flags_required() {
        let mut chip = At28C256::new(0x0000, 0x3fff, vec![0xff]);
        assert_eq!(0xab, chip.signal_tick(0x0000, 0xab, false, true, true));
        assert_eq!(0xab, chip.signal_tick(0x0000, 0xab, true, true, false));
        assert_eq!(0xab, chip.signal_tick(0x0000, 0xab, false, true, false));
        assert_eq!(0xff, chip.signal_tick(0x0000, 0xab, true, true, true));
        assert_eq!(0xab, chip.signal_tick(0x0000, 0xab, true, false, true));
    }

}
