//! Matyas-Meyer-Oseas compression: H_i = E_K(0) ⊕ double(extract(E_K(0)))
//! Corresponds to mmo_compress @ 0x8004fbac

use crate::gift256::encrypt::encrypt;

/// MMO compression state
pub struct MmoState {
    pub round_keys: [u32; 120],
    pub chaining: [u8; 16],
}

/// MMO compression function.
/// Decompiled flow:
///   1. Encrypt all-zero plaintext: gift256_encrypt(ciphertext, round_keys, zeros)
///   2. Extract bytes 0x20..0x2F (16 bytes) from ciphertext buffer
///   3. Assemble as two u64 (big-endian): hi=bytes[0x20..0x27], lo=bytes[0x28..0x2F]
///   4. GF(2^128) doubling: {hi,lo} <<= 1, if carry: hi ^= (carry<<62) | (carry<<57)
///   5. Write into chaining value, copy round keys
pub fn mmo_compress(round_keys: &[u32; 120]) -> MmoState {
    // 1. Encrypt all-zero plaintext
    let plaintext = [0u32; 8];
    let ciphertext = encrypt(&plaintext, round_keys);

    // 2. Convert ciphertext to bytes (little-endian u32 layout)
    let ct_bytes = ciphertext_to_bytes(&ciphertext);

    // 3. Build hi and lo u64 from bytes at offset 0x20 (= first 16 bytes of ciphertext)
    // The ciphertext buffer in the decompilation starts at the plaintext+ciphertext stack area
    // offset 0x20 from the base = the ciphertext portion (bytes 0..15)
    let hi = u64::from_be_bytes([
        ct_bytes[0], ct_bytes[1], ct_bytes[2], ct_bytes[3],
        ct_bytes[4], ct_bytes[5], ct_bytes[6], ct_bytes[7],
    ]);
    let lo = u64::from_be_bytes([
        ct_bytes[8], ct_bytes[9], ct_bytes[10], ct_bytes[11],
        ct_bytes[12], ct_bytes[13], ct_bytes[14], ct_bytes[15],
    ]);

    // 4. GF(2^128) doubling: shift left by 1
    let carry = hi >> 63;
    let mut hi = (hi << 1) | (lo >> 63);
    let lo = lo << 1;

    // Feedback polynomial: if carry bit set, XOR with reduction polynomial
    // Decompiled: (uVar16 << 0x38 & 0x8000000000000000 | uVar18 << 0x3e | uVar18 << 0x39)
    // where uVar18 = carry bit
    if carry != 0 {
        hi ^= (1u64 << 62) | (1u64 << 57);
    }

    // 5. Write chaining value (big-endian)
    let mut chaining = [0u8; 16];
    chaining[0..8].copy_from_slice(&hi.to_be_bytes());
    chaining[8..16].copy_from_slice(&lo.to_be_bytes());

    MmoState {
        round_keys: *round_keys,
        chaining,
    }
}

fn ciphertext_to_bytes(ct: &[u32; 8]) -> [u8; 32] {
    let mut bytes = [0u8; 32];
    for i in 0..8 {
        let b = ct[i].to_le_bytes();
        bytes[i * 4..i * 4 + 4].copy_from_slice(&b);
    }
    bytes
}
