//! GIFT-256 fixsliced encryption (13 rounds)
//! Corresponds to gift256_encrypt_fixsliced @ 0x80048469
//!
//! Round structure: 4 cycles x {SBox+P1+KeyXOR, SBox+DA+KeyXOR, SBox+P2+KeyXOR, SBox+DB+KeyXOR}
//! Last cycle only executes the first type (SBox+P1+KeyXOR)

use super::sbox::sbox_bitsliced;
use super::linear::{linear_p1, linear_p2, diffusion_a, diffusion_b};
use super::interleave::{pack_input, nibble_deinterleave, unpack_output};

/// GIFT-256 encryption.
/// input:  256-bit plaintext (8 u32, little-endian)
/// rk:     120 u32 round keys (from key_schedule)
/// output: 256-bit ciphertext (8 u32)
pub fn encrypt(input: &[u32; 8], rk: &[u32; 120]) -> [u32; 8] {
    // === Input packing (bit-interleave + initial round key XOR) ===
    let initial_rk: [u32; 8] = rk[0..8].try_into().unwrap();
    let mut s = pack_input(input, &initial_rk);

    // === 13 rounds main loop (4 cycles) ===
    // Round key indexing:
    //   Initial: rk[0..7]
    //   Type A: rk[base+8 .. base+15]
    //   Type DA: rk[base+16 .. base+23]
    //   Type P2: rk[base+24 .. base+31]
    //   Type DB: rk[base+32 .. base+39]
    // where base = cycle * 32

    for cycle in 0..4 {
        let base = cycle * 32;

        // --- Round Type A: SBox + P1 + KeyXOR ---
        sbox_bitsliced(&mut s);
        linear_p1(&mut s);
        let rk_a: [u32; 8] = rk[base + 8..base + 16].try_into().unwrap();
        for j in 0..8 {
            s[j] ^= rk_a[j];
        }

        if cycle == 3 {
            break; // Last cycle only has Type A
        }

        // --- Round Type DA: SBox + nibble-ROT diffusion + KeyXOR ---
        sbox_bitsliced(&mut s);
        let rk_da: [u32; 8] = rk[base + 16..base + 24].try_into().unwrap();
        diffusion_a(&mut s, &rk_da);

        // --- Round Type P2: SBox + P2 + KeyXOR ---
        sbox_bitsliced(&mut s);
        linear_p2(&mut s);
        let rk_p2: [u32; 8] = rk[base + 24..base + 32].try_into().unwrap();
        for j in 0..8 {
            s[j] ^= rk_p2[j];
        }

        // --- Round Type DB: SBox + byte-ROT8 diffusion + KeyXOR ---
        sbox_bitsliced(&mut s);
        let rk_db: [u32; 8] = rk[base + 32..base + 40].try_into().unwrap();
        diffusion_b(&mut s, &rk_db);
    }

    // === Output deinterleave ===
    for j in 0..8 {
        s[j] = nibble_deinterleave(s[j]);
    }

    // Final SBox
    sbox_bitsliced(&mut s);

    // Final round key XOR + deinterleave (rk[112..119])
    let final_rk: [u32; 8] = rk[112..120].try_into().unwrap();
    unpack_output(&s, &final_rk)
}
