// json_walk.cm — JSON parse + walk + serialize kernel
// Stresses strings, records, arrays, and recursive traversal.

var json = use('json')

function make_nested_object(depth, breadth) {
  var obj = {}
  var i = 0
  var k = null
  if (depth <= 0) {
    for (i = 0; i < breadth; i++) {
      k = `key_${i}`
      obj[k] = i * 3.14
    }
    return obj
  }
  for (i = 0; i < breadth; i++) {
    k = `node_${i}`
    obj[k] = make_nested_object(depth - 1, breadth)
  }
  obj.value = depth
  obj.name = `level_${depth}`
  return obj
}

function make_array_data(size) {
  var arr = []
  var i = 0
  for (i = 0; i < size; i++) {
    push(arr, {
      id: i,
      name: `item_${i}`,
      active: i % 2 == 0,
      score: i * 1.5,
      tags: [`tag_${i % 5}`, `tag_${(i + 1) % 5}`]
    })
  }
  return arr
}

// Walk an object tree, counting nodes
function walk_count(obj) {
  var count = 1
  var keys = null
  var i = 0
  var v = null
  if (is_object(obj)) {
    keys = array(obj)
    for (i = 0; i < length(keys); i++) {
      v = obj[keys[i]]
      if (is_object(v) || is_array(v)) {
        count += walk_count(v)
      }
    }
  } else if (is_array(obj)) {
    for (i = 0; i < length(obj); i++) {
      v = obj[i]
      if (is_object(v) || is_array(v)) {
        count += walk_count(v)
      }
    }
  }
  return count
}

// Walk and extract all numbers
function walk_sum(obj) {
  var sum = 0
  var keys = null
  var i = 0
  var v = null
  if (is_object(obj)) {
    keys = array(obj)
    for (i = 0; i < length(keys); i++) {
      v = obj[keys[i]]
      if (is_number(v)) {
        sum += v
      } else if (is_object(v) || is_array(v)) {
        sum += walk_sum(v)
      }
    }
  } else if (is_array(obj)) {
    for (i = 0; i < length(obj); i++) {
      v = obj[i]
      if (is_number(v)) {
        sum += v
      } else if (is_object(v) || is_array(v)) {
        sum += walk_sum(v)
      }
    }
  }
  return sum
}

// Pre-build test data strings
var nested_obj = make_nested_object(3, 4)
var nested_json = json.encode(nested_obj)
var array_data = make_array_data(200)
var array_json = json.encode(array_data)

return {
  // Parse nested JSON
  json_parse_nested: function(n) {
    var i = 0
    var obj = null
    for (i = 0; i < n; i++) {
      obj = json.decode(nested_json)
    }
    return obj
  },

  // Parse array-of-records JSON
  json_parse_array: function(n) {
    var i = 0
    var arr = null
    for (i = 0; i < n; i++) {
      arr = json.decode(array_json)
    }
    return arr
  },

  // Encode nested object to JSON
  json_encode_nested: function(n) {
    var i = 0
    var s = null
    for (i = 0; i < n; i++) {
      s = json.encode(nested_obj)
    }
    return s
  },

  // Encode array to JSON
  json_encode_array: function(n) {
    var i = 0
    var s = null
    for (i = 0; i < n; i++) {
      s = json.encode(array_data)
    }
    return s
  },

  // Parse + walk + count
  json_roundtrip_walk: function(n) {
    var i = 0
    var obj = null
    var count = 0
    for (i = 0; i < n; i++) {
      obj = json.decode(nested_json)
      count += walk_count(obj)
    }
    return count
  },

  // Parse + sum all numbers + re-encode
  json_roundtrip_full: function(n) {
    var i = 0
    var obj = null
    var sum = 0
    var out = null
    for (i = 0; i < n; i++) {
      obj = json.decode(array_json)
      sum += walk_sum(obj)
      out = json.encode(obj)
    }
    return sum
  }
}
