#include "hash_matrix.h"
#include <stdbool.h>

// Lookup table for single bytes (256 entries)
hash_matrix_t BYTE_LOOKUPS[256];

// Initialization flag
static bool initialized = false;

// BIT_LOOKUPS: index 0 = B, index 1 = A
// So if bit is 0, use B; if bit is 1, use A
static inline hash_matrix_t get_bit_matrix(uint8_t b, int bit_pos) {
    int bit = (b >> bit_pos) & 1;
    return bit ? MATRIX_A : MATRIX_B;
}

// Compute hash matrix for a single byte
// Uses the same multiplication order as Rust: ((m7*m6)*(m5*m4))*((m3*m2)*(m1*m0))
static hash_matrix_t mul_from_byte(uint8_t b) {
    hash_matrix_t m0 = get_bit_matrix(b, 0);
    hash_matrix_t m1 = get_bit_matrix(b, 1);
    hash_matrix_t m2 = get_bit_matrix(b, 2);
    hash_matrix_t m3 = get_bit_matrix(b, 3);
    hash_matrix_t m4 = get_bit_matrix(b, 4);
    hash_matrix_t m5 = get_bit_matrix(b, 5);
    hash_matrix_t m6 = get_bit_matrix(b, 6);
    hash_matrix_t m7 = get_bit_matrix(b, 7);

    // Same order as Rust: constmatmul(constmatmul(constmatmul(m7, m6), constmatmul(m5, m4)),
    //                                 constmatmul(constmatmul(m3, m2), constmatmul(m1, m0)))
    hash_matrix_t p76 = matmul(m7, m6);
    hash_matrix_t p54 = matmul(m5, m4);
    hash_matrix_t p32 = matmul(m3, m2);
    hash_matrix_t p10 = matmul(m1, m0);

    hash_matrix_t p7654 = matmul(p76, p54);
    hash_matrix_t p3210 = matmul(p32, p10);

    return matmul(p7654, p3210);
}

// Initialize lookup tables (must be called before hashing)
void init_lookup_tables(void) {
    if (initialized) return;

    for (int i = 0; i < 256; i++) {
        BYTE_LOOKUPS[i] = mul_from_byte((uint8_t)i);
    }

    initialized = true;
}

// Check if tables are initialized
bool lookup_tables_initialized(void) {
    return initialized;
}
