#include "utils.h"


const char b64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

size_t b64tamanoCodificado(size_t inlen) {
    size_t ret;
    ret = inlen;
    if (inlen % 3 != 0)
        ret += 3 - (inlen % 3);
    ret /= 3;
    ret *= 4;
    return ret;
}

int aleatorioInt32(int min, int max) {
    return (rand() % (max - min + 1)) + min;
}

char* b64Codificado(const unsigned char* in, SIZE_T len) {
    char* out;
    SIZE_T  elen;
    SIZE_T  i;
    SIZE_T  j;
    SIZE_T  v;

    if (in == NULL || len == 0) {
        return NULL;
    }

    elen = b64tamanoCodificado(len);
    out = (char*)LocalAlloc(LPTR, elen + 1);
    if (!out) {
        return NULL;
    }
    out[elen] = '\0';

    for (i = 0, j = 0; i < len; i += 3, j += 4) {
        v = in[i];
        v = i + 1 < len ? v << 8 | in[i + 1] : v << 8;
        v = i + 2 < len ? v << 8 | in[i + 2] : v << 8;

        out[j] = b64chars[(v >> 18) & 0x3F];
        out[j + 1] = b64chars[(v >> 12) & 0x3F];
        if (i + 1 < len) {
            out[j + 2] = b64chars[(v >> 6) & 0x3F];
        } else {
            out[j + 2] = '=';
        }
        if (i + 2 < len) {
            out[j + 3] = b64chars[v & 0x3F];
        } else {
            out[j + 3] = '=';
        }
    }

    return out;
}

SIZE_T b64tamanoDecodificado(const char* in) {
    SIZE_T len;
    SIZE_T ret;
    SIZE_T i;

    if (in == NULL) {
        return 0;
    }

    len = strlen(in);
    ret = len / 4 * 3;

    for (i = len; i-- > 0; ) {
        if (in[i] == '=') {
            ret--;
        } else {
            break;
        }
    }

    return ret;
}

static int b64_inverse(char c) {
    if (c >= 'A' && c <= 'Z') return c - 'A';
    if (c >= 'a' && c <= 'z') return c - 'a' + 26;
    if (c >= '0' && c <= '9') return c - '0' + 52;
    if (c == '+') return 62;
    if (c == '/') return 63;
    return -1;
}

int b64Decodificado(const char* in, unsigned char* out, SIZE_T outlen) {
    if (!in || !out) return 0;

    SIZE_T len = strlen(in);
    if (len % 4 != 0) return 0;

    SIZE_T i = 0, j = 0;
    while (i < len) {
        int a = b64_inverse(in[i]);
        int b = b64_inverse(in[i+1]);
        int c = (in[i+2] == '=') ? -1 : b64_inverse(in[i+2]);
        int d = (in[i+3] == '=') ? -1 : b64_inverse(in[i+3]);

        if (a < 0 || b < 0 || (in[i+2] != '=' && c < 0) || (in[i+3] != '=' && d < 0))
            return 0;

        out[j++] = (a << 2) | (b >> 4);
        if (c >= 0) out[j++] = ((b & 0xF) << 4) | (c >> 2);
        if (d >= 0) out[j++] = ((c & 0x3) << 6) | d;
        i += 4;
    }
    return 1;
}

VOID addInt32ToBuffer(PBYTE buffer, UINT32 value) {
    (buffer)[0] = (value >> 24) & 0xFF;
    (buffer)[1] = (value >> 16) & 0xFF;
    (buffer)[2] = (value >> 8) & 0xFF;
    (buffer)[3] = (value) & 0xFF;
}

VOID addInt64ToBuffer(PBYTE buffer, UINT64 value) {
    buffer[7] = value & 0xFF; value >>= 8;
    buffer[6] = value & 0xFF; value >>= 8;
    buffer[5] = value & 0xFF; value >>= 8;
    buffer[4] = value & 0xFF; value >>= 8;
    buffer[3] = value & 0xFF; value >>= 8;
    buffer[2] = value & 0xFF; value >>= 8;
    buffer[1] = value & 0xFF; value >>= 8;
    buffer[0] = value & 0xFF;
}

/* =========================================================
   Wrappers para interfaz simple de base64 (socks_manager)
   - base64_decode: asigna *out con LocalAlloc; devuelve 1 en ok.
   - base64_encode: asigna *out_b64 con LocalAlloc; devuelve 1 en ok.
   ========================================================= */

int base64_decode(const char *b64, unsigned char **out, size_t *out_len) {
    if (!b64 || !out || !out_len) return 0;
    SIZE_T need = b64tamanoDecodificado(b64);
    if (need == 0) {
        *out = NULL;
        *out_len = 0;
        return 1; /* empty input is OK */
    }

    unsigned char *buf = (unsigned char*)LocalAlloc(LPTR, need);
    if (!buf) {
        _err("base64_decode: LocalAlloc failed for %zu bytes", need);
        return 0;
    }

    int ok = b64Decodificado(b64, buf, need);
    if (!ok) {
        LocalFree(buf);
        _err("base64_decode: b64Decodificado failed");
        return 0;
    }

    *out = buf;
    *out_len = (size_t)need;
    _dbg("base64_decode: decoded %zu bytes", *out_len);
    return 1;
}

int base64_encode(const unsigned char *data, size_t dlen, char **out_b64) {
    if ((!data && dlen>0) || !out_b64) return 0;
    if (dlen == 0) {
        *out_b64 = (char*)LocalAlloc(LPTR, 1);
        if (!*out_b64) return 0;
        (*out_b64)[0] = '\0';
        return 1;
    }

    char *s = b64Codificado(data, (SIZE_T)dlen);
    if (!s) {
        _err("base64_encode: b64Codificado failed");
        return 0;
    }

    /* b64Codificado already uses LocalAlloc in original impl */
    *out_b64 = s;
    _dbg("base64_encode: encoded len=%zu -> out=%p", dlen, (void*)*out_b64);
    return 1;
}

/* Hash function for BOF function resolution (FNV-1a algorithm) */
#define HASH_SEED 0x811C9DC5
#define HASH_PRIME 0x01000193

UINT32 custom_hash(const char *data) {
    UINT32 hash = HASH_SEED;
    if (!data) return 0;
    while (*data) {
        hash ^= (UINT8)(*data);
        hash *= HASH_PRIME;
        data++;
    }
    return hash;
}