// Simple TOML parser for cell modules
// Supports basic TOML features needed for the module system

function toml_unescape(s) {
  if (!is_text(s)) return null
  var r = replace(s, '\\"', '"')
  r = replace(r, '\\\\', '\\')
  return r
}

function toml_escape(s) {
  if (!is_text(s)) return null
  var r = replace(s, '\\', '\\\\')
  r = replace(r, '"', '\\"')
  return r
}

function parse_toml(toml_text) {
  if (!is_text(toml_text)) return null

  // Prefer Misty split if present; fall back to JS split.
  var lines = array(toml_text, '\n')
  if (lines == null) lines = array(toml_text, '\n')

  var result = {}
  var current_section = result
  var current_section_name = ''

  var i = 0
  var line = null
  var inner = null
  var section_path = null
  var j = 0
  var key = null
  var eq_index = null
  var key_part = null
  var value = null
  var unquoted = null
  for (i = 0; i < length(lines); i++) {
    line = trim(lines[i])
    if (line == null) line = lines[i]
    // Skip empty lines and comments
    if (!line || starts_with(line, '#')) continue

    // Section header
    if (starts_with(line, '[') && ends_with(line, ']')) {
      inner = text(line, 1, -1)
      section_path = parse_key_path(inner)
      if (section_path == null) return null

      current_section = result
      current_section_name = text(section_path, '.')

      for (j = 0; j < length(section_path); j++) {
        key = section_path[j]

        // Only treat null as "missing"; do not clobber false/0/""
        if (current_section[key] == null) {
          current_section[key] = {}
        } else if (!is_object(current_section[key])) {
          // Scalar/table collision like: a = 1 then [a.b]
          return null
        }

        current_section = current_section[key]
      }
      continue
    }

    // Key-value pair
    eq_index = search(line, '=')
    if (eq_index != null && eq_index > 0) {
      key_part = trim(text(line, 0, eq_index))
      value = trim(text(line, eq_index + 1))
      if (key_part == null) key_part = trim(text(line, 0, eq_index))
      if (value == null) value = trim(text(line, eq_index + 1))

      key = parse_key(key_part)
      if (key == null) return null

      if (starts_with(value, '"') && ends_with(value, '"')) {
        unquoted = text(value, 1, -1)
        current_section[key] = toml_unescape(unquoted)
        if (current_section[key] == null) return null
      } else if (starts_with(value, '[') && ends_with(value, ']')) {
        current_section[key] = parse_array(value)
        if (current_section[key] == null) return null
      } else if (value == 'true' || value == 'false') {
        current_section[key] = value == 'true'
      } else if (number(value) != null) {
        current_section[key] = number(value)
      } else {
        // Unquoted string
        current_section[key] = value
      }
    }
  }

  return result
}

function parse_key(str) {
  if (!is_text(str)) return null
  var inner = null
  if (starts_with(str, '"') && ends_with(str, '"')) {
    inner = text(str, 1, -1)
    return toml_unescape(inner)
  }
  return str
}

// Split a key path by dots, respecting quotes
function parse_key_path(str) {
  if (!is_text(str)) return null

  var parts = []
  var current = ''
  var in_quote = false

  var i = 0
  var c = null
  var piece = null
  for (i = 0; i < length(str); i++) {
    c = str[i]
    if (c == '"' && (i == 0 || str[i - 1] != '\\')) {
      in_quote = !in_quote
    } else if (c == '.' && !in_quote) {
      piece = trim(current)
      if (piece == null) piece = trim(current)
      push(parts, parse_key(piece))
      current = ''
      continue
    }
    current = current + c
  }

  var tail = trim(current)
  if (tail == null) tail = trim(current)
  if (length(tail) > 0) push(parts, parse_key(tail))

  return parts
}

function parse_array(str) {
  if (!is_text(str)) return null

  // Remove brackets and trim
  var s = text(str, 1, -1)
  s = trim(s)
  if (!s) return []

  var items = []
  var current = ''
  var in_quotes = false

  var i = 0
  var ch = null
  var piece = null
  for (i = 0; i < length(s); i++) {
    ch = s[i]

    if (ch == '"' && (i == 0 || s[i - 1] != '\\')) {
      in_quotes = !in_quotes
      current = current + ch
    } else if (ch == ',' && !in_quotes) {
      piece = trim(current)
      if (piece == null) piece = trim(current)
      push(items, parse_value(piece))
      current = ''
    } else {
      current = current + ch
    }
  }

  var last = trim(current)
  if (last == null) last = trim(current)
  if (last) push(items, parse_value(last))

  return items
}

function parse_value(str) {
  if (!is_text(str)) return null

  if (starts_with(str, '"') && ends_with(str, '"')) {
    return toml_unescape(text(str, 1, -1))
  }
  if (str == 'true' || str == 'false') return str == 'true'

  var n = number(str)
  if (n != null) return n

  return str
}

function encode_toml(obj) {
  var result = []

  function encode_value(value) {
    var items = null
    var i = 0
    if (is_text(value)) return '"' + toml_escape(value) + '"'
    if (is_logical(value)) return value ? 'true' : 'false'
    if (is_number(value)) return text(value)
    if (is_array(value)) {
      items = []
      for (i = 0; i < length(value); i++) push(items, encode_value(value[i]))
      return '[' + text(items, ', ') + ']'
    }
    return text(value)
  }

  function quote_key(k) {
    var i = 0
    var c = null
    for (i = 0; i < length(k); i++) {
      c = k[i]
      if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c == '-')) {
        return '"' + toml_escape(k) + '"'
      }
    }
    return k
  }

  // First pass: encode top-level simple values
  var keys = array(obj)
  var i = 0
  var key = null
  var value = null
  for (i = 0; i < length(keys); i++) {
    key = keys[i]
    value = obj[key]
    if (!is_object(value)) push(result, quote_key(key) + ' = ' + encode_value(value))
  }

  // Second pass: encode nested objects
  function encode_section(o, path) {
    var keys = array(o)
    var i = 0
    var key = null
    var value = null
    var quoted = null
    var section_path = null
    var section_keys = null
    var j = 0
    var sk = null
    var sv = null
    for (i = 0; i < length(keys); i++) {
      key = keys[i]
      value = o[key]

      if (is_object(value)) {
        quoted = quote_key(key)
        section_path = path ? path + '.' + quoted : quoted
        push(result, '[' + section_path + ']')

        // Direct properties
        section_keys = array(value)
        for (j = 0; j < length(section_keys); j++) {
          sk = section_keys[j]
          sv = value[sk]
          if (!is_object(sv)) push(result, quote_key(sk) + ' = ' + encode_value(sv))
        }

        // Nested sections
        encode_section(value, section_path)
      }
    }
  }

  encode_section(obj, '')
  return text(result, '\n')
}

return {
  decode: parse_toml,
  encode: encode_toml
}
