/* text.cm - text conversion and formatting utilities */

/* -------- helper functions ----------------------------------------- */

var blob = use('blob')
var utf8 = use('utf8')

var that = this

// Convert number to string with given radix
function to_radix(num, radix) {
  if (radix < 2 || radix > 36) return null;
  
  var digits = "0123456789abcdefghijklmnopqrstuvwxyz";
  var result = "";
  var n = Math.trunc(num);
  var negative = n < 0;
  n = Math.abs(n);
  
  if (n == 0) return "0";
  
  while (n > 0) {
    result = digits[n % radix] + result;
    n = Math.floor(n / radix);
  }
  
  return negative ? "-" + result : result;
}

// Insert separator every n digits from right
function add_separator(str, sep, n) {
  if (!n || n == 0) return str;
  
  var negative = str[0] == '-';
  if (negative) str = str.substring(1);
  
  var parts = str.split('.');
  var integer = parts[0];
  var decimal = parts[1] || '';
  
  // Add separators to integer part
  var result = "";
  for (var i = integer.length - 1, count = 0; i >= 0; i--) {
    if (count == n && i != integer.length - 1) {
      result = sep + result;
      count = 0;
    }
    result = integer[i] + result;
    count++;
  }
  
  if (decimal) result += '.' + decimal;
  return negative ? '-' + result : result;
}

// Format number with separator from left
function add_separator_left(str, sep, n) {
  if (!n || n == 0) return str;
  
  var negative = str[0] == '-';
  if (negative) str = str.substring(1);
  
  var result = "";
  for (var i = 0, count = 0; i < str.length; i++) {
    if (count == n && i != 0) {
      result += sep;
      count = 0;
    }
    result += str[i];
    count++;
  }
  
  return negative ? '-' + result : result;
}

/* -------- main text function --------------------------------------- */

function text(...arguments) {
  var arg = arguments[0];
  
  // Handle blob conversion
  if (arg instanceof blob) {
    if (!stone.p(arg))
      throw new Error("text: blob must be stone for reading");
    
    var format = arguments[1];
    var bit_length = arg.length;
    var result = "";
    
    if (typeof format == 'string') {
      // Extract style from format
      var style = '';
      for (var i = 0; i < format.length; i++) {
        if ((format[i] >= 'a' && format[i] <= 'z') || (format[i] >= 'A' && format[i] <= 'Z')) {
          style = format[i];
          break;
        }
      }
      
      // Handle blob encoding styles
      switch (style) {
        case 'h': // hexadecimal
          return that.blob_to_hex(arg);
          
        case 't': // base32
          return that.blob_to_base32(arg);
          
        case 'b': // binary
          for (var i = 0; i < bit_length; i++) {
            result += arg.read_logical(i) ? '1' : '0';
          }
          return result;
          
        case 'o': // octal
          var bits = 0;
          var value = 0;
          
          for (var i = 0; i < bit_length; i++) {
            var bit = arg.read_logical(i);
            value = (value << 1) | (bit ? 1 : 0);
            bits++;
            
            if (bits == 3) {
              result += value.toString();
              bits = 0;
              value = 0;
            }
          }
          
          // Handle remaining bits
          if (bits > 0) {
            value = value << (3 - bits);
            result += value.toString();
          }
          
          return result;
      }
    }
    
    // Default: interpret as UTF-8 text
    // Use the utf8 module to decode the blob
    return utf8.decode(arg);
  }
  
  // Handle array conversion
  if (Array.isArray(arg)) {
    var separator = arguments[1] || "";
    
    // Check if all items are valid codepoints
    var all_codepoints = true;
    for (var i = 0; i < arg.length; i++) {
      var item = arg[i];
      if (!(typeof item == 'number' && item >= 0 && item <= 0x10FFFF && item == Math.floor(item))) {
        all_codepoints = false;
        break;
      }
    }
    
    if (all_codepoints && separator == "") {
      // Use utf8 module to convert codepoints to string
      return utf8.from_codepoints(arg);
    } else {
      // General array to string conversion
      var result = "";
      for (var i = 0; i < arg.length; i++) {
        if (i > 0) result += separator;
        
        var item = arg[i];
        if (typeof item == 'number' && item >= 0 && item <= 0x10FFFF && item == Math.floor(item)) {
          // Single codepoint - use utf8 module
          result += utf8.from_codepoints([item]);
        } else {
          result += String(item);
        }
      }
      return result;
    }
  }
  
  // Handle number conversion
  if (typeof arg == 'number') {
    var format = arguments[1];
    
    // Simple radix conversion
    if (typeof format == 'number') {
      return to_radix(arg, format);
    }
    
    // Format string conversion
    if (typeof format == 'string') {
      return format_number(arg, format);
    }
    
    // Default conversion
    return String(arg);
  }
  
  // Handle text operations
  if (typeof arg == 'string') {
    if (arguments.length == 1) return arg;
    
    var from = arguments[1];
    var to = arguments[2];
    
    if (typeof from != 'number' || typeof to != 'number') return arg;
    
    var len = arg.length;
    
    // Adjust negative indices
    if (from < 0) from += len;
    if (to < 0) to += len;
    
    // Default values
    if (from == null) from = 0;
    if (to == null) to = len;
    
    // Validate range
    if (from < 0 || from > to || to > len) return null;
    
    return arg.substring(from, to);
  }
  
  return null;
}

