--[[--------------------------------------------------------------------------
--]]--------------------------------------------------------------------------
local upb = require "lupb"
local lunit = require "lunit"
local upb_test = require "lua.test_pb"
local test_messages_proto3 = require "google.protobuf.test_messages_proto3_pb"
local test_messages_proto2 = require "google.protobuf.test_messages_proto2_pb"
local descriptor = require "google.protobuf.descriptor_pb"
local empty = require "google.protobuf.empty_pb"
if _VERSION >= 'Lua 5.2' then
  _ENV = lunit.module("testupb", "seeall")
else
  module("testupb", lunit.testcase, package.seeall)
end
function iter_to_array(iter)
  local arr = {}
  for v in iter do
    arr[#arr + 1] = v
  end
  return arr
end
function test_def_readers()
  local m = test_messages_proto3.TestAllTypesProto3
  assert_equal("TestAllTypesProto3", m:name())
  assert_equal("protobuf_test_messages.proto3.TestAllTypesProto3", m:full_name())
  -- field
  local f = m:field("optional_int32")
  local f2 = m:field(1)
  assert_equal(f, f2)
  assert_equal(1, f:number())
  assert_equal("optional_int32", f:name())
  assert_equal(upb.LABEL_OPTIONAL, f:label())
  assert_equal(upb.DESCRIPTOR_TYPE_INT32, f:descriptor_type())
  assert_equal(upb.TYPE_INT32, f:type())
  assert_nil(f:containing_oneof())
  assert_equal(m, f:containing_type())
  assert_equal(0, f:default())
  local message_field_count = 0
  for field in m:fields() do
    message_field_count = message_field_count + 1
  end
  assert_equal(message_field_count, #m)
  local message_oneof_count = 0
  for oneof in m:oneofs() do
    message_oneof_count = message_oneof_count + 1
  end
  assert_equal(message_oneof_count, m:oneof_count())
  -- oneof
  local o = m:lookup_name("oneof_field")
  assert_equal("oneof_field", o:name())
  assert_equal(m, o:containing_type())
  local oneof_field_count = 0
  for field in o:fields() do
    oneof_field_count = oneof_field_count + 1
  end
  assert_equal(oneof_field_count, #o)
  -- enum
  local e = test_messages_proto3['TestAllTypesProto3.NestedEnum']
  assert_true(#e > 3 and #e < 10)
  assert_equal(2, e:value("BAZ"):number())
end
function test_msg_map()
  msg = test_messages_proto3.TestAllTypesProto3()
  msg.map_int32_int32[5] = 10
  msg.map_int32_int32[6] = 12
  assert_equal(10, msg.map_int32_int32[5])
  assert_equal(12, msg.map_int32_int32[6])
  -- Test overwrite.
  msg.map_int32_int32[5] = 20
  assert_equal(20, msg.map_int32_int32[5])
  assert_equal(12, msg.map_int32_int32[6])
  msg.map_int32_int32[5] = 10
  -- Test delete.
  msg.map_int32_int32[5] = nil
  assert_nil(msg.map_int32_int32[5])
  assert_equal(12, msg.map_int32_int32[6])
  msg.map_int32_int32[5] = 10
  local serialized = upb.encode(msg)
  assert_true(#serialized > 0)
  local msg2 = upb.decode(test_messages_proto3.TestAllTypesProto3, serialized)
  assert_equal(10, msg2.map_int32_int32[5])
  assert_equal(12, msg2.map_int32_int32[6])
end
function test_map_sorting()
  function msg_with_int32_entries(start, expand)
    local msg = test_messages_proto3.TestAllTypesProto3()
    for i=start,start + 8 do
      msg.map_int32_int32[i] = i * 2
    end
    if expand then
      for i=start+20,200 do
        msg.map_int32_int32[i] = i
      end
      for i=start+20,200 do
        msg.map_int32_int32[i] = nil
      end
    end
    return msg
  end
  function msg_with_msg_entries(expand)
    local msg = test_messages_proto3.TestAllTypesProto3()
    -- 8! = 40320 possible orderings makes it overwhelmingly likely that two
    -- random orderings will be different.
    for i=1,8 do
      local submsg = test_messages_proto3.TestAllTypesProto3.NestedMessage()
      submsg.corecursive = msg_with_int32_entries(i, expand)
      msg.map_string_nested_message[tostring(i)] = submsg
    end
    expand = false
    if expand then
      for i=21,2000 do
        local submsg = test_messages_proto3.TestAllTypesProto3.NestedMessage()
        submsg.corecursive = msg_with_int32_entries(i, expand)
        msg.map_string_nested_message[tostring(i)] = submsg
      end
      for i=21,2000 do
        msg.map_string_nested_message[tostring(i)] = nil
      end
    end
    return msg
  end
  -- Create two messages with the same contents but (hopefully) different
  -- map table orderings.
  local msg = msg_with_msg_entries(false)
  local msg2 = msg_with_msg_entries(true)
  local text1 = upb.text_encode(msg)
  local text2 = upb.text_encode(msg2)
  assert_equal(text1, text2)
  local binary1 = upb.encode(msg, {upb.ENCODE_DETERMINISTIC})
  local binary2 = upb.encode(msg2, {upb.ENCODE_DETERMINISTIC})
  assert_equal(binary1, binary2)
  -- Non-sorted map should compare different.
  local text3 = upb.text_encode(msg, {upb.TXTENC_NOSORT})
  assert_not_equal(text1, text3)
  local binary3 = upb.encode(msg)
  assert_not_equal(binary1, binary3)
end
function test_utf8()
  local proto2_msg = test_messages_proto2.TestAllTypesProto2()
  proto2_msg.optional_string = "\xff"
  local serialized = upb.encode(proto2_msg)
  -- Decoding invalid UTF-8 succeeds in proto2.
  upb.decode(test_messages_proto2.TestAllTypesProto2, serialized)
  -- Decoding invalid UTF-8 fails in proto2.
  assert_error_match("Error decoding protobuf", function()
    upb.decode(test_messages_proto3.TestAllTypesProto3, serialized)
  end)
  -- TODO(haberman): should proto3 accessors also check UTF-8 at set time?
end
function test_string_double_map()
  msg = upb_test.MapTest()
  msg.map_string_double["one"] = 1.0
  msg.map_string_double["two point five"] = 2.5
  assert_equal(1, msg.map_string_double["one"])
  assert_equal(2.5, msg.map_string_double["two point five"])
  -- Test overwrite.
  msg.map_string_double["one"] = 2
  assert_equal(2, msg.map_string_double["one"])
  assert_equal(2.5, msg.map_string_double["two point five"])
  msg.map_string_double["one"] = 1.0
  -- Test delete.
  msg.map_string_double["one"] = nil
  assert_nil(msg.map_string_double["one"])
  assert_equal(2.5, msg.map_string_double["two point five"])
  msg.map_string_double["one"] = 1
  local serialized = upb.encode(msg)
  assert_true(#serialized > 0)
  local msg2 = upb.decode(upb_test.MapTest, serialized)
  assert_equal(1, msg2.map_string_double["one"])
  assert_equal(2.5, msg2.map_string_double["two point five"])
end
function test_string_double_map()
  local function fill_msg(msg)
    msg.i32_packed[1] = 100
    msg.i32_packed[2] = 200
    msg.i32_packed[3] = 50000
    msg.i64_packed[1] = 101
    msg.i64_packed[2] = 201
    msg.i64_packed[3] = 50001
    msg.f32_packed[1] = 102
    msg.f32_packed[2] = 202
    msg.f32_packed[3] = 50002
    msg.f64_packed[1] = 103
    msg.f64_packed[2] = 203
    msg.f64_packed[3] = 50003
  end
  local function check_msg(msg)
    assert_equal(100, msg.i32_packed[1])
    assert_equal(200, msg.i32_packed[2])
    assert_equal(50000, msg.i32_packed[3])
    assert_equal(3, #msg.i32_packed)
    assert_equal(101, msg.i64_packed[1])
    assert_equal(201, msg.i64_packed[2])
    assert_equal(50001, msg.i64_packed[3])
    assert_equal(3, #msg.i64_packed)
    assert_equal(102, msg.f32_packed[1])
    assert_equal(202, msg.f32_packed[2])
    assert_equal(50002, msg.f32_packed[3])
    assert_equal(3, #msg.f32_packed)
    assert_equal(103, msg.f64_packed[1])
    assert_equal(203, msg.f64_packed[2])
    assert_equal(50003, msg.f64_packed[3])
    assert_equal(3, #msg.f64_packed)
  end
  local msg = upb_test.PackedTest()
  fill_msg(msg)
  check_msg(msg)
  local serialized_packed = upb.encode(msg)
  local msg2 = upb.decode(upb_test.PackedTest, serialized_packed)
  local msg3 = upb.decode(upb_test.UnpackedTest, serialized_packed)
  check_msg(msg2)
  check_msg(msg3)
  serialized_unpacked = upb.encode(msg3)
  local msg4 = upb.decode(upb_test.PackedTest, serialized_unpacked)
  local msg5 = upb.decode(upb_test.PackedTest, serialized_unpacked)
  check_msg(msg4)
  check_msg(msg5)
end
function test_msg_string_map()
  msg = test_messages_proto3.TestAllTypesProto3()
  msg.map_string_string["foo"] = "bar"
  msg.map_string_string["baz"] = "quux"
  assert_nil(msg.map_string_string["abc"])
  assert_equal("bar", msg.map_string_string["foo"])
  assert_equal("quux", msg.map_string_string["baz"])
  -- Test overwrite.
  msg.map_string_string["foo"] = "123"
  assert_equal("123", msg.map_string_string["foo"])
  assert_equal("quux", msg.map_string_string["baz"])
  msg.map_string_string["foo"] = "bar"
  -- Test delete
  msg.map_string_string["foo"] = nil
  assert_nil(msg.map_string_string["foo"])
  assert_equal("quux", msg.map_string_string["baz"])
  msg.map_string_string["foo"] = "bar"
  local serialized = upb.encode(msg)
  assert_true(#serialized > 0)
  local msg2 = upb.decode(test_messages_proto3.TestAllTypesProto3, serialized)
  assert_equal("bar", msg2.map_string_string["foo"])
  assert_equal("quux", msg2.map_string_string["baz"])
end
function test_msg_array()
  msg = test_messages_proto3.TestAllTypesProto3()
  assert_not_nil(msg.repeated_int32)
  assert_equal(msg.repeated_int32, msg.repeated_int32)
  assert_equal(0, #msg.repeated_int32)
  msg.repeated_int32[1] = 2
  assert_equal(1, #msg.repeated_int32);
  assert_equal(2, msg.repeated_int32[1]);
  -- Can't assign a scalar; array is expected.
  assert_error_match("lupb.array expected", function() msg.repeated_int32 = 5 end)
  -- Can't assign array of the wrong type.
  local function assign_int64()
    msg.repeated_int32 = upb.Array(upb.TYPE_INT64)
  end
  assert_error_match("array type mismatch", assign_int64)
  local arr = upb.Array(upb.TYPE_INT32)
  arr[1] = 6
  assert_equal(1, #arr)
  msg.repeated_int32 = arr
  assert_equal(msg.repeated_int32, msg.repeated_int32)
  assert_equal(arr, msg.repeated_int32)
  assert_equal(1, #msg.repeated_int32)
  assert_equal(6, msg.repeated_int32[1])
  -- Can't assign other Lua types.
  assert_error_match("array expected", function() msg.repeated_int32 = "abc" end)
  assert_error_match("array expected", function() msg.repeated_int32 = true end)
  assert_error_match("array expected", function() msg.repeated_int32 = false end)
  assert_error_match("array expected", function() msg.repeated_int32 = nil end)
  assert_error_match("array expected", function() msg.repeated_int32 = {} end)
  assert_error_match("array expected", function() msg.repeated_int32 = print end)
end
function test_array_append()
  local arr = upb.Array(upb.TYPE_INT32)
  for i=1,200000 do
    arr[i] = i
  end
  for i=1,200000 do
    assert_equal(i, arr[i])
  end
end
function test_msg_submsg()
  --msg = test_messages_proto3.TestAllTypesProto3()
  msg = test_messages_proto3['TestAllTypesProto3']()
  assert_nil(msg.optional_nested_message)
  -- Can't assign message of the wrong type.
  local function assign_int64()
    msg.optional_nested_message = test_messages_proto3.TestAllTypesProto3()
  end
  assert_error_match("message type mismatch", assign_int64)
  local nested = test_messages_proto3['TestAllTypesProto3.NestedMessage']()
  msg.optional_nested_message = nested
  assert_equal(nested, msg.optional_nested_message)
  -- Can't assign other Lua types.
  assert_error_match("msg expected", function() msg.optional_nested_message = "abc" end)
  assert_error_match("msg expected", function() msg.optional_nested_message = true end)
  assert_error_match("msg expected", function() msg.optional_nested_message = false end)
  assert_error_match("msg expected", function() msg.optional_nested_message = nil end)
  assert_error_match("msg expected", function() msg.optional_nested_message = {} end)
  assert_error_match("msg expected", function() msg.optional_nested_message = print end)
end
-- Lua 5.1 and 5.2 have slightly different semantics for how a finalizer
-- can be defined in Lua.
if _VERSION >= 'Lua 5.2' then
  function defer(fn)
    setmetatable({}, { __gc = fn })
  end
else
  function defer(fn)
    getmetatable(newproxy(true)).__gc = fn
  end
end
function test_finalizer()
  -- Tests that we correctly handle a call into an already-finalized object.
  -- Collectible objects are finalized in the opposite order of creation.
  do
    local t = {}
    defer(function()
      assert_error_match("called into dead object", function()
        -- Generic def call.
        t[1]:lookup_msg("abc")
      end)
    end)
    t = {
      upb.DefPool(),
    }
  end
  collectgarbage()
end
-- in-range of 64-bit types but not exactly representable as double
local bad64 = 2^68 - 1
local numeric_types = {
  [upb.TYPE_UINT32] = {
    valid_val = 2^32 - 1,
    too_big = 2^32,
    too_small = -1,
    other_bad = 5.1
  },
  [upb.TYPE_UINT64] = {
    valid_val = 2^63,
    too_big = 2^64,
    too_small = -1,
    other_bad = bad64
  },
  [upb.TYPE_INT32] = {
    valid_val = 2^31 - 1,
    too_big = 2^31,
    too_small = -2^31 - 1,
    other_bad = 5.1
  },
  -- Enums don't exist at a language level in Lua, so we just represent enum
  -- values as int32s.
  [upb.TYPE_ENUM] = {
    valid_val = 2^31 - 1,
    too_big = 2^31,
    too_small = -2^31 - 1,
    other_bad = 5.1
  },
  [upb.TYPE_INT64] = {
    valid_val = 2^62,
    too_big = 2^63,
    too_small = -2^64,
    other_bad = bad64
  },
  [upb.TYPE_FLOAT] = {
    valid_val = 340282306073709652508363335590014353408
  },
  [upb.TYPE_DOUBLE] = {
    valid_val = 10^101
  },
}
function test_utf8()
  local invalid_utf8 = "\xff"
  local proto2_msg = test_messages_proto2.TestAllTypesProto2{
    optional_string = invalid_utf8,
  }
  -- As proto2, invalid UTF-8 parses and serializes fine.
  local serialized = upb.encode(proto2_msg)
  local proto2_msg2 = upb.decode(test_messages_proto2.TestAllTypesProto2, serialized)
  -- Decoding as proto3 fails.
  assert_error(function()
    upb.decode(test_messages_proto3.TestAllTypesProto3, serialized)
  end)
end
function test_msg_primitives()
  local msg = test_messages_proto3.TestAllTypesProto3{
    optional_int32 = 10,
    optional_uint32 = 20,
    optional_int64 = 30,
    optional_uint64 = 40,
    optional_double = 50,
    optional_float = 60,
    optional_sint32 = 70,
    optional_sint64 = 80,
    optional_fixed32 = 90,
    optional_fixed64 = 100,
    optional_sfixed32 = 110,
    optional_sfixed64 = 120,
    optional_bool = true,
    optional_string = "abc",
    optional_nested_message = test_messages_proto3['TestAllTypesProto3.NestedMessage']{a = 123},
  }
  -- Attempts to access non-existent fields fail.
  assert_error_match("no such field", function() msg.no_such = 1 end)
  assert_equal(10, msg.optional_int32)
  assert_equal(20, msg.optional_uint32)
  assert_equal(30, msg.optional_int64)
  assert_equal(40, msg.optional_uint64)
  assert_equal(50, msg.optional_double)
  assert_equal(60, msg.optional_float)
  assert_equal(70, msg.optional_sint32)
  assert_equal(80, msg.optional_sint64)
  assert_equal(90, msg.optional_fixed32)
  assert_equal(100, msg.optional_fixed64)
  assert_equal(110, msg.optional_sfixed32)
  assert_equal(120, msg.optional_sfixed64)
  assert_equal(true, msg.optional_bool)
  assert_equal("abc", msg.optional_string)
  assert_equal(123, msg.optional_nested_message.a)
end
function test_string_array()
  local function test_for_string_type(upb_type)
    local array = upb.Array(upb_type)
    assert_equal(0, #array)
    -- 0 is never a valid index in Lua.
    assert_error_match("array index", function() return array[0] end)
    -- Past the end of the array.
    assert_error_match("array index", function() return array[1] end)
    array[1] = "foo"
    assert_equal("foo", array[1])
    assert_equal(1, #array)
    -- Past the end of the array.
    assert_error_match("array index", function() return array[2] end)
    local array2 = upb.Array(upb_type)
    assert_equal(0, #array2)
    array[2] = "bar"
    assert_equal("foo", array[1])
    assert_equal("bar", array[2])
    assert_equal(2, #array)
    -- Past the end of the array.
    assert_error_match("array index", function() return array[3] end)
    -- Can't assign other Lua types.
    assert_error_match("Expected string", function() array[3] = 123 end)
    assert_error_match("Expected string", function() array[3] = true end)
    assert_error_match("Expected string", function() array[3] = false end)
    assert_error_match("Expected string", function() array[3] = nil end)
    assert_error_match("Expected string", function() array[3] = {} end)
    assert_error_match("Expected string", function() array[3] = print end)
    assert_error_match("Expected string", function() array[3] = array end)
  end
  test_for_string_type(upb.TYPE_STRING)
  test_for_string_type(upb.TYPE_BYTES)
end
function test_numeric_array()
  local function test_for_numeric_type(upb_type)
    local array = upb.Array(upb_type)
    local vals = numeric_types[upb_type]
    assert_equal(0, #array)
    -- 0 is never a valid index in Lua.
    assert_error_match("array index", function() return array[0] end)
    -- Past the end of the array.
    assert_error_match("array index", function() return array[1] end)
    array[1] = vals.valid_val
    assert_equal(vals.valid_val, array[1])
    assert_equal(1, #array)
    assert_equal(vals.valid_val, array[1])
    -- Past the end of the array.
    assert_error_match("array index", function() return array[2] end)
    array[2] = 10
    assert_equal(vals.valid_val, array[1])
    assert_equal(10, array[2])
    assert_equal(2, #array)
    -- Past the end of the array.
    assert_error_match("array index", function() return array[3] end)
    -- Values that are out of range.
    local errmsg = "not an integer or out of range"
    if vals.too_small then
      assert_error_match(errmsg, function() array[3] = vals.too_small end)
    end
    if vals.too_big then
      assert_error_match(errmsg, function() array[3] = vals.too_big end)
    end
    if vals.other_bad then
      assert_error_match(errmsg, function() array[3] = vals.other_bad end)
    end
    -- Can't assign other Lua types.
    errmsg = "bad argument #3"
    assert_error_match(errmsg, function() array[3] = "abc" end)
    assert_error_match(errmsg, function() array[3] = true end)
    assert_error_match(errmsg, function() array[3] = false end)
    assert_error_match(errmsg, function() array[3] = nil end)
    assert_error_match(errmsg, function() array[3] = {} end)
    assert_error_match(errmsg, function() array[3] = print end)
    assert_error_match(errmsg, function() array[3] = array end)
  end
  for k in pairs(numeric_types) do
    test_for_numeric_type(k)
  end
end
function test_numeric_map()
  local function test_for_numeric_types(key_type, val_type)
    local map = upb.Map(key_type, val_type)
    local key_vals = numeric_types[key_type]
    local val_vals = numeric_types[val_type]
    assert_equal(0, #map)
    -- Unset keys return nil
    assert_nil(map[key_vals.valid_val])
    map[key_vals.valid_val] = val_vals.valid_val
    assert_equal(1, #map)
    assert_equal(val_vals.valid_val, map[key_vals.valid_val])
    i = 0
    for k, v in pairs(map) do
      assert_equal(key_vals.valid_val, k)
      assert_equal(val_vals.valid_val, v)
    end
    -- Out of range key/val
    local errmsg = "not an integer or out of range"
    if key_vals.too_small then
      assert_error_match(errmsg, function() map[key_vals.too_small] = 1 end)
    end
    if key_vals.too_big then
      assert_error_match(errmsg, function() map[key_vals.too_big] = 1 end)
    end
    if key_vals.other_bad then
      assert_error_match(errmsg, function() map[key_vals.other_bad] = 1 end)
    end
    if val_vals.too_small then
      assert_error_match(errmsg, function() map[1] = val_vals.too_small end)
    end
    if val_vals.too_big then
      assert_error_match(errmsg, function() map[1] = val_vals.too_big end)
    end
    if val_vals.other_bad then
      assert_error_match(errmsg, function() map[1] = val_vals.other_bad end)
    end
  end
  for k in pairs(numeric_types) do
    for v in pairs(numeric_types) do
      test_for_numeric_types(k, v)
    end
  end
end
function test_unknown()
  local bytes = string.rep("\x38\x00", 1000)
  for i=1,1000 do
    local msg = upb.decode(test_messages_proto3.TestAllTypesProto3, bytes)
  end
end
function test_foo()
  local defpool = upb.DefPool()
  local filename = "external/com_google_protobuf/src/google/protobuf/descriptor_proto-descriptor-set.proto.bin"
  local file = io.open(filename, "rb") or io.open("bazel-bin/" .. filename, "rb")
  assert_not_nil(file)
  local descriptor = file:read("*a")
  assert_true(#descriptor > 0)
  defpool:add_set(descriptor)
  local FileDescriptorSet = defpool:lookup_msg("google.protobuf.FileDescriptorSet")
  assert_not_nil(FileDescriptorSet)
  set = FileDescriptorSet()
  assert_equal(#set.file, 0)
  assert_error_match("lupb.array expected", function () set.file = 1 end)
  set = upb.decode(FileDescriptorSet, descriptor)
  -- Test that we can at least call this without crashing.
  set_textformat = tostring(set)
  -- print(set_textformat)
  assert_equal(#set.file, 1)
  assert_equal(set.file[1].name, "google/protobuf/descriptor.proto")
end
function test_descriptor()
  local defpool = upb.DefPool()
  local file_proto = descriptor.FileDescriptorProto {
    name = "test.proto",
    message_type = upb.Array(descriptor.DescriptorProto, {
      descriptor.DescriptorProto{
        name = "ABC",
      },
    })
  }
  local file = defpool:add_file(upb.encode(file_proto))
  assert_equal(file:defpool(), defpool)
end
function test_descriptor_error()
  local defpool = upb.DefPool()
  local file = descriptor.FileDescriptorProto()
  file.name = "test.proto"
  file.message_type[1] = descriptor.DescriptorProto{
    name = "ABC"
  }
  file.message_type[2] = descriptor.DescriptorProto{
    name = "BC."
  }
  assert_error(function () defpool:add_file(upb.encode(file)) end)
  assert_nil(defpool:lookup_msg("ABC"))
end
function test_duplicate_enumval()
  local defpool = upb.DefPool()
  local file_proto = descriptor.FileDescriptorProto {
    name = "test.proto",
    message_type = upb.Array(descriptor.DescriptorProto, {
      descriptor.DescriptorProto{
        name = "ABC",
      },
    }),
    enum_type = upb.Array(descriptor.EnumDescriptorProto, {
      descriptor.EnumDescriptorProto{
        name = "MyEnum",
        value = upb.Array(descriptor.EnumValueDescriptorProto, {
          descriptor.EnumValueDescriptorProto{
            name = "ABC",
            number = 1,
          }
        }),
      },
    })
  }
  assert_error(function () defpool:add_file(upb.encode(file_proto)) end)
end
function test_duplicate_filename_error()
  local defpool = upb.DefPool()
  local file = descriptor.FileDescriptorProto()
  file.name = "test.proto"
  defpool:add_file(upb.encode(file))
  -- Second add with the same filename fails.
  assert_error(function () defpool:add_file(upb.encode(file)) end)
end
function test_encode_skipunknown()
  -- Test that upb.ENCODE_SKIPUNKNOWN does not encode unknown fields.
  local msg = test_messages_proto3.TestAllTypesProto3{
    optional_int32 = 10,
    optional_uint32 = 20,
    optional_int64 = 30,
  }
  -- SKIPUNKNOWN here tests that it does *not* affect regular fields.
  local serialized = upb.encode(msg, {upb.ENCODE_SKIPUNKNOWN})
  assert_true(#serialized > 0)
  local empty_with_unknown = upb.decode(empty.Empty, serialized)
  assert_true(#upb.encode(empty_with_unknown) > 0)
  -- Verify that unknown fields are not serialized.
  assert_true(#upb.encode(empty_with_unknown, {upb.ENCODE_SKIPUNKNOWN}) == 0)
end
function test_json_emit_defaults()
  local msg = test_messages_proto3.TestAllTypesProto3()
end
function test_json_locale()
  local msg = test_messages_proto3.TestAllTypesProto3()
  msg.optional_double = 1.1
  local original_locale = os.setlocale(nil)
  os.setlocale("C")
  local json = upb.json_encode(msg)
  os.setlocale("de_DE.utf8")
  assert_equal(json, upb.json_encode(msg))
  os.setlocale(original_locale)  -- Restore.
end
function test_encode_depth_limit()
  local msg = test_messages_proto3.TestAllTypesProto3()
  msg.recursive_message = msg
  assert_error(function() upb.encode(msg) end)
end
function test_large_field_number()
  local msg = upb_test.TestLargeFieldNumber()
  msg.i32 = 5
  local serialized = upb.encode(msg)
  local msg2 = upb.decode(upb_test.TestLargeFieldNumber, serialized)
  assert_equal(msg.i32, msg2.i32)
end
function test_timestamp_minutes()
  local msg = upb.json_decode(upb_test.TestTimestamp, '{"ts": "2000-01-01T00:00:00-06:59"}')
  assert_equal(msg.ts.seconds, 946684800 + ((6 * 60) + 59) * 60)
end
function test_gc()
  local top = test_messages_proto3.TestAllTypesProto3()
  local n = 100
  local m
  for i=1,n do
    local inner = test_messages_proto3.TestAllTypesProto3()
    m = inner
    for j=1,n do
      local tmp = m
      m = test_messages_proto3.TestAllTypesProto3()
      -- This will cause the arenas to fuse. But we stop referring to the child,
      -- so the Lua object is eligible for collection (and therefore its original
      -- arena can be collected too). Only the fusing will keep the C mem alivd.
      m.recursive_message = tmp
    end
    top.recursive_message = m
  end
  collectgarbage()
  for i=1,n do
    -- Verify we can touch all the messages again and without accessing freed
    -- memory.
    m = m.recursive_message
    assert_not_nil(m)
  end
end
function test_b9440()
  local m = upb_test.HelloRequest()
  m.id = 8
  assert_equal(8, m.id)
  m.version = "1"
  assert_equal(8, m.id)
end
local stats = lunit.main()
if stats.failed > 0 or stats.errors > 0 then
  error("One or more errors in test suite")
end


upb

--[[--------------------------------------------------------------------------
--]]--------------------------------------------------------------------------
local upb = require("lupb")
upb.generated_pool = upb.DefPool()
local module_metatable = {
  __index = function(t, k)
    local package = t._filedef:package()
    if package then
      k = package .. "." .. k
    end
    local pool = upb.generated_pool
    local def = pool:lookup_msg(k) or pool:lookup_enum(k)
    local v = nil
    if def and def:file():name() == t._filedef:name() then
      v = def
      t[k] = v
    end
    return v
  end
}
function upb._generated_module(desc_string)
  local file = upb.generated_pool:add_file(desc_string)
  local module = {_filedef = file}
  setmetatable(module, module_metatable)
  return module
end
return upb


-- Make lldb available in global
lldb = require('lldb')

-- Global assertion functions
function assertTrue(x)
    if not x then error('assertTrue failure') end
end

function assertFalse(x)
    if x then error('assertNotNil failure') end
end

function assertNotNil(x)
    if x == nil then error('assertNotNil failure') end
end

function assertEquals(x, y)
    if type(x) == 'table' and type(y) == 'table' then
        for k, _ in pairs(x) do
            assertEquals(x[k], y[k])
        end
    elseif type(x) ~= type(y) then
        error('assertEquals failure')
    elseif x ~= y then
        error('assertEquals failure')
    end
end

function assertStrContains(x, y)
    if not string.find(x, y, 1, true) then
        error('assertStrContains failure')
    end
end

-- Global helper functions
function read_file_non_empty_lines(f)
    local lines = {}
    while true do
        local line = f:read('*l')
        if not line then break end
        if line ~= '\n' then table.insert(lines, line) end
    end
    return lines
end

function split_lines(str)
    local lines = {}
    for line in str:gmatch("[^\r\n]+") do
        table.insert(lines, line)
    end
    return lines
end

function get_stopped_threads(process, reason)
    local threads = {}
    for i = 0, process:GetNumThreads() - 1 do
        local t = process:GetThreadAtIndex(i)
        if t:IsValid() and t:GetStopReason() == reason then
            table.insert(threads, t)
        end
    end
    return threads
end

function get_stopped_thread(process, reason)
    local threads = get_stopped_threads(process, reason)
    if #threads ~= 0 then return threads[1]
    else return nil end
end

-- Test helper

local _M = {}
local _m = {}

local _mt = { __index = _m }

function _M.create_test(name, exe, output, input)
    print('[lldb/lua] Create test ' .. name)
    exe = exe or os.getenv('TEST_EXE')
    output = output or os.getenv('TEST_OUTPUT')
    input = input or os.getenv('TEST_INPUT')
    lldb.SBDebugger.Initialize()
    local debugger = lldb.SBDebugger.Create()
    -- Ensure that debugger is created
    assertNotNil(debugger)
    assertTrue(debugger:IsValid())

    debugger:SetAsync(false)

    local lua_language = debugger:GetScriptingLanguage('lua')
    assertNotNil(lua_language)
    debugger:SetScriptLanguage(lua_language)

    local test = setmetatable({
        output = output,
        input = input,
        name = name,
        exe = exe,
        debugger = debugger
    }, _mt)
    _G[name] = test
    return test
end

function _m:create_target(exe)
    local target
    if not exe then exe = self.exe end
    target = self.debugger:CreateTarget(exe)
    -- Ensure that target is created
    assertNotNil(target)
    assertTrue(target:IsValid())
    return target
end

function _m:handle_command(command, collect)
    if collect == nil then collect = true end
    if collect then
        local ret = lldb.SBCommandReturnObject()
        local interpreter = self.debugger:GetCommandInterpreter()
        assertTrue(interpreter:IsValid())
        interpreter:HandleCommand(command, ret)
        self.debugger:GetOutputFile():Flush()
        self.debugger:GetErrorFile():Flush()
        assertTrue(ret:Succeeded())
        return ret:GetOutput()
    else
        self.debugger:HandleCommand(command)
        self.debugger:GetOutputFile():Flush()
        self.debugger:GetErrorFile():Flush()
    end
end

function _m:run()
    local tests = {}
    for k, v in pairs(self) do
        if string.sub(k, 1, 4) == 'Test' then
            table.insert(tests, k)
        end
    end
    table.sort(tests)
    for _, t in ipairs(tests) do
        print('[lldb/lua] Doing test ' .. self.name .. ' - ' .. t)
        local success = xpcall(self[t], function(e)
            print(debug.traceback())
        end, self)
        if not success then
            print('[lldb/lua] Failure in test ' .. self.name .. ' - ' .. t)
            return 1
        end
    end
    return 0
end

return _M


TestFileHandle

_T = require('lua_lldb_test').create_test('TestFileHandle')

function _T:TestLegacyFileOutScript()
    local f = io.open(self.output, 'w')
    self.debugger:SetOutputFile(f)
    self:handle_command('script print(1+1)')
    self.debugger:GetOutputFileHandle():write('FOO\n')
    self.debugger:GetOutputFileHandle():flush()
    f:close()

    f = io.open(self.output, 'r')
    assertEquals(read_file_non_empty_lines(f), {'2', 'FOO'})
    f:close()
end

function _T:TestLegacyFileOut()
    local f = io.open(self.output, 'w')
    self.debugger:SetOutputFile(f)
    self:handle_command('expression/x 3735928559', false)
    f:close()

    f = io.open(self.output, 'r')
    assertStrContains(f:read('*l'), 'deadbeef')
    f:close()
end

function _T:TestLegacyFileErr()
    local f = io.open(self.output, 'w')
    self.debugger:SetErrorFile(f)
    self:handle_command('lol', false)

    f = io.open(self.output, 'r')
    assertStrContains(f:read('*l'), 'is not a valid command')
    f:close()
end

os.exit(_T:run())


TestComprehensive

_T = require('lua_lldb_test').create_test('TestComprehensive')

function _T:Test0_CreateTarget()
    self.target = self:create_target()
    assertTrue(self.target:IsValid())
end

function _T:Test1_Breakpoint()
    self.main_bp = self.target:BreakpointCreateByName('main', 'a.out')
    self.loop_bp = self.target:BreakpointCreateByLocation('main.c', 28)
    assertTrue(self.main_bp:IsValid() and self.main_bp:GetNumLocations() == 1)
    assertTrue(self.loop_bp:IsValid() and self.loop_bp:GetNumLocations() == 1)
end

function _T:Test2_Launch()
    local error = lldb.SBError()
    self.args = { 'arg' }
    self.process = self.target:Launch(
        self.debugger:GetListener(),
        self.args,
        nil,
        nil,
        self.output,
        nil,
        nil,
        0,
        false,
        error
    )
    assertTrue(error:Success())
    assertTrue(self.process:IsValid())
end

function _T:Test3_BreakpointFindVariables()
    -- checking "argc" value
    local thread = get_stopped_thread(self.process, lldb.eStopReasonBreakpoint)
    assertNotNil(thread)
    assertTrue(thread:IsValid())
    local frame = thread:GetFrameAtIndex(0)
    assertTrue(frame:IsValid())
    local error = lldb.SBError()
    local var_argc = frame:FindVariable('argc')
    assertTrue(var_argc:IsValid())
    local var_argc_value = var_argc:GetValueAsSigned(error, 0)
    assertTrue(error:Success())
    assertEquals(var_argc_value, 2)

    -- checking "inited" value
    local continue = self.process:Continue()
    assertTrue(continue:Success())
    thread = get_stopped_thread(self.process, lldb.eStopReasonBreakpoint)
    assertNotNil(thread)
    assertTrue(thread:IsValid())
    frame = thread:GetFrameAtIndex(0)
    assertTrue(frame:IsValid())
    error = lldb.SBError()
    local var_inited = frame:FindVariable('inited')
    assertTrue(var_inited:IsValid())
    self.var_inited = var_inited
    local var_inited_value = var_inited:GetValueAsUnsigned(error, 0)
    assertTrue(error:Success())
    assertEquals(var_inited_value, 0xDEADBEEF)
end

function _T:Test3_RawData()
    local error = lldb.SBError()
    local address = self.var_inited:GetAddress()
    assertTrue(address:IsValid())
    local size = self.var_inited:GetByteSize()
    local raw_data = self.process:ReadMemory(address:GetOffset(), size, error)
    assertTrue(error:Success())
    local data_le = lldb.SBData.CreateDataFromUInt32Array(lldb.eByteOrderLittle, 1, {0xDEADBEEF})
    local data_be = lldb.SBData.CreateDataFromUInt32Array(lldb.eByteOrderBig, 1, {0xDEADBEEF})
    assertTrue(data_le:GetUnsignedInt32(error, 0) == 0xDEADBEEF or data_be:GetUnsignedInt32(error, 0) == 0xDEADBEEF)
    assertTrue(raw_data == "\xEF\xBE\xAD\xDE" or raw_data == "\xDE\xAD\xBE\xEF")
end

function _T:Test4_ProcessExit()
    self.loop_bp:SetAutoContinue(true)
    local continue = self.process:Continue()
    assertTrue(continue:Success())
    assertTrue(self.process:GetExitStatus() == 0)
end

function _T:Test5_FileOutput()
    local f = io.open(self.output, 'r')
    assertEquals(
        read_file_non_empty_lines(f),
        {
            self.exe,
            table.unpack(self.args),
            'I am a function.',
            'sum = 5050'
        }
    )
    f:close()
end

os.exit(_T:run())


TestProcessAPI

_T = require('lua_lldb_test').create_test('TestProcessAPI')

function _T:TestProcessLaunchSimple()
    local target = self:create_target()
    local args = { 'arg1', 'arg2', 'arg3' }
    local process = target:LaunchSimple(
        -- argv
        args,
        -- envp
        nil,
        -- working directory
        nil
    )
    assertTrue(process:IsValid())
    local stdout = process:GetSTDOUT(1000)
    assertEquals(split_lines(stdout), {self.exe, table.unpack(args)})
end

function _T:TestProcessLaunch()
    local target = self:create_target()
    local args = { 'arg1', 'arg2', 'arg3' }
    local error = lldb.SBError()
    local f = io.open(self.output, 'w')
    f:write()
    f:close()
    local process = target:Launch(
        -- listener
        self.debugger:GetListener(),
        -- argv
        args,
        -- envp
        nil,
        -- stdin
        nil,
        -- stdout
        self.output,
        -- stderr
        nil,
        -- working directory
        nil,
        -- launch flags
        0,
        -- stop at entry
        true,
        -- error
        error
    )
    assertTrue(error:Success())
    assertTrue(process:IsValid())
    local threads = get_stopped_threads(process, lldb.eStopReasonSignal)
    assertTrue(#threads ~= 0)
    local continue = process:Continue()
    assertTrue(continue:Success())
    local f = io.open(self.output, 'r')
    assertEquals(read_file_non_empty_lines(f), {self.exe, table.unpack(args)})
    f:close()
end

os.exit(_T:run())


TestBreakpointAPI

_T = require('lua_lldb_test').create_test('TestBreakpointAPI')

function _T:TestBreakpointIsValid()
    local target = self:create_target()
    local breakpoint = target:BreakpointCreateByName('AFunction', 'a.out')
    assertTrue(breakpoint:IsValid() and breakpoint:GetNumLocations() == 1)
    local did_delete = target:BreakpointDelete(breakpoint:GetID())
    assertTrue(did_delete)
    local del_bkpt = target:FindBreakpointByID(breakpoint:GetID())
    assertFalse(del_bkpt:IsValid())
    assertFalse(breakpoint:IsValid())
end

function _T:TestTargetDelete()
    local target = self:create_target()
    local breakpoint = target:BreakpointCreateByName('AFunction', 'a.out')
    assertTrue(breakpoint:IsValid() and breakpoint:GetNumLocations() == 1)
    local location = breakpoint:GetLocationAtIndex(0)
    assertTrue(location:IsValid())
    assertEquals(target, breakpoint:GetTarget())
    assertTrue(self.debugger:DeleteTarget(target))
    assertFalse(breakpoint:IsValid())
    assertFalse(location:IsValid())
end

function _T:TestBreakpointHitCount()
    local target = self:create_target()
    local breakpoint = target:BreakpointCreateByName('BFunction', 'a.out')
    assertTrue(breakpoint:IsValid() and breakpoint:GetNumLocations() == 1)
    breakpoint:SetAutoContinue(true)
    target:LaunchSimple(nil, nil, nil)
    assertEquals(breakpoint:GetHitCount(), 100)
end

function _T:TestBreakpointFrame()
    local target = self:create_target()
    local breakpoint = target:BreakpointCreateByName('main', 'a.out')
    assertTrue(breakpoint:IsValid() and breakpoint:GetNumLocations() == 1)
    local process = target:LaunchSimple({ 'arg1', 'arg2' }, nil, nil)
    local thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint)
    assertNotNil(thread)
    assertTrue(thread:IsValid())
    local frame = thread:GetFrameAtIndex(0)
    assertTrue(frame:IsValid())
    local error = lldb.SBError()
    local var_argc = frame:FindVariable('argc')
    local var_argc_value = var_argc:GetValueAsSigned(error, 0)
    assertTrue(error:Success())
    assertEquals(var_argc_value, 3)
end

os.exit(_T:run())


testmodule

local mymodule = {}

function mymodule.foo()
  print("Hello World!")
end

return mymodule


--- Helper script for generating bindings with tolua++
-- This file is based on the file of the same name which ships
-- as part of cocos2dx.
-- example usage:
-- tolua++ -L tolua_preload.lua -o LuaCocos2d.cpp Cocos2d.pkg

_is_functions = _is_functions or {}
_to_functions = _to_functions or {}
_push_functions = _push_functions or {}

local CCObjectTypes = {
    "CCPhysicsSprite",
}

-- register CCObject types
for i = 1, #CCObjectTypes do
    _push_functions[CCObjectTypes[i]] = "toluafix_pushusertype_ccobject"
end

-- register LUA_FUNCTION, LUA_TABLE, LUA_HANDLE type
_to_functions["LUA_FUNCTION"] = "toluafix_ref_function"
_is_functions["LUA_FUNCTION"] = "toluafix_isfunction"
_to_functions["LUA_TABLE"] = "toluafix_totable"
_is_functions["LUA_TABLE"] = "toluafix_istable"


-- Example/default game logic.  This code displays the level selection
-- menu and triggers the loading of individual levels.
local util = require 'util'
local gui = require 'gui'
local editor = require 'editor'
local handlers = {}
-- Horizontally center a node within a layer at a given height.
local function Center(node, height)
    local visible_size = CCDirector:sharedDirector():getVisibleSize()
    local node_size = node:getContentSize()
    -- A negative height means an offet from the top of the visible area
    if height < 0 then
        height = height + visible_size.height
    end
    local xpos = game_obj.origin.x + visible_size.width / 2
    local ypos = game_obj.origin.y + height - node_size.height / 2
    node:setPosition(CCPointMake(xpos, ypos))
end
local function ElasticMove(node, start_pos, end_pos, duration, period)
    node:setPosition(start_pos)
    local move_to = CCMoveTo:create(duration, end_pos)
    node:runAction(CCEaseElasticOut:create(move_to, period))
end
--- Local function for creating the level selection menu.
local function CreateLevelMenu(layer)
    local label = gui.CreateLabel({name="Select Level"})
    layer:addChild(label)
    -- Center label 100 pixels from the top of the sceen
    Center(label, -100)
    local start_pos = ccp(label:getPositionX(), label:getPositionY() + 100)
    ElasticMove(label, start_pos, ccp(label:getPosition()), 1.0, 0.3)
    -- Create the level selection menu
    local menu = CCMenu:create()
    local icon = CCSprite:create(game_obj.assets.level_icon)
    local icon_size = icon:getContentSize()
    local label_pos = CCPointMake(icon_size.width/2, icon_size.height/2)
    local function LevelSelected(tag)
        local level_number = tag
        util.Log('LevelSelected: ' .. level_number)
        GameManager:sharedManager():LoadLevel(level_number)
    end
    -- For each level create a menu item and a textual label
    for i=1,#game_obj.levels do
        local label_string = string.format("%d", i)
        local item = CCMenuItemImage:create(game_obj.assets.level_icon,
                                            game_obj.assets.level_icon_selected)
        menu:addChild(item)
        item:setTag(i)
        item:registerScriptTapHandler(LevelSelected)
        local label = gui.CreateLabel({name=label_string})
        label:setPosition(label_pos)
        item:addChild(label)
    end
    layer:addChild(menu)
    gui.GridLayout(menu, 2, CCPointMake(20, 20))
    -- Slide menu in from botton
    local end_pos = ccp(game_obj.origin.x, game_obj.origin.y)
    local start_pos = ccp(end_pos.x, end_pos.y - 600)
    ElasticMove(menu, start_pos, end_pos, 1.0, 0.7)
end
local function MainMenuCallback(value)
    local scene = CCScene:create()
    local layer = CCLayerColor:create(background_color)
    local director = CCDirector:sharedDirector()
    util.Log("Got to MainMenuCallback with " .. value)
    if value == 1 then
        scene:addChild(layer)
        CreateLevelMenu(layer)
        director:replaceScene(scene)
        game_obj.game_mode = 'play'
    elseif value == 2 then
        scene:addChild(layer)
        CreateLevelMenu(layer)
        director:replaceScene(scene)
        game_obj.script = editor
        game_obj.game_mode = 'edit'
    end
end
--- Game behaviour callback.   This function is called when the game
-- first starts and in charge of creating the first scene.
function handlers.StartGame()
    util.Log('StartGame')
    local director = CCDirector:sharedDirector()
    background_color = ccc4(0, 0xD8, 0x8F, 0xD8)
    -- Create a new scene and layer
    local scene = CCScene:create()
    local layer = CCLayerColor:create(background_color)
    scene:addChild(layer)
    local menu_def = {
        items = {
            {
              name = 'Start Game',
              callback = MainMenuCallback,
              value = 1
            },
            {
              name = 'Edit Game',
              callback = MainMenuCallback,
              value = 2
            },
        },
    }
    local menu = gui.CreateMenu(menu_def)
    layer:addChild(menu)
    director:runWithScene(scene)
end
return handlers


drawing

-- Functions for drawing new sprites using a brush.
-- This module defines a single 'drawing' global containing the following
-- functions:
--   - SetBrush
--   - CreateShape
--   - CreateSprite
--   - DrawStartPoint
--   - DrawEndPoint
--   - AddLineToShape
--   - OnTouchBegan
--   - OnTouchMoved
--   - OnTouchEnded
local util = require 'util'
local drawing = {
    MODE_SELECT = 1,
    MODE_FREEHAND = 2,
    MODE_LINE = 3,
    MODE_RECT = 4,
    MODE_CIRCLE = 5,
}
drawing.mode = drawing.MODE_FREEHAND
-- Brush information (set by SetBrush)
local brush_tex
local brush_thickness
-- Constant for grouping physics bodies
local MAIN_CATEGORY = 0x1
local DRAWING_CATEGORY = 0x2
-- Constants for tagging cocos nodes
local TAG_BATCH_NODE = 0x1
-- Starting batch size for CCSpriteBatchNode.  We override
-- the default since we often have many sprites in each
-- node (each drawn element is it own batch node).
local DEFAULT_BATCH_COUNT = 100
-- Local state for default touch handlers
local current_shape = nil
local current_tag = 99 -- util.tags.TAG_DYNAMIC_START
local start_pos = nil
local last_pos = nil
local brush_color = ccc3(255, 100, 100)
-- Callbacks that are registered for drawn objects.  The game
-- can register its own callbacks here to add behavior for
-- drawn objects.
drawing.handlers = {}
--- Create a b2Vec from a lua list containing 2 elements.
-- This is used to convert point data from .def files directly
-- to the box2dx coordinate system
local function b2VecFromLua(luapoint)
    local ccpoint = util.PointFromLua(luapoint)
    local rtn = util.b2VecFromCocos(ccpoint)
    return rtn
end
local function CreateBrushBatch(parent)
    local node = CCSpriteBatchNode:createWithTexture(brush_tex, DEFAULT_BATCH_COUNT)
    assert(node)
    parent:addChild(node, 1, TAG_BATCH_NODE)
    return node
end
--- Create a fixed pivot point between the world and the given body.
local function CreatePivot(anchor, body)
    local anchor_point = util.b2VecFromCocos(anchor)
    -- create a new fixed body to pivot against
    local ground_def = b2BodyDef:new_local()
    ground_def.position = anchor_point
    local ground_body = level_obj.world:CreateBody(ground_def);
    -- create the pivot joint
    local joint_def = b2RevoluteJointDef:new_local()
    joint_def:Initialize(ground_body, body, anchor_point)
    local joint = level_obj.world:CreateJoint(joint_def)
end
local function AddShapeToBody(body, shape, sensor)
    local fixture_def = b2FixtureDef:new_local()
    fixture_def.shape = shape
    fixture_def.density = 1.0
    fixture_def.friction = 0.5
    fixture_def.restitution = 0.3
    fixture_def.isSensor = sensor
    return body:CreateFixture(fixture_def)
end
local function InitPhysicsNode(node, location, dynamic, tag)
    local body_def = b2BodyDef:new_local()
    if dynamic == true then
        body_def.type = b2_dynamicBody
    end
    local body = level_obj.world:CreateBody(body_def)
    node:setB2Body(body)
    node:setPTMRatio(util.PTM_RATIO)
    node:setPosition(location)
    node:setTag(tag)
    body:SetUserData(tag)
    level_obj.layer:addChild(node, 1, tag)
    return body
end
-- Create and initialise a new invisible physics node.
local function CreatePhysicsNode(location, dynamic, tag)
    local node = CCPhysicsNode:create()
    InitPhysicsNode(node, location, dynamic, tag)
    return node
end
local function DrawBrush(parent, location, color)
    local child_sprite = CCSprite:createWithTexture(brush_tex)
    child_sprite:setPosition(location)
    child_sprite:setColor(color)
    parent:addChild(child_sprite)
end
-- Add a new circle/sphere fixture to a body and return the new fixture
local function AddSphereToBody(body, location, radius, sensor)
    local sphere = b2CircleShape:new_local()
    sphere.m_radius = util.ScreenToWorld(radius)
    sphere.m_p.x = util.ScreenToWorld(location.x) - body:GetPosition().x
    sphere.m_p.y = util.ScreenToWorld(location.y) - body:GetPosition().y
    return AddShapeToBody(body, sphere, sensor)
end
-- Add a new line/box fixture to a body and return the new fixture
local function AddLineToShape(node, from, to, color, absolute)
    -- calculate length and angle of line based on start and end points
    local body = node:getB2Body()
    local length = ccpDistance(from, to);
    local dist_x = to.x - from.x
    local dist_y = to.y - from.y
    -- create fixture
    local rel_start = from
    if absolute then
       rel_start = node:convertToNodeSpace(from)
    end
    local center = b2Vec2:new_local(util.ScreenToWorld(rel_start.x + dist_x/2),
                                    util.ScreenToWorld(rel_start.y + dist_y/2))
    local shape = b2PolygonShape:new_local()
    local angle = math.atan2(dist_y, dist_x)
    shape:SetAsBox(util.ScreenToWorld(length/2), util.ScreenToWorld(brush_thickness),
                   center, angle)
    local fixture = AddShapeToBody(body, shape, false)
    -- Create sequence of sprite nodes as children
    local dist = CCPointMake(dist_x, dist_y)
    local num_children = math.ceil(length / brush_step)
    local inc_x = dist_x / num_children
    local inc_y = dist_y / num_children
    local child_location = rel_start
    util.Log('Create line at: rel=' .. util.PointToString(rel_start) .. ' len=' .. length .. ' num=' .. num_children)
    local batch_node = node:getChildByTag(TAG_BATCH_NODE)
    assert(batch_node)
    for i = 1,num_children do
        child_location.x = child_location.x + inc_x
        child_location.y = child_location.y + inc_y
        DrawBrush(batch_node, child_location, color)
    end
    return fixture
end
-- Set the collision group for a fixture
local function SetCategory(fixture, category)
    local filter = fixture:GetFilterData()
    filter.categoryBits = category
    filter.maskBits = category
    fixture:SetFilterData(filter)
end
--- Make the a body dynamic and put it in the default collision group
local function MakeBodyDynamic(body)
    body:SetType(b2_dynamicBody)
    local fixture = body:GetFixtureList()
    while fixture do
        SetCategory(fixture, MAIN_CATEGORY)
        fixture = fixture:GetNext()
    end
end
--- Set brush texture for subsequent draw operations
function drawing.SetBrush(brush)
    -- calculate thickness based on brush sprite size
    brush_tex = brush:getTexture()
    local brush_size = brush_tex:getContentSizeInPixels()
    brush_thickness = math.max(brush_size.height/2, brush_size.width/2)
    brush_step = brush_thickness * 1.5
end
--- Create a physics sprite at a given location with a given image
local function AddSpriteToShape(node, sprite_def, absolute)
    local pos = util.PointFromLua(sprite_def.pos, absolute)
    util.Log('Create sprite [tag=' .. sprite_def.tag .. ' image=' .. sprite_def.image .. ' absolute=' .. tostring(absolute) .. ']: ' ..
        util.PointToString(pos))
    local image = game_obj.assets[sprite_def.image]
    local sprite = CCSprite:create(image)
    local rel_pos
    local world_pos
    if absolute then
       rel_pos = node:convertToNodeSpace(pos)
       world_pos = pos
    else
       rel_pos = pos
       world_pos = node:convertToWorldSpace(pos)
    end
    sprite:setPosition(rel_pos)
    node:addChild(sprite)
    AddSphereToBody(node:getB2Body(), world_pos, sprite:boundingBox().size.height/2, sprite_def.sensor)
    return sprite
end
local function AddChildShape(shape, child_def, absolute)
    if child_def.color then
        color = ccc3(child_def.color[1], child_def.color[2], child_def.color[3])
    else
        color = ccc3(255, 255, 255)
    end
    if child_def.type == 'line' then
        local start = util.PointFromLua(child_def.start, absolute)
        local finish = util.PointFromLua(child_def.finish, absolute)
        AddLineToShape(shape, start, finish, color, absolute)
    elseif child_def.type == 'image' then
        AddSpriteToShape(shape, child_def, absolute)
    else
        assert(false, 'invalid shape type: ' .. shape_def.type)
    end
end
--- Draw a shape described by a given shape def.
-- This creates physics sprites and accosiated box2d bodies for
-- the shape.
function drawing.CreateShape(shape_def)
    local shape = nil
    if shape_def.type == 'compound' then
        local pos = util.PointFromLua(shape_def.pos)
        shape = CreatePhysicsNode(pos, shape_def.dynamic, shape_def.tag)
        CreateBrushBatch(shape)
        if shape_def.children then
            for _, child_def in ipairs(shape_def.children) do
                child_def.tag = shape_def.tag
                child = AddChildShape(shape, child_def, false)
            end
        end
    elseif shape_def.type == 'line' then
        local pos = util.PointFromLua(shape_def.start)
        shape = CreatePhysicsNode(pos, shape_def.dynamic, shape_def.tag)
        CreateBrushBatch(shape)
        AddChildShape(shape, shape_def, true)
    elseif shape_def.type == 'edge' then
        local body_def = b2BodyDef:new_local()
        local body = level_obj.world:CreateBody(body_def)
        local b2shape = b2EdgeShape:new_local()
        local start = b2VecFromLua(shape_def.start)
        local finish = b2VecFromLua(shape_def.finish)
        util.Log('Create edge from: ' .. util.VecToString(start) .. ' to: ' .. util.VecToString(finish))
        b2shape:Set(start, finish)
        body:CreateFixture(b2shape, 0)
        return
    elseif shape_def.type == 'image' then
        local pos = util.PointFromLua(shape_def.pos)
        shape = CreatePhysicsNode(pos, shape_def.dynamic, shape_def.tag)
        AddChildShape(shape, shape_def, true)
    else
        assert(false, 'invalid shape type: ' .. shape_def.type)
    end
    if shape_def.anchor then
        local body = shape:getB2Body()
        local anchor = util.PointFromLua(shape_def.anchor)
        CreatePivot(anchor, body)
    end
    return shape
end
--- Create a single circlular point with the brush.
-- This is used to start shapes that the user draws.  The returned
-- node is the an invisible node that acts as the physics objects.
-- Sprite nodes are then attached to this as the user draws.
function drawing.DrawStartPoint(location, color, tag, dynamic)
    -- Add invisibe physics node
    local node = CreatePhysicsNode(location, dynamic, tag)
    CreateBrushBatch(node)
    -- Add visible sprite
    local sprite = CCSprite:createWithTexture(brush_tex)
    sprite:setColor(color)
    node:addChild(sprite)
    -- Add collision info
    local fixture = AddSphereToBody(node:getB2Body(), location, brush_thickness, false)
    SetCategory(fixture, DRAWING_CATEGORY)
    return node
end
--- Create a circle composed of brush sprites backed by a single box2d
-- circle fixture.
function drawing.DrawCircle(center, radius, color, tag)
    -- Create the initial (invisible) node at the center
    -- and then attach a sequence of visible child sprites
    local node = CreatePhysicsNode(center, true, tag)
    local batch_node = CreateBrushBatch(node)
    local inner_radius = math.max(radius - brush_thickness, 1)
    local circumference = 2 * math.pi * inner_radius
    local num_sprites = math.max(circumference / brush_step, 1)
    local angle_delta = 2 * math.pi / num_sprites
    util.Log('drawing circle: radius=' .. math.floor(radius) .. ' sprites=' .. math.floor(num_sprites))
    for angle = 0, 2 * math.pi, angle_delta do
        x = inner_radius * math.cos(angle)
        y = inner_radius * math.sin(angle)
        DrawBrush(batch_node, ccp(x, y), color)
    end
    -- Create the box2d physics body to match the sphere.
    local fixture = AddSphereToBody(node:getB2Body(), center, radius, false)
    SetCategory(fixture, DRAWING_CATEGORY)
    return node
end
function drawing.DrawEndPoint(node, location, color)
    -- Add visible sprite
    local child_sprite = CCSprite:createWithTexture(brush_tex)
    child_sprite:setPosition(node:convertToNodeSpace(location))
    child_sprite:setColor(color)
    node:addChild(child_sprite)
    -- Add collision info
    local body = node:getB2Body()
    local fixture = AddSphereToBody(body, location, brush_thickness, false)
    SetCategory(fixture, DRAWING_CATEGORY)
end
function drawing.AddLineToShape(sprite, from, to, color)
    fixture = AddLineToShape(sprite, from, to, color, true)
    SetCategory(fixture, DRAWING_CATEGORY)
end
function drawing.IsDrawing()
   return current_shape ~= nil
end
--- Sample OnTouchBegan for drawing-based games.  For bespoke drawing behaviour
-- clone and modify this code.
function drawing.OnTouchBegan(x, y)
    --- ignore touches if we are already drawing something
    if drawing.IsDrawing() then
        return false
    end
    if drawing.mode == drawing.MODE_SELECT then
        return false
    end
    start_pos = ccp(x, y)
    last_pos = start_pos
    -- New shape def
    local shape = {
        tag = current_tag,
        tag_str = 'drawn_shape_' .. current_tag,
        script = drawing.handlers,
    }
    if drawing.mode == drawing.MODE_FREEHAND or drawing.mode == drawing.MODE_LINE then
        -- create initial sphere to represent start of shape
        shape.node = drawing.DrawStartPoint(start_pos, brush_color, current_tag)
    elseif drawing.mode == drawing.MODE_CIRCLE then
        shape.node = drawing.DrawCircle(start_pos, 1, brush_color, current_tag)
    else
        error('invalid drawing mode: ' .. tostring(drawing.mode))
    end
    -- Register the new shape
    RegisterObject(shape, shape.tag, shape.tag_str)
    current_shape = shape
    current_tag = current_tag + 1
    return true
end
function drawing.DestroySprite(sprite)
   -- Work around crash bug!
   sprite:setPosition(sprite:getPositionX(), sprite:getPositionY())
   local body = sprite:getB2Body()
   sprite:removeFromParentAndCleanup(true)
   body:GetWorld():DestroyBody(body)
end
--- Sample OnTouchMoved for drawing-based games.  For bespoke drawing behaviour
-- clone and modify this code.
function drawing.OnTouchMoved(x, y)
    new_pos = ccp(x, y)
    if drawing.mode == drawing.MODE_FREEHAND then
        -- Draw line segments as the touch moves
        local length = ccpDistance(new_pos, last_pos);
        if length > brush_thickness * 2 then
            drawing.AddLineToShape(current_shape.node, last_pos, new_pos, brush_color)
            last_pos = new_pos
        end
    elseif drawing.mode == drawing.MODE_LINE then
        local tag = current_shape.node:getTag()
        drawing.DestroySprite(current_shape.node)
        current_shape.node = drawing.DrawStartPoint(start_pos, brush_color, tag)
        drawing.AddLineToShape(current_shape.node, start_pos, new_pos, brush_color)
    elseif drawing.mode == drawing.MODE_CIRCLE then
        local tag = current_shape.node:getTag()
        drawing.DestroySprite(current_shape.node)
        radius = ccpDistance(start_pos, new_pos)
        current_shape.node = drawing.DrawCircle(start_pos, radius, brush_color, tag)
    else
        error('invalid drawing mode: ' .. tostring(drawing.mode))
    end
end
--- Sample OnTouchEnded for drawing-based games.  For bespoke drawing behaviour
-- clone and modify this code.
function drawing.OnTouchEnded(x, y)
    -- Draw the final line segment and the end point of the line
    if drawing.mode == drawing.MODE_FREEHAND then
        new_pos = ccp(x, y)
        local length = ccpDistance(new_pos, last_pos);
        if length > brush_thickness then
            drawing.AddLineToShape(current_shape.node, last_pos, new_pos, brush_color)
        end
        drawing.DrawEndPoint(current_shape.node, new_pos, brush_color)
    elseif drawing.mode == drawing.MODE_CIRCLE or drawing.mode == drawing.MODE_LINE then
        --
    else
        error('invalid drawing mode: ' .. tostring(drawing.mode))
    end
    MakeBodyDynamic(current_shape.node:getB2Body())
    local rtn = current_shape
    last_pos = nil
    start_pos = nil
    current_shape = nil
    return rtn
end
return drawing


editor

--- Editor logic.
local util = require 'util'
local gui = require 'gui'
local drawing = require 'drawing'
local editor = {}
local MENU_DRAW_ORDER = 3
local VELOCITY_ITERATIONS = 8
local POS_ITERATIONS = 1
local actions = { ADD_SHAPE = 1, MOVE = 2 }
local undo_buffer = {}
local redo_buffer = {}
local function Set(list)
    local set = {}
    for _, l in ipairs(list) do set[l] = true end
    return set
end
local function SerializeLevel()
    local ignore_keys = Set({ 'tag', 'script', 'tag_map', 'tag_list', 'object_map' })
    local key_map = { tag_str = 'tag', script_name = 'script' }
    local output = util.TableToYaml(level_obj, ignore_keys, key_map)
    return '# Automatically generated by editor.lua\n\n' .. output
end
function editor.Update(delta)
    if level_obj.run_physics then
      level_obj.world:Step(delta, VELOCITY_ITERATIONS, POS_ITERATIONS)
    end
end
function editor.OnTouchBegan(x, y, tapcount)
    if drawing.IsDrawing() then
        return false
    end
    last_draw_time = CCTime:getTime()
    return drawing.OnTouchBegan(x, y, tapcount)
end
function editor.OnTouchMoved(x, y)
    drawing.OnTouchMoved(x, y)
end
local function AddAction(action_type, properties)
    properties.action = action_type
    redo_buffer = {}
    table.insert(undo_buffer, properties)
end
function editor.OnTouchEnded(x, y)
    last_drawn_shape = drawing.OnTouchEnded(x, y)
    AddAction(actions.ADD_SHAPE, { shape = last_drawn_shape })
end
local object_handlers = {}
local start_pos = nil
local touch_pos = nil
function object_handlers.OnTouchBegan(self, x, y, tapcount)
    -- Only move one object at a time.
    if touch_pos then
        return false
    end
    -- Store start local of object and start location of touch
    start_pos = ccp(self.node:getPositionX(), self.node:getPositionY())
    touch_pos = ccp(x, y)
    return true
end
function object_handlers.OnTouchMoved(self, x, y, tapcount)
    local delta = ccp(x - touch_pos.x, y - touch_pos.y)
    self.node:setPosition(ccp(start_pos.x + delta.x, start_pos.y + delta.y))
end
function object_handlers.OnTouchEnded(self)
    local new_position = ccp(self.node:getPositionX(), self.node:getPositionY())
    AddAction(actions.MOVE, { object = self,
                              old_position = start_pos,
                              new_position = new_position })
    touch_pos = nil
    start_pos = nil
end
local function HandleRestart()
    GameManager:sharedManager():Restart()
end
local function ChangeTool(value)
    if value == 'Line' then
        drawing.mode = drawing.MODE_LINE
    elseif value == 'Select' then
        drawing.mode = drawing.MODE_SELECT
    elseif value == 'Paint' then
        drawing.mode = drawing.MODE_FREEHAND
    elseif value == 'Circle' then
        drawing.mode = drawing.MODE_CIRCLE
    else
        error('unknown tool: ' .. value)
    end
end
local function Save(value)
   local string = SerializeLevel()
   print("***")
   print(string)
   print("***")
end
local function Undo(value)
    if #undo_buffer == 0 then
        return
    end
    item = table.remove(undo_buffer, #undo_buffer)
    table.insert(redo_buffer, item)
    if item.action == actions.MOVE then
        print("undo move")
        item.object.node:runAction(CCMoveTo:create(0.2, item.old_position))
    elseif item.action == actions.ADD_SHAPE then
        print("undo new")
        drawing.DestroySprite(item.shape.node)
        level_obj.object_map[1].node:setPosition(ccp(0, 0))
    else
        error('unknown undo action: ' .. item.action)
    end
end
local function Redo(value)
    if #redo_buffer == 0 then
        return
    end
    item = table.remove(redo_buffer, #redo_buffer)
    table.insert(undo_buffer, item)
    if item.action == actions.MOVE then
        print("redo move")
        item.object.node:runAction(CCMoveTo:create(0.2, item.new_position))
    elseif item.action == actions.ADD_SHAPE then
        print("redo new")
        -- drawing.DestroySprite(item.shape.node)
        -- level_obj.object_map[1].node:setPosition(ccp(0, 0))
    else
        error('unknown redo action: ' .. item.action)
    end
end
local function ToggleDebug()
    level_obj.layer:ToggleDebug()
end
local function ToggleRun()
    level_obj.run_physics = not level_obj.run_physics
end
local function HandleRestart()
    GameManager:sharedManager():Restart()
end
local function HandleExit()
    CCDirector:sharedDirector():popScene()
end
function editor.StartLevel(level_number)
    -- Create a textual menu it its own layer as a sibling of the LevelLayer
    level_obj.run_physics = false
    menu_def = {
        font_size = 24,
        align = 'Left',
        pos = { 10, 300 },
        items = {
            { name='Select', callback=ChangeTool },
            { name='Paint', callback=ChangeTool },
            { name='Circle', callback=ChangeTool },
            { name='Line', callback=ChangeTool },
            { name='Undo', callback=Undo },
            { name='Redo', callback=Redo },
            { name='Run', callback=ToggleRun },
            { name='Save', callback=Save },
            { name='Exit', callback=HandleExit },
            { name='Toggle Debug', callback=ToggleDebug },
        },
    }
    local menu = gui.CreateMenu(menu_def)
    local parent = level_obj.layer:getParent()
    parent:addChild(menu, MENU_DRAW_ORDER)
    -- Override all the object behaviour scripts.
    drawing.handlers = object_handlers
    for _, object in pairs(level_obj.object_map) do
        object.script = object_handlers
    end
end
return editor


util

--- Utility functions and constants shared by nacltoons lua code
local yaml = require 'yaml'
local util = {}
util.PTM_RATIO = 32
util.tags = {
    TAG_DYNAMIC_START = 0xff,
}
-- Convert a value from screen coordinate system to Box2D world coordinates
function util.ScreenToWorld(value)
    return value / util.PTM_RATIO
end
function util.WorldToScreen(value)
    return value * util.PTM_RATIO
end
--- Log messages to console
function util.Log(arg)
    print('LUA: ' .. tostring(arg))
end
function util.PointToString(point)
    return string.format('%dx%d', point.x, point.y)
end
function util.VecToString(v)
    return string.format('%dx%d', util.WorldToScreen(v.x), util.WorldToScreen(v.y))
end
--- Create CCPoint from a lua table containing 2 elements.
-- This is used to convert point data from .def files into
-- the cocos2dx coordinate space.
function util.PointFromLua(point, absolute)
    if absolute == nil then
        absolute = true
    end
    if absolute then
        return ccp(point[1] + game_obj.origin.x, point[2] + game_obj.origin.y)
    else
        return ccp(point[1], point[2])
    end
end
--- Convert CCPoint to b2Vec.
function util.b2VecFromCocos(cocos_vec)
    return b2Vec2:new_local(util.ScreenToWorld(cocos_vec.x),
                            util.ScreenToWorld(cocos_vec.y))
end
--- Load a yaml file and return a lua table that represents the data
-- in the file.
function util.LoadYaml(filename)
    local filedata = io.open(filename, "r"):read("*all")
    return yaml.load(filedata)
end
--- Escape string for inclusion in yaml output.  Normal strings
-- that contain no special characters don't even need quoting in
-- in yaml.  This function adds quotes and escape chars as needed.
local function EscapeYamlString(str)
    -- TODO(sbc): implement this once we start needs to use special
    -- chars in our data files.
    return str
end
function util.TableToYamlOneLine(tt, ignore_keys, key_map)
    local sb = {}
    local is_array = #tt ~= 0
    ignore_keys = ignore_keys or {}
    key_map = key_map or {}
    if is_array then
        table.insert(sb, '[')
    else
        table.insert(sb, '{ ')
    end
    local first = true
    for key, value in pairs(tt) do
        if not ignore_keys[key] and type(value) ~= 'userdata' then
            if first then
                first = false
            else
                table.insert(sb, ', ')
            end
            key = key_map[key] or key
            if type(key) ~= 'number' then
                table.insert(sb, tostring(key) .. ': ')
            end
            if type(value) == 'table' then
                table.insert(sb, util.TableToYamlOneLine(value, ignore_keys, key_map))
            elseif type(value) == 'boolean' or type(value) == 'number' then
                table.insert(sb, string.format("%s", tostring(value)))
            elseif type(value) == 'string' then
                table.insert(sb, string.format("%s", EscapeYamlString(value)))
            elseif type(value) == 'userdata' then
                error('cannot serialise <userdata>')
            else
                table.insert(sb, string.format("'%s'", tostring(value)))
            end
        end
    end
    if is_array then
        table.insert(sb, ']')
    else
        table.insert(sb, ' }')
    end
    return table.concat(sb)
end
--- Serialise a lua data structure (table) to a yaml string
function util.TableToYaml(tt, ignore_keys, key_map, long_form, indent)
    indent = indent or 0
    ignore_keys = ignore_keys or {}
    key_map = key_map or {}
    -- special case for when the entire table fits on a single line
    if not long_form then
        local oneline = util.TableToYamlOneLine(tt, ignore_keys, key_map)
        if #oneline + indent < 80 then
            return oneline
        end
    end
    local sb = {}
    local is_array = true
    local next_indent = indent + 1
    local skip_first = true
    if #tt == 0 then
       is_array = false
    end
    if is_array then
       skip_first = false
       next_indent = indent + 2
       table.insert(sb, '\n')
    end
    for key, value in pairs(tt) do
        if not ignore_keys[key] and type(value) ~= 'userdata' then
            if skip_first then
                skip_first = false
            else
                table.insert(sb, string.rep(' ', indent))
            end
            if is_array then
                table.insert(sb, '- ')
            end
            key = key_map[key] or key
            if type(key) ~= 'number' then
                table.insert(sb, tostring(key))
                table.insert(sb, ':')
                if type(value) ~= 'table' then
                     table.insert(sb, ' ')
                end
            end
            if type(value) == 'table' then
                local childtable = util.TableToYaml(value, ignore_keys, key_map, long_form, next_indent)
                if string.sub(childtable, -1) ~= '\n' then
                    childtable = childtable .. '\n'
                    if not is_array then
                        childtable = ' ' .. childtable
                    end
                end
                table.insert(sb, childtable)
            elseif type(value) == 'boolean' or type(value) == 'number' then
                table.insert(sb, string.format("%s\n", tostring(value)))
            elseif type(value) == 'string' then
                table.insert(sb, string.format("%s\n", EscapeYamlString(value)))
            elseif type(value) == 'userdata' then
                error('cannot serialise <userdata>')
            else
                table.insert(sb, string.format("'%s'\n", tostring(value)))
            end
        end
    end
    return table.concat(sb)
end
--- Serialise a lua table by converting it to a string in the standard lua
-- table notation.
function util.TableToString(tt, ignore_keys, key_map, indent)
    indent = indent or 0
    ignore_keys = ignore_keys or {}
    key_map = key_map or {}
    local sb = {}
    indent = indent + 2
    table.insert(sb, '{\n')
    for key, value in pairs(tt) do
        if ignore_keys[key] == nil and type(value) ~= 'userdata' then
            table.insert(sb, string.rep(' ', indent))
            key = key_map[key] or key
            if type(key) ~= 'number' then
                table.insert(sb, tostring(key))
                table.insert(sb, ' = ')
            end
            if type(value) == 'table' then
                table.insert(sb, util.TableToString(value, ignore_keys, key_map, indent))
            elseif type(value) == 'boolean' or type(value) == 'number' then
                table.insert(sb, string.format("%s,\n", tostring(value)))
            else
                table.insert(sb, string.format("'%s',\n", tostring(value)))
            end
        end
    end
    table.insert(sb, string.rep (' ', indent - 2))
    table.insert(sb, '}\n')
    return table.concat(sb)
end
return util


loader

-- Main entry points of the lua game engine.
-- Currently this file exposed 3 functions to the C++ code during
-- startup:
--  - LoadGame  (called my game_manager to load game.def)
--  - LoadLevel  (called by level_layer to load a level)
--
-- There are also 3 functions for which the game can define its own
-- handlers:
--  - OnContactBegan
--  - OnContactEnded
--  - StartLevel
local drawing = require 'drawing'
local path = require 'path'
local touch_handler = require 'touch_handler'
local util = require 'util'
local validate = require 'validate'
-- Make local alias of util functions so loader code can be shorter
-- and easier to read.
local Log = util.Log
-- The currently loaded game (set by LoadGame)
game_obj = nil
-- The currently loaded level (set by LoadLevel)
level_obj = nil
--- Load game def from the given filename.  This function loads
-- the game.def file which is essentailly a dictionary and performs
-- a bit of post-processing on it.
local function LoadGameDef(filename)
    Log('loading gamedef: '..filename)
    local game = util.LoadYaml(filename)
    game.root = path.dirname(filename)
    validate.ValidateGameDef(filename, game)
    Log('found ' .. #game.levels .. ' level(s)')
    game.filename = filename
    if game.script then
        Log('loading game script: ' .. game.script)
        game.script = dofile(path.join(game.root, game.script))
    end
    return game
end
function RegisterObject(object, tag, tag_str)
    level_obj.tag_list[tag] = tag_str
    assert(level_obj.object_map[tag] == nil, 'object_map already contains ' .. tag)
    level_obj.object_map[tag] = object
    -- If a tag_str is given then register it in the string -> int mapping
    if tag_str then
        assert(level_obj.tag_map[tag_str] == nil, 'duplicate object tag: ' .. tag_str)
        level_obj.tag_map[tag_str] = tag
    end
    if not tag_str then tag_str = '' end
    Log('object registered: ' .. tag .. " = '" .. tag_str .. "'")
end
local function RegisterObjectDef(object)
    local new_tag = #level_obj.tag_list + 1
    if object.tag then
        object.tag_str = object.tag
    else
        object.tag_str = tostring(new_tag)
    end
    object.tag = new_tag
    RegisterObject(object, object.tag, object.tag_str)
end
local function LoadScript(obj_def)
    if obj_def.script and game_obj.game_mode ~= "edit" then
        Log('loading object script: ' .. obj_def.script)
        local script = path.join(game_obj.root, obj_def.script)
        obj_def.script = dofile(script)
        if obj_def.script and obj_def.script.Update then
            obj_def.node:scheduleUpdateWithPriorityLua(obj_def.script.Update, 0)
        end
    end
end
--- Load game data from a given root directory.
-- This game then becomes the currently running game.
function LoadGame(root_dir)
   game_root = root_dir
   game_obj = LoadGameDef(path.join(game_root, 'game.def'))
   game_obj.origin = CCDirector:sharedDirector():getVisibleOrigin()
   local default_game
   if not game_obj.script or not game_obj.script.StartGame then
       default_game = dofile('default_game.lua')
   end
   if not game_obj.script then
       game_obj.script = default_game
   end
   if game_obj.script.StartGame then
       game_obj.script.StartGame()
   else
       default_game.StartGame()
   end
   if game_obj.assets.music then
       game_obj.assets.music = CCFileUtils:sharedFileUtils():fullPathForFilename(game_obj.assets.music)
       SimpleAudioEngine:sharedEngine():preloadBackgroundMusic(game_obj.assets.music)
   end
end
local function LevelInit()
    -- level_obj.tag_map maps string tags to integer tags
    -- level_obj.tag_list is simply a list of string tags
    -- level_obj.object_map maps tags to object defs
    level_obj.tag_map = {}
    level_obj.tag_list = {}
    level_obj.object_map = {}
end
local function GameUpdate(delta)
    if game_obj.script.Update then
        game_obj.script.Update(delta)
    end
    if level_obj and level_obj.script and level_obj.script.Update then
        level_obj.script.Update(delta)
    end
end
--- Load the given level of the given game
function LoadLevel(layer, level_number)
    Log('loading level ' .. level_number .. ' from ' .. game_obj.filename)
    -- Get level descrition object
    assert(level_number <= #game_obj.levels and level_number > 0,
           'Invalid level number: ' .. level_number)
    local filename = path.join(game_obj.root, game_obj.levels[level_number])
    level_obj = util.LoadYaml(filename)
    validate.ValidateLevelDef(filename, game_obj, level_obj)
    LevelInit()
    level_obj.layer = layer
    level_obj.world = layer:GetWorld()
    local assets = game_obj.assets
    -- Load brush image
    level_obj.brush = CCSpriteBatchNode:create(assets.brush_image, 500)
    layer:addChild(level_obj.brush, 1)
    drawing.SetBrush(level_obj.brush)
    -- Start music playback
    if game_obj.assets.music then
        SimpleAudioEngine:sharedEngine():playBackgroundMusic(game_obj.assets.music, true)
    end
    -- Load background image
    if game_obj.assets.background_image then
        local winsize = CCDirector:sharedDirector():getWinSize()
        local sprite = CCSprite:create(game_obj.assets.background_image)
        sprite:setPosition(ccp(winsize.width/2, winsize.height/2))
        layer:addChild(sprite)
    end
    -- Load shapes
    local function LoadShapes(shapes)
        for _, shape_def in ipairs(shapes) do
            if #shape_def > 0 then
                LoadShapes(shape_def)
            else
                RegisterObjectDef(shape_def)
                shape_def.node = drawing.CreateShape(shape_def)
                LoadScript(shape_def)
            end
        end
    end
    if level_obj.shapes then
        LoadShapes(level_obj.shapes)
    end
    -- Load custom level script
    level_obj.node = level_obj.layer
    LoadScript(level_obj)
    if game_obj.script.Update then
        level_obj.layer:scheduleUpdateWithPriorityLua(GameUpdate, 0)
    end
    layer:registerScriptTouchHandler(touch_handler.TouchHandler)
    StartLevel(level_number)
end
local function ApplyToAllChildren(node, callback)
    callback(node)
    local children = node:getChildren()
    if children then
        for i=0,children:count()-1 do
            child = children:objectAtIndex(i)
            ApplyToAllChildren(child, callback)
        end
    end
end
function LevelComplete()
    level_obj.layer:unscheduleUpdate()
    level_obj.layer:LevelComplete()
    local function unschedule(node)
        node:unscheduleUpdate()
    end
    ApplyToAllChildren(level_obj.layer, unschedule)
    level_obj = nil
end
local function CallCollisionHandler(tag1, tag2, handler_name)
    local object1 = level_obj.object_map[tag1]
    local object2 = level_obj.object_map[tag2]
    -- only call handlers if the objects in question have tags
    -- that are known to the currently running level
    if object1 == nil or object2 == nil then
        return
    end
    -- call the individual object's collision handler, if any
    if object1.script and object1.script[handler_name] then
        object1.script[handler_name](object1, object2)
    end
    if object2.script and object2.script[handler_name] then
        object2.script[handler_name](object2, object1)
    end
    if level_obj.script and level_obj.script[handler_name] then
        level_obj.script[handler_name](object1, object2)
    end
    -- call the game's collision handler, if any
    if game_obj.script[handler_name] then
        game_obj.script[handler_name](object1, object2)
    end
end
function OnContactBegan(tag1, tag2)
    CallCollisionHandler(tag1, tag2, 'OnContactBegan')
end
function OnContactEnded(tag1, tag2)
    CallCollisionHandler(tag1, tag2, 'OnContactEnded')
end
function StartLevel(level_number)
    -- only call handlers if the objects in question have tags
    -- that are known to the currently running level
    if game_obj.script.StartLevel then
        game_obj.script.StartLevel(level_number)
    end
end


path

--- Path manipulation utility functions.
local path = {}
function path.dirname(filename)
    while true do
        if filename == "" or string.sub(filename, -1) == "/" then
            break
        end
        filename = string.sub(filename, 1, -2)
    end
    if filename == "" then
        filename = "."
    end
    return filename
end
function path.join(dirname, basename)
    if string.sub(dirname, -1) ~= "/" then
        dirname = dirname .. "/"
    end
    return dirname .. basename
end
return path


-- Touch handling code for game engine.
-- This module defines a singe global table touch_handler which contains
-- a single function called TouchHandler.  This functions registered
-- as the touch handler for CCLayer in which the gameplace takes place.
local util = require 'util'
local touch_handler = {}
local touch_state = {
    touchid = -1,
    reciever = nil,
    is_object = false,
}
local function FindTaggedBodiesAt(x, y)
    local b2pos = util.b2VecFromCocos(ccp(x, y))
    local found_bodies = {}
    local found_something = false
    local function handler(body)
        found_something = true
        local tag = body:GetUserData()
        if tag ~= 0 then
            -- FindBodiesAt can report the same bodies more than
            -- once since it reports the body for each fixture that
            -- exists at a given point, so filter out duplicates here.
            if not found_bodies[tag] then
                util.Log("found body.. " .. tag)
                assert(level_obj.object_map[tag])
                found_bodies[tag] = level_obj.object_map[tag]
            end
        end
    end
    level_obj.layer:FindBodiesAt(b2pos, handler)
    if found_something and #found_bodies == 0 then
        util.Log("Found untagged bodies")
    end
    return found_bodies
end
local lasttap_time = 0
local lasttap_count = 1
local lasttap_location = nil
touch_handler.DOUBLE_CLICK_INTERVAL = 0.250
touch_handler.DOUBLE_CLICK_TOLERANCE = 5
local function OnTouchBegan(x, y, touchid)
    -- Do double tap detection based on taps that occur within
    -- DOUBLE_CLICK_INTERVAL of each other and with a certain
    -- distance of each other.
    local now = CCTime:getTime()
    local distance = 0
    if lasttap_location then
        distance = ccpDistance(ccp(x, y), ccp(lasttap_location[1], lasttap_location[2]))
    end
    if (now - lasttap_time > touch_handler.DOUBLE_CLICK_INTERVAL
        or distance > touch_handler.DOUBLE_CLICK_TOLERANCE) then
        lasttap_count = 1
    else
        lasttap_count = lasttap_count + 1
    end
    lasttap_time = now
    lasttap_location = { x, y }
    tapcount = lasttap_count
    util.Log('tapcount ' .. tapcount)
    local objects = FindTaggedBodiesAt(x, y, 0x1)
    for _, obj_def in pairs(objects) do
        if obj_def.script and obj_def.script.OnTouchBegan then
            if obj_def.script.OnTouchBegan(obj_def, x, y, tapcount) then
                touch_state.touchid = touchid
                touch_state.receiver = obj_def
                touch_state.is_object = true
                return true
            end
        end
    end
    -- If neither object handles the touch then ask the level
    if level_obj.script and level_obj.script.OnTouchBegan then
        if level_obj.script.OnTouchBegan(x, y, tapcount) then
            touch_state.touchid = touchid
            touch_state.receiver = level_obj
            touch_state.is_object = false
            return true
        end
    end
    -- Finally ask the game.
    if game_obj.script.OnTouchBegan then
        if game_obj.script.OnTouchBegan(x, y, tapcount) then
            touch_state.touchid = touchid
            touch_state.receiver = game_obj
            touch_state.is_object = false
            return true
        end
    end
    -- No object handle the touch.  Returning false tells cocos2dx we are not
    -- interested in this particular touch.
    return false
end
local function OnTouchMoved(x, y, touchid)
    -- ignore touch moves unless the touchid matches the one we are currently tracking
    if touchid == touch_state.touchid then
        if touch_state.receiver.script.OnTouchMoved then
            if touch_state.is_object then
                touch_state.receiver.script.OnTouchMoved(touch_state.receiver, x, y)
            else
                touch_state.receiver.script.OnTouchMoved(x, y)
            end
        end
    end
end
local function OnTouchEnded(x, y, touchid)
    -- ignore touch ends unless the touchid matches the one we are currently tracking
    if touchid == touch_state.touchid then
        if touch_state.receiver.script.OnTouchEnded then
            if touch_state.is_object then
                touch_state.receiver.script.OnTouchEnded(touch_state.receiver, x, y)
            else
                touch_state.receiver.script.OnTouchEnded(x, y)
            end
        end
        touch_state.touchid = -1
        touch_state.receiver = nil
        touch_state.is_object = false
    end
end
function touch_handler.TouchHandler(touch_type, x, y, touchid)
    if touch_type == 'began' then
        return OnTouchBegan(x, y, touchid)
    elseif touch_type == 'moved' then
        return OnTouchMoved(x, y, touchid)
    elseif touch_type == 'ended' then
        return OnTouchEnded(x, y, touchid)
    end
end
return touch_handler


validate

-- Functions for validating game data files (.def files).
-- This module provides a single global called 'validate' which
-- contains two function:
--   ValidateGameDef
--   ValidateLevelDef
--
-- It is possible to run this code a game.def file from the command line:
-- $ ./lua.sh ./data/res/validate.lua data/res/sample_game/game.def
local path = require 'path'
local util = require 'util'
local yaml = require 'yaml'
local function Error(filename, message)
    assert(false, "Error in '" .. filename .. "':" ..  message)
end
--- Return true if a list contains a given element, false otherwise
local function ListContains(list, element)
  for _, value in ipairs(list) do
      if value == element then
          return true
      end
  end
  return false
end
--- Check that all the keys in the given object are in the list
-- of valid keys.
local function CheckValidKeys(filename, object, valid_keys)
    for key, _ in pairs(object) do
        if not ListContains(valid_keys, key) then
            return Error(filename, 'invalid key: ' .. key)
        end
    end
end
local function CheckRequiredKeys(filename, object, required_keys, name)
    for _, required in ipairs(required_keys) do
        if object[required] == nil then
            Error(filename, 'missing required key in ' .. name .. ' : ' .. required)
        end
    end
end
validate = {}
--- Validate a game.def file.
validate.ValidateGameDef = function(filename, gamedef)
    local function Err(message)
        Error(filename, message)
    end
    if type(gamedef) ~= 'table' then
        return Err('file does not evaluate to an object of type table')
    end
    CheckValidKeys(filename, gamedef, { 'assets', 'script', 'levels', 'root' })
    if not gamedef.assets then
        return
    end
    CheckRequiredKeys(filename, gamedef.assets, { 'level_icon', 'level_icon_selected' }, 'asset list')
    for asset_name, asset_file in pairs(gamedef.assets) do
        local fullname = path.join(gamedef.root, asset_file)
        local f = io.open(fullname, 'r')
        if f ~= nil then
            io.close(f)
        else
            Err('asset does not exist: ' .. gamedef.asset_name)
        end
        gamedef.assets[asset_name] = fullname
    end
end
--- Validate a level.def file.
validate.ValidateLevelDef = function(filename, gamedef, leveldef)
    local function Err(message)
        Error(filename, message)
    end
    if type(leveldef) ~= 'table' then
        return Err("file does not evaluate to an object of type 'table'")
    end
    CheckValidKeys(filename, leveldef, { 'num_stars', 'shapes', 'script' })
    if leveldef.shapes then
        local valid_keys = { 'script', 'pos', 'children', 'sensor', 'image', 'start', 'finish', 'color', 'type', 'anchor', 'tag', 'dynamic' }
        local valid_types = { 'compound', 'line', 'edge', 'image' }
        local required_keys = { 'type' }
        local function ValidateShapeList(shapes)
            for _, shape in pairs(shapes) do
                if #shape > 0 then
                    ValidateShapeList(shape)
                else
                    CheckValidKeys(filename, shape, valid_keys)
                    CheckRequiredKeys(filename, shape, required_keys, 'shape')
                    if not ListContains(valid_types, shape.type) then
                        Err('invalid shape type: ' .. shape.type)
                    end
                end
            end
        end
        ValidateShapeList(leveldef.shapes)
    end
end
if debug.getinfo(1).what == "main" and debug.getinfo(3) == nil then
   -- When run from the command line run validation on passed in game.def file.
   local filename = arg[1]
   local gamedef = util.LoadYaml(filename)
   gamedef.root = path.dirname(filename)
   validate.ValidateGameDef(filename, gamedef)
   -- Now validate all the levels in the game.
   for _, level in ipairs(gamedef.levels) do
       filename = path.join(gamedef.root, level)
       level = util.LoadYaml(filename)
       validate.ValidateLevelDef(filename, gamedef, level)
   end
   print("Validation successful!")
end
return validate


gui

function gui.GridLayout(menu, max_cols, padding)
    local items = menu:getChildren()
    local item0 = tolua.cast(items:objectAtIndex(0), "CCNode")
    local item_size = item0:getContentSize()
    local w = item_size.width
    local h = item_size.height
    local num_rows = (items:count() + max_cols - 1) / max_cols
    -- Totol menu height, including padding between rows.
    local menu_height = num_rows * (h + padding.y) - padding.y
    local startY = (600 - menu_height) / 2
    local item_index = 0
    for i=0,num_rows-1 do
        local num_cols = math.min(max_cols, items:count() - (i * max_cols))
        local row_width = num_cols * w + padding.x * (num_cols - 1)
        local x = (800 - row_width)/2
        local y = startY + (num_rows - i - 1) * (padding.y + h)
        for i=0,num_cols-1 do
            local item = items:objectAtIndex(item_index)
            item:setAnchorPoint(CCPointMake(0,0))
            item:setPosition(ccp(x, y))
            util.Log("Setting pos of menu item %d: %.fx%.f", item_index, x, y)
            x = x + padding.x + w
            item_index = item_index + 1
        end
    end
    assert(item_index == items:count())
end
function gui.CreateLabel(label_def)
    local font_name = label_def.font_name or gui.FONT_NAME
    local font_size = label_def.font_size or gui.FONT_SIZE
    local name = label_def.name or "UNNAMED"
    local value = label_def.value or name
    local label = CCLabelTTF:create(name, font_name, font_size)
    local item = CCMenuItemLabel:create(label)
    if label_def.callback then
        local callback = function()
            util.Log("Select " .. name .. " = " .. value)
            label_def.callback(value)
        end
        item:registerScriptTapHandler(callback)
    end
    util.Log("Create Label: " .. name .. " = " .. value)
    return item
end
function gui.CreateMenu(menu_def, parent)
    local menu = CCMenu:create()
    local align = menu_def.align or 'Center'
    if menu_def.pos then
        menu:setPosition(ccp(menu_def.pos[1], menu_def.pos[2]))
        util.Log("Set Position " .. menu_def.pos[1] .. ", " ..  menu_def.pos[2])
    end
    if menu_def.size then
        menu:setContentSize(ccp(menu_def.size[1], menu_def.size[2]))
    end
    local children = {}
    for index, label_def in ipairs(menu_def.items) do
        label_def.callback = label_def.callback or menu_def.callback
        label_def.font_size = label_def.font_size or menu_def.font_size
        label_def.font_name = label_def.font_name or menu_def.font_name
        label = gui.CreateLabel(label_def)
        menu:addChild(label)
        children[index] = label
    end
    menu:alignItemsVertically()
    for index, label in ipairs(children) do
        if menu_def.align == 'Right' then
            size = label:getContentSize()
            label:setPositionX(0 - size.width / 2)
        end
        if menu_def.align == 'Left' then
            size = label:getContentSize()
            label:setPositionX(size.width / 2)
        end
    end
    return menu
end
return gui


-- Example of a custom level.lua script which can override game
-- lua script.  All this level script does it peform drawing offset
-- from the touch location.

local drawing = require 'drawing'

local handlers = {}

local drawing_offset = 30

function handlers.OnTouchBegan(x, y)
    return drawing.OnTouchBegan(x + drawing_offset, y + drawing_offset)
end

function handlers.OnTouchMoved(x, y)
    drawing.OnTouchMoved(x + drawing_offset, y + drawing_offset)
end

function handlers.OnTouchEnded(x, y)
    drawing.OnTouchEnded(x + drawing_offset, y + drawing_offset)
end

return handlers


ball

-- Object behaviour script.  This script returns a table containing
-- callback function which are called when events occur on a given
-- object.  It should not pollute the global namespace.
--
-- The following callbacks are available to game objects:
--
--   OnTouchBegan(self, x, y) - return true to accept touch
--   OnTouchMoved(self, x, y)
--   OnTouchEnded(self, x, y)
--   OnContactBegan(self, other)
--   OnContactEnded(self, other)
--
-- As well as arguments recieved this script has access
-- to global game variables:
--   game_obj - global state, which persists between level and
--              level reloads but is destroyed on game restart
--              or reload.
--   level_obj - stores level specific state which is destroyed
--               on level restart.

local handlers = {}

local util = require 'util'

function handlers.OnTouchBegan(self, x, y)
    util.Log('ball touch began')
    return true
end

function handlers.OnTouchEnded(self, x, y)
    util.Log('ball touch ended')
end

function handlers.OnContactBegan(self, other)
    util.Log('ball contact start: ' .. other.tag_str)
end

function handlers.OnContactEnded(self, other)
    util.Log('ball contact ended: ' .. other.tag_str)
end

return handlers