#include "sha256_hasher.h"
#include <string.h>

// Platform detection declarations (from other cpp files)
TransformFunc detect_x86_transform(void);
TransformFunc detect_armv8_transform(void);

void sha256_init(Sha256State* hasher) {
    hasher->buffer_len = 0;
    hasher->total_len = 0;
    memcpy(hasher->state, H0, sizeof(H0));
    
    // Detect best transform implementation
    TransformFunc f = detect_best_transform();
    hasher->transform_fn = f ? f : transform_generic;
}

void sha256_update(Sha256State* hasher, const uint8_t* data, size_t len) {
    hasher->total_len += len;
    
    // Fill buffer if there's pending data
    if (hasher->buffer_len > 0) {
        size_t space = 64 - hasher->buffer_len;
        size_t to_copy = len < space ? len : space;
        memcpy(hasher->buffer + hasher->buffer_len, data, to_copy);
        hasher->buffer_len += to_copy;
        data += to_copy;
        len -= to_copy;
        
        if (hasher->buffer_len == 64) {
            hasher->transform_fn(hasher->state, hasher->buffer);
            hasher->buffer_len = 0;
        }
    }
    
    // Process full blocks
    while (len >= 64) {
        hasher->transform_fn(hasher->state, data);
        data += 64;
        len -= 64;
    }
    
    // Store remaining data
    if (len > 0) {
        memcpy(hasher->buffer, data, len);
        hasher->buffer_len = len;
    }
}

static void sha256_pad_and_finalize(Sha256State* hasher) {
    uint64_t bit_len = hasher->total_len * 8;
    
    // Padding
    hasher->buffer[hasher->buffer_len++] = 0x80;
    
    if (hasher->buffer_len > 56) {
        while (hasher->buffer_len < 64) hasher->buffer[hasher->buffer_len++] = 0;
        hasher->transform_fn(hasher->state, hasher->buffer);
        hasher->buffer_len = 0;
    }
    
    while (hasher->buffer_len < 56) hasher->buffer[hasher->buffer_len++] = 0;
    
    // Append length (big-endian)
    for (int i = 7; i >= 0; --i) {
        hasher->buffer[56 + (7 - i)] = (bit_len >> (i * 8)) & 0xff;
    }
    
    hasher->transform_fn(hasher->state, hasher->buffer);
}

void sha256_finalize(Sha256State* hasher, uint8_t out[32]) {
    sha256_pad_and_finalize(hasher);
    
    // Output (big-endian)
    for (int i = 0; i < 8; ++i) {
        out[i * 4] = (hasher->state[i] >> 24) & 0xff;
        out[i * 4 + 1] = (hasher->state[i] >> 16) & 0xff;
        out[i * 4 + 2] = (hasher->state[i] >> 8) & 0xff;
        out[i * 4 + 3] = hasher->state[i] & 0xff;
    }
}

static void bytes_to_hex(const uint8_t data[32], char* out) {
    static const char hex[] = "0123456789abcdef";
    for (int i = 0; i < 32; ++i) {
        out[i * 2] = hex[(data[i] >> 4) & 0xf];
        out[i * 2 + 1] = hex[data[i] & 0xf];
    }
    out[64] = '\0';
}

void sha256_hash_to_hex(const uint8_t* data, size_t len, char* out_hex) {
    Sha256State hasher;
    sha256_init(&hasher);
    sha256_update(&hasher, data, len);
    uint8_t result[32];
    sha256_finalize(&hasher, result);
    bytes_to_hex(result, out_hex);
}

TransformFunc detect_best_transform(void) {
    // Try platform-specific first
    TransformFunc f = detect_armv8_transform();
    if (f) return f;
    f = detect_x86_transform();
    if (f) return f;
    return transform_generic;
}

const char* sha256_impl_name(void) {
    TransformFunc f = detect_best_transform();
    TransformFunc arm = detect_armv8_transform();
    TransformFunc x86 = detect_x86_transform();
    if (f == arm) return "ARMv8";
    if (f == x86) return "SHA-NI";
    return "generic";
}

int sha256_has_hardware_accel(void) {
    return detect_best_transform() != transform_generic;
}