/* -------- number formatting ---------------------------------------- */

function format_number(num, format) {
  // Parse format string
  var separation = 0;
  var style = '';
  var places = 0;
  
  var i = 0;
  
  // Parse separation digit
  if (i < format.length && format[i] >= '0' && format[i] <= '9') {
    separation = parseInt(format[i]);
    i++;
  }
  
  // Parse style letter
  if (i < format.length) {
    style = format[i];
    i++;
  } else {
    return null;
  }
  
  // Parse places digits
  if (i < format.length && format[i] >= '0' && format[i] <= '9') {
    places = parseInt(format[i]);
    i++;
    if (i < format.length && format[i] >= '0' && format[i] <= '9') {
      places = places * 10 + parseInt(format[i]);
      i++;
    }
  }
  
  // Invalid format if there's more
  if (i < format.length) return null;
  
  // Real number styles
  if (style == 'e' || style == 'n' || style == 's' || 
      style == 'u' || style == 'd' || style == 'v' || style == 'l') {
    
    var decimal_point = '.';
    var separator = '';
    var default_separation = 0;
    var default_places = 0;
    
    switch (style) {
      case 'e': // exponential
        decimal_point = '.';
        separator = '';
        default_separation = 0;
        default_places = 0;
        break;
      case 'n': // number
        decimal_point = '.';
        separator = '';
        default_separation = 0;
        default_places = 0;
        break;
      case 's': // space
        decimal_point = '.';
        separator = ' ';
        default_separation = 3;
        default_places = 0;
        break;
      case 'u': // underbar
        decimal_point = '.';
        separator = '_';
        default_separation = 0;
        default_places = 0;
        break;
      case 'd': // decimal
        decimal_point = '.';
        separator = ',';
        default_separation = 3;
        default_places = 2;
        break;
      case 'v': // comma (European style)
        decimal_point = ',';
        separator = '.';
        default_separation = 0;
        default_places = 0;
        break;
      case 'l': // locale (default to 'd' style for now)
        decimal_point = '.';
        separator = ',';
        default_separation = 3;
        default_places = 2;
        break;
    }
    
    if (separation == 0) separation = default_separation;
    if (places == 0 && style != 'e' && style != 'n') places = default_places;
    
    // Format the number
    if (style == 'e') {
      // Scientific notation
      var str = places > 0 ? num.toExponential(places) : num.toExponential();
      return str;
    } else if (style == 'n' && (Math.abs(num) >= 1e21 || (Math.abs(num) < 1e-6 && num != 0))) {
      // Use scientific notation for extreme values
      return num.toExponential();
    } else {
      // Regular decimal formatting
      var str;
      if (places > 0) {
        str = num.toFixed(places);
      } else {
        str = num.toString();
      }
      
      // Replace decimal point if needed
      if (decimal_point != '.') {
        str = str.replace('.', decimal_point);
      }
      
      // Add separators
      if (separation > 0 && separator) {
        str = add_separator(str, separator, separation);
      }
      
      return str;
    }
  }
  
  // Integer styles
  if (style == 'i' || style == 'b' || style == 'o' || 
      style == 'h' || style == 't') {
    
    var radix = 10;
    var default_separation = 0;
    var default_places = 1;
    
    switch (style) {
      case 'i': // integer
        radix = 10;
        default_separation = 0;
        default_places = 1;
        break;
      case 'b': // binary
        radix = 2;
        default_separation = 0;
        default_places = 1;
        break;
      case 'o': // octal
        radix = 8;
        default_separation = 0;
        default_places = 1;
        break;
      case 'h': // hexadecimal
        radix = 16;
        default_separation = 0;
        default_places = 1;
        break;
      case 't': // base32
        radix = 32;
        default_separation = 0;
        default_places = 1;
        break;
    }
    
    if (separation == 0) separation = default_separation;
    if (places == 0) places = default_places;
    
    // Convert to integer
    var n = Math.trunc(num);
    var str = to_radix(n, radix).toUpperCase();
    
    // Pad with zeros if needed
    var negative = str[0] == '-';
    if (negative) str = str.substring(1);
    
    while (str.length < places) {
      str = '0' + str;
    }
    
    // Add separators
    if (separation > 0) {
      str = add_separator_left(str, '_', separation);
    }
    
    return negative ? '-' + str : str;
  }
  
  return null;
}

return text;