#include "qjs_text.h"
#include "qjs_blob.h"
#include "blob.h"
#include "jsffi.h"
#include <string.h>
#include <stdlib.h>

JSC_CCALL(text_blob_to_hex,
  size_t blob_len;
  void *blob_data = js_get_blob_data(js, &blob_len, argv[0]);
  if (!blob_data) return JS_ThrowTypeError(js, "Expected stone blob");
  
  uint8_t *bytes = (uint8_t *)blob_data;
  
  // Hex encoding: each byte becomes 2 hex characters
  size_t hex_len = blob_len * 2;
  char *hex_str = malloc(hex_len + 1);
  
  static const char hex_digits[] = "0123456789ABCDEF";
  
  for (size_t i = 0; i < blob_len; i++) {
    hex_str[i * 2] = hex_digits[(bytes[i] >> 4) & 0xF];
    hex_str[i * 2 + 1] = hex_digits[bytes[i] & 0xF];
  }
  
  hex_str[hex_len] = '\0';
  ret = JS_NewString(js, hex_str);
  free(hex_str);
)

JSC_CCALL(text_blob_to_base32,
  size_t blob_len;
  void *blob_data = js_get_blob_data(js, &blob_len, argv[0]);
  if (!blob_data) return JS_ThrowTypeError(js, "Expected stone blob");
  
  uint8_t *bytes = (uint8_t *)blob_data;
  
  static const char b32_digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
  
  // Calculate output length: each 5 bytes becomes 8 base32 chars
  size_t groups = (blob_len + 4) / 5;
  size_t b32_len = groups * 8;
  char *b32_str = malloc(b32_len + 1);
  
  size_t in_idx = 0;
  size_t out_idx = 0;
  
  while (in_idx < blob_len) {
    // Process 5 bytes (40 bits) at a time to produce 8 base32 chars
    uint64_t buf = 0;
    int bytes_read = 0;
    
    // Read up to 5 bytes into buffer
    for (int i = 0; i < 5 && in_idx < blob_len; i++) {
      buf = (buf << 8) | bytes[in_idx++];
      bytes_read++;
    }
    
    // Pad with zeros if we read fewer than 5 bytes
    buf = buf << (8 * (5 - bytes_read));
    
    // Extract 8 groups of 5 bits from the 40-bit buffer
    for (int i = 7; i >= 0; i--) {
      b32_str[out_idx + i] = b32_digits[buf & 0x1F];
      buf >>= 5;
    }
    out_idx += 8;
  }
  
  // Replace trailing chars with padding if needed
  int padding = (5 - (blob_len % 5)) % 5;
  if (padding > 0) {
    static const int pad_chars[] = {0, 6, 4, 3, 1};
    for (int i = 0; i < pad_chars[padding]; i++) {
      b32_str[b32_len - 1 - i] = '=';
    }
  }
  
  b32_str[b32_len] = '\0';
  ret = JS_NewString(js, b32_str);
  free(b32_str);
)

static const JSCFunctionListEntry js_text_funcs[] = {
  MIST_FUNC_DEF(text, blob_to_hex, 1),
  MIST_FUNC_DEF(text, blob_to_base32, 1),
};

JSValue js_text_use(JSContext *js)
{
  JSValue mod = JS_NewObject(js);
  JS_SetPropertyFunctionList(js, mod, js_text_funcs, countof(js_text_funcs));
  return mod;
}
