object.pony

use "collections"
use "itertools"
use ".."

class Object
  embed _items: Array[(String, Item)]

  new create(items: (ReadSeq[(String, Item)] | None) = None) =>
    match items
    | let items': ReadSeq[(String, Item)] =>
      _items = _items.create(items'.size())
      _items.append(items')
    else
      _items = _items.create()
    end

  fun contains(key: (String | USize)): Bool =>
    let key' =
      match key
      | let k: String => k
      | let n: USize => n.string()
      end
    for (k, _) in _items.values() do
      if k == key' then
        return true
      end
    end
    false

  fun apply(key: String): this->Item ? =>
    for (k, v) in _items.values() do
      if k == key then
        return v
      end
    end
    error

  fun ref update(key: String, value: Item) =>
    var i: USize = 0
    while i < _items.size() do
      try
        if _items(i)?._1 == key then
          _items(i)? = (key, value)
          return
        end
      end
      i = i + 1
    end
    _items.push((key, value))

  fun pairs(): Iterator[(String, this->Item)] =>
    _items.values()

  fun get_string(pretty: Bool, indent: String = ""): String iso^ =>
    let indent' =
      if pretty then
        recover val indent + "  " end
      else
        indent
      end
    let result: String iso = String
    result.append("{")
    if _items.size() > 0 then
      if pretty then result.append("\n") end
      var first = true
      for (key, value) in _items.values() do
        if first then
          first = false
        else
          result.append(",")
          if pretty then result.append("\n") end
        end
        result.append(indent')
        result.append("\"" + key + "\":")
        if pretty then result.append(" ") end
        match value
        | let obj: Object box =>
          result.append(obj.get_string(pretty, indent'))
        | let seq: Sequence box =>
          result.append(seq.get_string(pretty, indent'))
        | let str: String box =>
          result.append("\"" + StringUtil.escape(str) + "\"")
        | let int: I128 =>
          result.append(int.string())
        | let flt: F64 =>
          result.append(flt.string())
        | let bool: Bool =>
          result.append(if bool then "true" else "false" end)
        | let null: Null =>
          result.append("null")
        end
      end
      if pretty then
        result.append("\n")
        result.append(indent)
      end
    end
    result.append("}")
    consume result

  fun string(): String iso^ =>
    get_string(true, "")