pub enum GpioChipPorts {
    PORT_A,
    PORT_B
}

pub trait GpioChip {
    fn set_port(&mut self, new_a_value: u8, port: GpioChipPorts);
    fn set_ddr(&mut self, new_ddra_value: u8, port: GpioChipPorts);
    fn get_port(&self, port: GpioChipPorts) -> u8;
    fn get_ddr(&self, port: GpioChipPorts) -> u8;
    fn get_in(&self, port: GpioChipPorts) -> u8;
    fn get_out(&self, port: GpioChipPorts) -> u8;
    fn update_ports(&mut self);

    fn split_port(value: u8, ddr: u8) -> (u8, u8) {
        let mut in_val = 0x00;
        let mut out_val = 0x00;

        for bit in 0..8 {
            let mask = 1 << bit;
            let is_output = (ddr & mask) != 0;
            let is_set = (value & mask) != 0;

            match (is_output, is_set) {
                (true, true) => out_val |= mask,
                (false, true) => in_val |= mask,
                _ => {}
            }
        }
        (in_val, out_val)
    }
}


#[cfg(test)]
mod test {
    use crate::periph::mos6520::Mos6520;
    use super::*;

    #[test]
    fn test_split() {
        let params = vec![
            (0xf0, 0xff, 0xf0, 0x0f),
            (0x0f, 0xff, 0x0f, 0xf0),
            (0xff, 0xff, 0xff, 0x00),
            (0x00, 0xff, 0x00, 0xff),
            (0x55, 0xff, 0x55, 0xaa)
        ];

        for (ddr, port, output, input) in params {

            let (calc_ina, calc_oua) = Mos6520::split_port(port, ddr);

            assert_eq!(calc_oua, output);
            assert_eq!(calc_ina, input);
        }
    }
}