use crate::{block::BLOCK, encrypt::sbox, keyGen::Keys};

fn inv_std_round(block: &mut BLOCK, key: BLOCK) {
    *block = *block ^ key;
    inv_mix_columns(block);
    inv_shift_row(block);
    sbox(block, false);
}
pub fn full_decrypt(block: &mut BLOCK, keys: &Keys) {
    *block = *block ^ keys.keyBlocks[9];
    inv_shift_row(block);
    sbox(block, false);
    for i in (1..9).rev() {
        inv_std_round(block, keys.keyBlocks[i])
    }
    *block = *block ^ keys.keyBlocks[0];
    block.flipyx();
}


fn inv_mix_columns(block: &mut BLOCK) {
    fn g_mul(mut a: u8, b: u8) -> u8 {
        let mut p = 0;
        let mut b = b;
        for _ in 0..8 {
            if b & 1 != 0 {
                p = a ^ p;
            }

            let high_bit_set = a & 0x80 != 0;
            a <<= 1;

            if high_bit_set {
                a = a^ 0x1b;
            }

            b >>= 1;
        }

        p
    }

    let mut output = BLOCK::new([0; 16]);
    //14 11 13 9
    for c in 0..4 {
        output.0[c][0] = g_mul(14, block.0[c][0])
            ^ g_mul(11, block.0[c][1])
            ^ g_mul(13, block.0[c][2])
            ^ g_mul(9, block.0[c][3]);
        output.0[c][1] = g_mul(9, block.0[c][0])
            ^ g_mul(14, block.0[c][1])
            ^ g_mul(11, block.0[c][2])
            ^ g_mul(13, block.0[c][3]);
        output.0[c][2] = g_mul(13, block.0[c][0])
            ^ g_mul(9, block.0[c][1])
            ^ g_mul(14, block.0[c][2])
            ^ g_mul(11, block.0[c][3]);
        output.0[c][3] = g_mul(11, block.0[c][0])
            ^ g_mul(13, block.0[c][1])
            ^ g_mul(9, block.0[c][2])
            ^ g_mul(14, block.0[c][3]);
    }

    *block = output;
}

fn inv_shift_row(block: &mut BLOCK) {
    //  i was tempted just to use the .rotate_left(x) function but i thought that this may be quicker.   less to call i guess?
    for row in 1..4 {
        for _ in 0..row {
            let buf = block.0[row][0];
            for i in 0..3 {
                block.0[row][i] = block.0[row][(i + 1) % 4];
            }
            block.0[row][3] = buf;
        }
    }
}

#[cfg(test)]
mod dec_tests {
    use super::inv_shift_row;
    use crate::{
        block::BLOCK,
        encrypt::{full_encrypt, sbox},
        keyGen,
    };

    #[test]
    fn rev_shift_row_test() {
        let mut first = BLOCK::new([
            57, 160, 179, 25, 70, 55, 12, 124, 30, 165, 124, 35, 110, 134, 26, 22,
        ]);
        inv_shift_row(&mut first);
        assert_eq!(
            first.0,
            [
                [57, 160, 179, 25],
                [55, 12, 124, 70],
                [124, 35, 30, 165],
                [22, 110, 134, 26]
            ]
        );
    }
    #[test]
    fn rev_sbox_test() {
        let mut encrypted = BLOCK::new([
            57, 160, 179, 25, 70, 55, 12, 124, 30, 165, 124, 35, 110, 134, 26, 22,
        ]);
        sbox(&mut encrypted, true);
        let mut decrypted = encrypted.clone();
        sbox(&mut decrypted, false);
        assert_eq!(
            decrypted.0,
            BLOCK::new([57, 160, 179, 25, 70, 55, 12, 124, 30, 165, 124, 35, 110, 134, 26, 22]).0
        )
    }
    #[test]
    fn time_enc() {
        let mut block = BLOCK::new([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
        let keys = keyGen::Keys::from(BLOCK::new([
            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
        ]));
        full_encrypt(&mut block, &keys);
    }
}
