local setmetatable = setmetatable
local rawset = rawset
local rawget = rawget
local error = error
local ipairs = ipairs
local pairs = pairs
local print = print
local table = table
local string = string
local tostring = tostring
local type = type
local pb = require("pb2")
local wire_format = require("protobuf.wire_format")
local type_checkers = require("protobuf.type_checkers")
local encoder = require("protobuf.encoder")
local decoder = require("protobuf.decoder")
local listener_mod = require("protobuf.listener")
local containers = require("protobuf.containers")
local descriptor = require("protobuf.descriptor")
local FieldDescriptor = descriptor.FieldDescriptor
local text_format = require("protobuf.text_format")
local protobuf = {}
if setfenv then
  setfenv(1, protobuf)
else
  _ENV = protobuf
end
local make_descriptor = function(name, descriptor, usable_key)
  local meta = {
    __newindex = function(self, key, value)
      if usable_key[key] then
        rawset(self, key, value)
      else
        error("error key: " .. key)
      end
    end
  }
  meta.__index = meta
  
  function meta.__call()
    return setmetatable({}, meta)
  end
  
  protobuf[name] = setmetatable(descriptor, meta)
end
make_descriptor("Descriptor", {}, {
  name = true,
  full_name = true,
  filename = true,
  containing_type = true,
  fields = true,
  nested_types = true,
  enum_types = true,
  extensions = true,
  options = true,
  is_extendable = true,
  extension_ranges = true
})
make_descriptor("FieldDescriptor", FieldDescriptor, {
  name = true,
  full_name = true,
  index = true,
  number = true,
  type = true,
  cpp_type = true,
  label = true,
  has_default_value = true,
  default_value = true,
  containing_type = true,
  message_type = true,
  enum_type = true,
  is_extension = true,
  extension_scope = true
})
make_descriptor("EnumDescriptor", {}, {
  name = true,
  full_name = true,
  values = true,
  containing_type = true,
  options = true
})
make_descriptor("EnumValueDescriptor", {}, {
  name = true,
  index = true,
  number = true,
  type = true,
  options = true
})
local FIELD_TYPE_TO_WIRE_TYPE = {
  [FieldDescriptor.TYPE_DOUBLE] = wire_format.WIRETYPE_FIXED64,
  [FieldDescriptor.TYPE_FLOAT] = wire_format.WIRETYPE_FIXED32,
  [FieldDescriptor.TYPE_INT64] = wire_format.WIRETYPE_VARINT,
  [FieldDescriptor.TYPE_UINT64] = wire_format.WIRETYPE_VARINT,
  [FieldDescriptor.TYPE_INT32] = wire_format.WIRETYPE_VARINT,
  [FieldDescriptor.TYPE_FIXED64] = wire_format.WIRETYPE_FIXED64,
  [FieldDescriptor.TYPE_FIXED32] = wire_format.WIRETYPE_FIXED32,
  [FieldDescriptor.TYPE_BOOL] = wire_format.WIRETYPE_VARINT,
  [FieldDescriptor.TYPE_STRING] = wire_format.WIRETYPE_LENGTH_DELIMITED,
  [FieldDescriptor.TYPE_GROUP] = wire_format.WIRETYPE_START_GROUP,
  [FieldDescriptor.TYPE_MESSAGE] = wire_format.WIRETYPE_LENGTH_DELIMITED,
  [FieldDescriptor.TYPE_BYTES] = wire_format.WIRETYPE_LENGTH_DELIMITED,
  [FieldDescriptor.TYPE_UINT32] = wire_format.WIRETYPE_VARINT,
  [FieldDescriptor.TYPE_ENUM] = wire_format.WIRETYPE_VARINT,
  [FieldDescriptor.TYPE_SFIXED32] = wire_format.WIRETYPE_FIXED32,
  [FieldDescriptor.TYPE_SFIXED64] = wire_format.WIRETYPE_FIXED64,
  [FieldDescriptor.TYPE_SINT32] = wire_format.WIRETYPE_VARINT,
  [FieldDescriptor.TYPE_SINT64] = wire_format.WIRETYPE_VARINT
}
local NON_PACKABLE_TYPES = {
  [FieldDescriptor.TYPE_STRING] = true,
  [FieldDescriptor.TYPE_GROUP] = true,
  [FieldDescriptor.TYPE_MESSAGE] = true,
  [FieldDescriptor.TYPE_BYTES] = true
}
local _VALUE_CHECKERS = {
  [FieldDescriptor.CPPTYPE_INT32] = type_checkers.Int32ValueChecker(),
  [FieldDescriptor.CPPTYPE_INT64] = type_checkers.TypeChecker({string = true, number = true}),
  [FieldDescriptor.CPPTYPE_UINT32] = type_checkers.Uint32ValueChecker(),
  [FieldDescriptor.CPPTYPE_UINT64] = type_checkers.TypeChecker({string = true, number = true}),
  [FieldDescriptor.CPPTYPE_DOUBLE] = type_checkers.TypeChecker({number = true}),
  [FieldDescriptor.CPPTYPE_FLOAT] = type_checkers.TypeChecker({number = true}),
  [FieldDescriptor.CPPTYPE_BOOL] = type_checkers.TypeChecker({
    boolean = true,
    bool = true,
    int = true
  }),
  [FieldDescriptor.CPPTYPE_ENUM] = type_checkers.Int32ValueChecker(),
  [FieldDescriptor.CPPTYPE_STRING] = type_checkers.TypeChecker({string = true})
}
local TYPE_TO_BYTE_SIZE_FN = {
  [FieldDescriptor.TYPE_DOUBLE] = wire_format.DoubleByteSize,
  [FieldDescriptor.TYPE_FLOAT] = wire_format.FloatByteSize,
  [FieldDescriptor.TYPE_INT64] = wire_format.Int64ByteSize,
  [FieldDescriptor.TYPE_UINT64] = wire_format.UInt64ByteSize,
  [FieldDescriptor.TYPE_INT32] = wire_format.Int32ByteSize,
  [FieldDescriptor.TYPE_FIXED64] = wire_format.Fixed64ByteSize,
  [FieldDescriptor.TYPE_FIXED32] = wire_format.Fixed32ByteSize,
  [FieldDescriptor.TYPE_BOOL] = wire_format.BoolByteSize,
  [FieldDescriptor.TYPE_STRING] = wire_format.StringByteSize,
  [FieldDescriptor.TYPE_GROUP] = wire_format.GroupByteSize,
  [FieldDescriptor.TYPE_MESSAGE] = wire_format.MessageByteSize,
  [FieldDescriptor.TYPE_BYTES] = wire_format.BytesByteSize,
  [FieldDescriptor.TYPE_UINT32] = wire_format.UInt32ByteSize,
  [FieldDescriptor.TYPE_ENUM] = wire_format.EnumByteSize,
  [FieldDescriptor.TYPE_SFIXED32] = wire_format.SFixed32ByteSize,
  [FieldDescriptor.TYPE_SFIXED64] = wire_format.SFixed64ByteSize,
  [FieldDescriptor.TYPE_SINT32] = wire_format.SInt32ByteSize,
  [FieldDescriptor.TYPE_SINT64] = wire_format.SInt64ByteSize
}
local TYPE_TO_ENCODER = {
  [FieldDescriptor.TYPE_DOUBLE] = encoder.DoubleEncoder,
  [FieldDescriptor.TYPE_FLOAT] = encoder.FloatEncoder,
  [FieldDescriptor.TYPE_INT64] = encoder.Int64Encoder,
  [FieldDescriptor.TYPE_UINT64] = encoder.UInt64Encoder,
  [FieldDescriptor.TYPE_INT32] = encoder.Int32Encoder,
  [FieldDescriptor.TYPE_FIXED64] = encoder.Fixed64Encoder,
  [FieldDescriptor.TYPE_FIXED32] = encoder.Fixed32Encoder,
  [FieldDescriptor.TYPE_BOOL] = encoder.BoolEncoder,
  [FieldDescriptor.TYPE_STRING] = encoder.StringEncoder,
  [FieldDescriptor.TYPE_GROUP] = encoder.GroupEncoder,
  [FieldDescriptor.TYPE_MESSAGE] = encoder.MessageEncoder,
  [FieldDescriptor.TYPE_BYTES] = encoder.BytesEncoder,
  [FieldDescriptor.TYPE_UINT32] = encoder.UInt32Encoder,
  [FieldDescriptor.TYPE_ENUM] = encoder.EnumEncoder,
  [FieldDescriptor.TYPE_SFIXED32] = encoder.SFixed32Encoder,
  [FieldDescriptor.TYPE_SFIXED64] = encoder.SFixed64Encoder,
  [FieldDescriptor.TYPE_SINT32] = encoder.SInt32Encoder,
  [FieldDescriptor.TYPE_SINT64] = encoder.SInt64Encoder
}
local TYPE_TO_SIZER = {
  [FieldDescriptor.TYPE_DOUBLE] = encoder.DoubleSizer,
  [FieldDescriptor.TYPE_FLOAT] = encoder.FloatSizer,
  [FieldDescriptor.TYPE_INT64] = encoder.Int64Sizer,
  [FieldDescriptor.TYPE_UINT64] = encoder.UInt64Sizer,
  [FieldDescriptor.TYPE_INT32] = encoder.Int32Sizer,
  [FieldDescriptor.TYPE_FIXED64] = encoder.Fixed64Sizer,
  [FieldDescriptor.TYPE_FIXED32] = encoder.Fixed32Sizer,
  [FieldDescriptor.TYPE_BOOL] = encoder.BoolSizer,
  [FieldDescriptor.TYPE_STRING] = encoder.StringSizer,
  [FieldDescriptor.TYPE_GROUP] = encoder.GroupSizer,
  [FieldDescriptor.TYPE_MESSAGE] = encoder.MessageSizer,
  [FieldDescriptor.TYPE_BYTES] = encoder.BytesSizer,
  [FieldDescriptor.TYPE_UINT32] = encoder.UInt32Sizer,
  [FieldDescriptor.TYPE_ENUM] = encoder.EnumSizer,
  [FieldDescriptor.TYPE_SFIXED32] = encoder.SFixed32Sizer,
  [FieldDescriptor.TYPE_SFIXED64] = encoder.SFixed64Sizer,
  [FieldDescriptor.TYPE_SINT32] = encoder.SInt32Sizer,
  [FieldDescriptor.TYPE_SINT64] = encoder.SInt64Sizer
}
local TYPE_TO_DECODER = {
  [FieldDescriptor.TYPE_DOUBLE] = decoder.DoubleDecoder,
  [FieldDescriptor.TYPE_FLOAT] = decoder.FloatDecoder,
  [FieldDescriptor.TYPE_INT64] = decoder.Int64Decoder,
  [FieldDescriptor.TYPE_UINT64] = decoder.UInt64Decoder,
  [FieldDescriptor.TYPE_INT32] = decoder.Int32Decoder,
  [FieldDescriptor.TYPE_FIXED64] = decoder.Fixed64Decoder,
  [FieldDescriptor.TYPE_FIXED32] = decoder.Fixed32Decoder,
  [FieldDescriptor.TYPE_BOOL] = decoder.BoolDecoder,
  [FieldDescriptor.TYPE_STRING] = decoder.StringDecoder,
  [FieldDescriptor.TYPE_GROUP] = decoder.GroupDecoder,
  [FieldDescriptor.TYPE_MESSAGE] = decoder.MessageDecoder,
  [FieldDescriptor.TYPE_BYTES] = decoder.BytesDecoder,
  [FieldDescriptor.TYPE_UINT32] = decoder.UInt32Decoder,
  [FieldDescriptor.TYPE_ENUM] = decoder.EnumDecoder,
  [FieldDescriptor.TYPE_SFIXED32] = decoder.SFixed32Decoder,
  [FieldDescriptor.TYPE_SFIXED64] = decoder.SFixed64Decoder,
  [FieldDescriptor.TYPE_SINT32] = decoder.SInt32Decoder,
  [FieldDescriptor.TYPE_SINT64] = decoder.SInt64Decoder
}
local FIELD_TYPE_TO_WIRE_TYPE = {
  [FieldDescriptor.TYPE_DOUBLE] = wire_format.WIRETYPE_FIXED64,
  [FieldDescriptor.TYPE_FLOAT] = wire_format.WIRETYPE_FIXED32,
  [FieldDescriptor.TYPE_INT64] = wire_format.WIRETYPE_VARINT,
  [FieldDescriptor.TYPE_UINT64] = wire_format.WIRETYPE_VARINT,
  [FieldDescriptor.TYPE_INT32] = wire_format.WIRETYPE_VARINT,
  [FieldDescriptor.TYPE_FIXED64] = wire_format.WIRETYPE_FIXED64,
  [FieldDescriptor.TYPE_FIXED32] = wire_format.WIRETYPE_FIXED32,
  [FieldDescriptor.TYPE_BOOL] = wire_format.WIRETYPE_VARINT,
  [FieldDescriptor.TYPE_STRING] = wire_format.WIRETYPE_LENGTH_DELIMITED,
  [FieldDescriptor.TYPE_GROUP] = wire_format.WIRETYPE_START_GROUP,
  [FieldDescriptor.TYPE_MESSAGE] = wire_format.WIRETYPE_LENGTH_DELIMITED,
  [FieldDescriptor.TYPE_BYTES] = wire_format.WIRETYPE_LENGTH_DELIMITED,
  [FieldDescriptor.TYPE_UINT32] = wire_format.WIRETYPE_VARINT,
  [FieldDescriptor.TYPE_ENUM] = wire_format.WIRETYPE_VARINT,
  [FieldDescriptor.TYPE_SFIXED32] = wire_format.WIRETYPE_FIXED32,
  [FieldDescriptor.TYPE_SFIXED64] = wire_format.WIRETYPE_FIXED64,
  [FieldDescriptor.TYPE_SINT32] = wire_format.WIRETYPE_VARINT,
  [FieldDescriptor.TYPE_SINT64] = wire_format.WIRETYPE_VARINT
}
local IsTypePackable = function(field_type)
  return NON_PACKABLE_TYPES[field_type] == nil
end
local GetTypeChecker = function(cpp_type, field_type)
  if cpp_type == FieldDescriptor.CPPTYPE_STRING and field_type == FieldDescriptor.TYPE_STRING then
    return type_checkers.UnicodeValueChecker()
  end
  return _VALUE_CHECKERS[cpp_type]
end
local _DefaultValueConstructorForField = function(field)
  if field.label == FieldDescriptor.LABEL_REPEATED then
    if type(field.default_value) ~= "table" or #field.default_value ~= 0 then
      error("Repeated field default value not empty list:" .. tostring(field.default_value))
    end
    if field.cpp_type == FieldDescriptor.CPPTYPE_MESSAGE then
      local message_type = field.message_type
      return function(message)
        return containers.RepeatedCompositeFieldContainer(message._listener_for_children, message_type)
      end
    else
      local type_checker = GetTypeChecker(field.cpp_type, field.type)
      return function(message)
        return containers.RepeatedScalarFieldContainer(message._listener_for_children, type_checker)
      end
    end
  end
  if field.cpp_type == FieldDescriptor.CPPTYPE_MESSAGE then
    local message_type = field.message_type
    return function(message)
      result = message_type._concrete_class()
      result._SetListener(message._listener_for_children)
      return result
    end
  end
  return function(message)
    return field.default_value
  end
end
local _AttachFieldHelpers = function(message_meta, field_descriptor)
  local is_repeated = field_descriptor.label == FieldDescriptor.LABEL_REPEATED
  local is_packed = field_descriptor.has_options and field_descriptor.GetOptions().packed
  rawset(field_descriptor, "_encoder", TYPE_TO_ENCODER[field_descriptor.type](field_descriptor.number, is_repeated, is_packed))
  rawset(field_descriptor, "_sizer", TYPE_TO_SIZER[field_descriptor.type](field_descriptor.number, is_repeated, is_packed))
  rawset(field_descriptor, "_default_constructor", _DefaultValueConstructorForField(field_descriptor))
  local AddDecoder = function(wiretype, is_packed)
    local tag_bytes = encoder.TagBytes(field_descriptor.number, wiretype)
    message_meta._decoders_by_tag[tag_bytes] = TYPE_TO_DECODER[field_descriptor.type](field_descriptor.number, is_repeated, is_packed, field_descriptor, field_descriptor._default_constructor)
  end
  AddDecoder(FIELD_TYPE_TO_WIRE_TYPE[field_descriptor.type], False)
  if is_repeated and IsTypePackable(field_descriptor.type) then
    AddDecoder(wire_format.WIRETYPE_LENGTH_DELIMITED, True)
  end
end
local _AddEnumValues = function(descriptor, message_meta)
  for _, enum_type in ipairs(descriptor.enum_types) do
    for _, enum_value in ipairs(enum_type.values) do
      message_meta._member[enum_value.name] = enum_value.number
    end
  end
end
local _InitMethod = function(message_meta)
  return function()
    local self = {}
    self._cached_byte_size = 0
    self._cached_byte_size_dirty = false
    self._fields = {}
    self._is_present_in_parent = false
    self._listener = listener_mod.NullMessageListener()
    self._listener_for_children = listener_mod.Listener(self)
    return setmetatable(self, message_meta)
  end
end
local _AddPropertiesForRepeatedField = function(field, message_meta)
  local property_name = field.name
  message_meta._getter[property_name] = function(self)
    local field_value = self._fields[field]
    if field_value == nil then
      field_value = field._default_constructor(self)
      self._fields[field] = field_value
      if not self._cached_byte_size_dirty then
        message_meta._member._Modified(self)
      end
    end
    return field_value
  end
  message_meta._setter[property_name] = function(self)
    error("Assignment not allowed to repeated field \"" .. property_name .. "\" in protocol message object.")
  end
end
local _AddPropertiesForNonRepeatedCompositeField = function(field, message_meta)
  local property_name = field.name
  local message_type = field.message_type
  message_meta._getter[property_name] = function(self)
    local field_value = self._fields[field]
    if field_value == nil then
      field_value = message_type._concrete_class()
      field_value:_SetListener(self._listener_for_children)
      self._fields[field] = field_value
      if not self._cached_byte_size_dirty then
        message_meta._member._Modified(self)
      end
    end
    return field_value
  end
  message_meta._setter[property_name] = function(self, new_value)
    error("Assignment not allowed to composite field" .. property_name .. "in protocol message object.")
  end
end
local _AddPropertiesForNonRepeatedScalarField = function(field, message)
  local property_name = field.name
  local type_checker = GetTypeChecker(field.cpp_type, field.type)
  local default_value = field.default_value
  message._getter[property_name] = function(self)
    local value = self._fields[field]
    if value ~= nil then
      return self._fields[field]
    else
      return default_value
    end
  end
  message._setter[property_name] = function(self, new_value)
    type_checker(new_value)
    self._fields[field] = new_value
    if not self._cached_byte_size_dirty then
      message._member._Modified(self)
    end
  end
end
local _AddPropertiesForField = function(field, message_meta)
  constant_name = field.name:upper() .. "_FIELD_NUMBER"
  message_meta._member[constant_name] = field.number
  if field.label == FieldDescriptor.LABEL_REPEATED then
    _AddPropertiesForRepeatedField(field, message_meta)
  elseif field.cpp_type == FieldDescriptor.CPPTYPE_MESSAGE then
    _AddPropertiesForNonRepeatedCompositeField(field, message_meta)
  else
    _AddPropertiesForNonRepeatedScalarField(field, message_meta)
  end
end
local _ED_meta = {
  __index = function(self, extension_handle)
    local _extended_message = rawget(self, "_extended_message")
    local value = _extended_message._fields[extension_handle]
    if value ~= nil then
      return value
    end
    if extension_handle.label == FieldDescriptor.LABEL_REPEATED then
      value = extension_handle._default_constructor(self._extended_message)
    elseif extension_handle.cpp_type == FieldDescriptor.CPPTYPE_MESSAGE then
      value = extension_handle.message_type._concrete_class()
      value:_SetListener(_extended_message._listener_for_children)
    else
      return extension_handle.default_value
    end
    _extended_message._fields[extension_handle] = value
    return value
  end,
  __newindex = function(self, extension_handle, value)
    local _extended_message = rawget(self, "_extended_message")
    if extension_handle.label == FieldDescriptor.LABEL_REPEATED or extension_handle.cpp_type == FieldDescriptor.CPPTYPE_MESSAGE then
      error("Cannot assign to extension \"" .. extension_handle.full_name .. "\" because it is a repeated or composite type.")
    end
    local type_checker = GetTypeChecker(extension_handle.cpp_type, extension_handle.type)
    type_checker.CheckValue(value)
    _extended_message._fields[extension_handle] = value
    _extended_message._Modified()
  end
}
local _ExtensionDict = function(message)
  local o = {}
  o._extended_message = message
  return setmetatable(o, _ED_meta)
end
local _AddPropertiesForFields = function(descriptor, message_meta)
  for _, field in ipairs(descriptor.fields) do
    _AddPropertiesForField(field, message_meta)
  end
  if descriptor.is_extendable then
    function message_meta._getter:Extensions()
      return _ExtensionDict(self)
    end
  end
end
local _AddPropertiesForExtensions = function(descriptor, message_meta)
  local extension_dict = descriptor._extensions_by_name
  for extension_name, extension_field in pairs(extension_dict) do
    local constant_name = string.upper(extension_name) .. "_FIELD_NUMBER"
    message_meta._member[constant_name] = extension_field.number
  end
end
local _AddStaticMethods = function(message_meta)
  function message_meta._member.RegisterExtension(extension_handle)
    extension_handle.containing_type = message_meta._descriptor
    
    _AttachFieldHelpers(message_meta, extension_handle)
    if message_meta._extensions_by_number[extension_handle.number] == nil then
      message_meta._extensions_by_number[extension_handle.number] = extension_handle
    else
      error(string.format("Extensions \"%s\" and \"%s\" both try to extend message type \"%s\" with field number %d.", extension_handle.full_name, actual_handle.full_name, message_meta._descriptor.full_name, extension_handle.number))
    end
    message_meta._extensions_by_name[extension_handle.full_name] = extension_handle
  end
  
  function message_meta._member.FromString(s)
    local message = message_meta._member.__call()
    message.MergeFromString(s)
    return message
  end
end
local _IsPresent = function(descriptor, value)
  if descriptor.label == FieldDescriptor.LABEL_REPEATED then
    return value
  elseif descriptor.cpp_type == FieldDescriptor.CPPTYPE_MESSAGE then
    return value._is_present_in_parent
  else
    return true
  end
end

function sortFunc(a, b)
  return a.index < b.index
end

function pairsByKeys(t, f)
  local a = {}
  for n in pairs(t) do
    table.insert(a, n)
  end
  table.sort(a, f)
  local i = 0
  local iter = function()
    i = i + 1
    if a[i] == nil then
      return nil
    else
      return a[i], t[a[i]]
    end
  end
  return iter
end

local _AddListFieldsMethod = function(message_descriptor, message_meta)
  function message_meta._member:ListFields()
    local list_field = function(fields)
      local f, s, v = pairsByKeys(self._fields, sortFunc)
      
      local iter = function(a, i)
        while true do
          local descriptor, value = f(a, i)
          if descriptor == nil then
            return
          elseif _IsPresent(descriptor, value) then
            return descriptor, value
          end
        end
      end
      return iter, s, v
    end
    return list_field(self._fields)
  end
end
local _AddHasFieldMethod = function(message_descriptor, message_meta)
  local singular_fields = {}
  for _, field in ipairs(message_descriptor.fields) do
    if field.label ~= FieldDescriptor.LABEL_REPEATED then
      singular_fields[field.name] = field
    end
  end
  
  function message_meta._member:HasField(field_name)
    field = singular_fields[field_name]
    if field == nil then
      error("Protocol message has no singular \"" .. field_name .. "\" field.")
    end
    if field.cpp_type == FieldDescriptor.CPPTYPE_MESSAGE then
      value = self._fields[field]
      return value ~= nil and value._is_present_in_parent
    else
      local valueTmp = self._fields[field]
      return valueTmp ~= nil
    end
  end
end
local _AddClearFieldMethod = function(message_descriptor, message_meta)
  local singular_fields = {}
  for _, field in ipairs(message_descriptor.fields) do
    if field.label ~= FieldDescriptor.LABEL_REPEATED then
      singular_fields[field.name] = field
    end
  end
  
  function message_meta._member:ClearField(field_name)
    field = singular_fields[field_name]
    if field == nil then
      error("Protocol message has no singular \"" .. field_name .. "\" field.")
    end
    if self._fields[field] then
      self._fields[field] = nil
    end
    message_meta._member._Modified(self)
  end
end
local _AddClearExtensionMethod = function(message_meta)
  function message_meta._member:ClearExtension(extension_handle)
    if self._fields[extension_handle] == nil then
      self._fields[extension_handle] = nil
    end
    message_meta._member._Modified(self)
  end
end
local _AddClearMethod = function(message_descriptor, message_meta)
  function message_meta._member:Clear()
    self._fields = {}
    
    message_meta._member._Modified(self)
  end
end
local _AddStrMethod = function(message_meta)
  local format = text_format.msg_format
  
  function message_meta:__tostring()
    return format(self)
  end
end
local _AddHasExtensionMethod = function(message_meta)
  function message_meta._member:HasExtension(extension_handle)
    if extension_handle.label == FieldDescriptor.LABEL_REPEATED then
      error(extension_handle.full_name .. " is repeated.")
    end
    if extension_handle.cpp_type == FieldDescriptor.CPPTYPE_MESSAGE then
      value = self._fields[extension_handle]
      return value ~= nil and value._is_present_in_parent
    else
      return self._fields[extension_handle]
    end
  end
end
local _AddSetListenerMethod = function(message_meta)
  function message_meta._member:_SetListener(listener)
    if listener ~= nil then
      self._listener = listener_mod.NullMessageListener()
    else
      self._listener = listener
    end
  end
end
local _AddByteSizeMethod = function(message_descriptor, message_meta)
  function message_meta._member:ByteSize()
    if not self._cached_byte_size_dirty and self._cached_byte_size > 0 then
      return self._cached_byte_size
    end
    local size = 0
    for field_descriptor, field_value in message_meta._member.ListFields(self) do
      size = field_descriptor._sizer(field_value) + size
    end
    self._cached_byte_size = size
    self._cached_byte_size_dirty = false
    self._listener_for_children.dirty = false
    return size
  end
end
local _AddSerializeToStringMethod = function(message_descriptor, message_meta)
  function message_meta._member:SerializeToString()
    if not message_meta._member.IsInitialized(self) then
      error("Message is missing required fields: " .. table.concat(message_meta._member.FindInitializationErrors(self), ","))
    end
    return message_meta._member.SerializePartialToString(self)
  end
  
  function message_meta._member:SerializeToIOString(iostring)
    if not message_meta._member.IsInitialized(self) then
      error("Message is missing required fields: " .. table.concat(message_meta._member.FindInitializationErrors(self), ","))
    end
    return message_meta._member.SerializePartialToIOString(self, iostring)
  end
end
local _AddSerializePartialToStringMethod = function(message_descriptor, message_meta)
  local concat = table.concat
  local _internal_serialize = function(self, write_bytes)
    for field_descriptor, field_value in message_meta._member.ListFields(self) do
      field_descriptor._encoder(write_bytes, field_value)
    end
  end
  local _serialize_partial_to_iostring = function(self, iostring)
    local w = iostring.write
    local write = function(value)
      w(iostring, value)
    end
    _internal_serialize(self, write)
    return
  end
  local _serialize_partial_to_string = function(self)
    local out = {}
    local write = function(value)
      out[#out + 1] = value
    end
    _internal_serialize(self, write)
    return concat(out)
  end
  message_meta._member._InternalSerialize = _internal_serialize
  message_meta._member.SerializePartialToIOString = _serialize_partial_to_iostring
  message_meta._member.SerializePartialToString = _serialize_partial_to_string
end
local _AddMergeFromStringMethod = function(message_descriptor, message_meta)
  local ReadTag = decoder.ReadTag
  local SkipField = decoder.SkipField
  local decoders_by_tag = message_meta._decoders_by_tag
  local _internal_parse = function(self, buffer, pos, pend)
    message_meta._member._Modified(self)
    local field_dict = self._fields
    local tag_bytes, new_pos, field_decoder
    while pos ~= pend do
      tag_bytes, new_pos = ReadTag(buffer, pos)
      field_decoder = decoders_by_tag[tag_bytes]
      if field_decoder == nil then
        new_pos = SkipField(buffer, new_pos, pend, tag_bytes)
        if new_pos == -1 then
          return pos
        end
        pos = new_pos
      else
        pos = field_decoder(buffer, new_pos, pend, self, field_dict)
      end
    end
    return pos
  end
  message_meta._member._InternalParse = _internal_parse
  local merge_from_string = function(self, serialized)
    local length = #serialized
    if _internal_parse(self, serialized, 0, length) ~= length then
      error("Unexpected end-group tag.")
    end
    return length
  end
  message_meta._member.MergeFromString = merge_from_string
  
  function message_meta._member:ParseFromString(serialized)
    message_meta._member.Clear(self)
    merge_from_string(self, serialized)
  end
end
local _AddIsInitializedMethod = function(message_descriptor, message_meta)
  local required_fields = {}
  for _, field in ipairs(message_descriptor.fields) do
    if field.label == FieldDescriptor.LABEL_REQUIRED then
      required_fields[#required_fields + 1] = field
    end
  end
  
  function message_meta._member:IsInitialized(errors)
    for _, field in ipairs(required_fields) do
      if self._fields[field] == nil or field.cpp_type == FieldDescriptor.CPPTYPE_MESSAGE and not self._fields[field]._is_present_in_parent then
        if errors ~= nil then
          errors[#errors + 1] = message_meta._member.FindInitializationErrors(self)
        end
        return false
      end
    end
    for field, value in pairs(self._fields) do
      if field.cpp_type == FieldDescriptor.CPPTYPE_MESSAGE then
        if field.label == FieldDescriptor.LABEL_REPEATED then
          for _, element in ipairs(value) do
            if not element:IsInitialized() then
              if errors ~= nil then
                errors[#errors + 1] = message_meta._member.FindInitializationErrors(self)
              end
              return false
            end
          end
        elseif value._is_present_in_parent and not value:IsInitialized() then
          if errors ~= nil then
            errors[#errors + 1] = message_meta._member.FindInitializationErrors(self)
          end
          return false
        end
      end
    end
    return true
  end
  
  function message_meta._member:FindInitializationErrors()
    local errors = {}
    for _, field in ipairs(required_fields) do
      if not message_meta._member.HasField(self, field.name) then
        errors[#errors + 1] = field.name
      end
    end
    for field, value in message_meta._member.ListFields(self) do
      if field.cpp_type == FieldDescriptor.CPPTYPE_MESSAGE then
        if field.is_extension then
          name = string.format("(%s)", field.full_name)
        else
          name = field.name
        end
        if field.label == FieldDescriptor.LABEL_REPEATED then
          for i, element in ipairs(value) do
            prefix = string.format("%s[%d].", name, i)
            sub_errors = element:FindInitializationErrors()
            for _, e in ipairs(sub_errors) do
              errors[#errors + 1] = prefix .. e
            end
          end
        else
          prefix = name .. "."
          sub_errors = value:FindInitializationErrors()
          for _, e in ipairs(sub_errors) do
            errors[#errors + 1] = prefix .. e
          end
        end
      end
    end
    return errors
  end
end
local _AddMergeFromMethod = function(message_meta)
  local LABEL_REPEATED = FieldDescriptor.LABEL_REPEATED
  local CPPTYPE_MESSAGE = FieldDescriptor.CPPTYPE_MESSAGE
  
  function message_meta._member:MergeFrom(msg)
    assert(msg ~= self)
    message_meta._member._Modified(self)
    local fields = self._fields
    for field, value in pairs(msg._fields) do
      if field.label == LABEL_REPEATED or field.cpp_type == CPPTYPE_MESSAGE then
        field_value = fields[field]
        if field_value == nil then
          field_value = field._default_constructor(self)
          fields[field] = field_value
        end
        field_value:MergeFrom(value)
      else
        self._fields[field] = value
      end
    end
  end
end
local _AddMessageMethods = function(message_descriptor, message_meta)
  _AddListFieldsMethod(message_descriptor, message_meta)
  _AddHasFieldMethod(message_descriptor, message_meta)
  _AddClearFieldMethod(message_descriptor, message_meta)
  if message_descriptor.is_extendable then
    _AddClearExtensionMethod(message_meta)
    _AddHasExtensionMethod(message_meta)
  end
  _AddClearMethod(message_descriptor, message_meta)
  _AddStrMethod(message_meta)
  _AddSetListenerMethod(message_meta)
  _AddByteSizeMethod(message_descriptor, message_meta)
  _AddSerializeToStringMethod(message_descriptor, message_meta)
  _AddSerializePartialToStringMethod(message_descriptor, message_meta)
  _AddMergeFromStringMethod(message_descriptor, message_meta)
  _AddIsInitializedMethod(message_descriptor, message_meta)
  _AddMergeFromMethod(message_meta)
end
local _AddPrivateHelperMethods = function(message_meta)
  local Modified = function(self)
    if not self._cached_byte_size_dirty then
      self._cached_byte_size_dirty = true
      self._listener_for_children.dirty = true
      self._is_present_in_parent = true
      self._listener:Modified()
    end
  end
  message_meta._member._Modified = Modified
  message_meta._member.SetInParent = Modified
end
local property_getter = function(message_meta)
  local getter = message_meta._getter
  local member = message_meta._member
  return function(self, property)
    local g = getter[property]
    if g then
      return g(self)
    else
      return member[property]
    end
  end
end
local property_setter = function(message_meta)
  local setter = message_meta._setter
  return function(self, property, value)
    local s = setter[property]
    if s then
      s(self, value)
    else
      error(property .. " not found")
    end
  end
end

function _AddClassAttributesForNestedExtensions(descriptor, message_meta)
  local extension_dict = descriptor._extensions_by_name
  for extension_name, extension_field in pairs(extension_dict) do
    message_meta._member[extension_name] = extension_field
  end
end

function Message(descriptor)
  local message_meta = {}
  message_meta._decoders_by_tag = {}
  rawset(descriptor, "_extensions_by_name", {})
  for _, k in ipairs(descriptor.extensions) do
    descriptor._extensions_by_name[k.name] = k
  end
  rawset(descriptor, "_extensions_by_number", {})
  for _, k in ipairs(descriptor.extensions) do
    descriptor._extensions_by_number[k.number] = k
  end
  message_meta._descriptor = descriptor
  message_meta._extensions_by_name = {}
  message_meta._extensions_by_number = {}
  message_meta._getter = {}
  message_meta._setter = {}
  message_meta._member = {}
  local ns = setmetatable({}, message_meta._member)
  message_meta._member.__call = _InitMethod(message_meta)
  message_meta._member.__index = message_meta._member
  message_meta._member.type = ns
  if rawget(descriptor, "_concrete_class") == nil then
    rawset(descriptor, "_concrete_class", ns)
    for k, field in ipairs(descriptor.fields) do
      _AttachFieldHelpers(message_meta, field)
    end
  end
  _AddEnumValues(descriptor, message_meta)
  _AddClassAttributesForNestedExtensions(descriptor, message_meta)
  _AddPropertiesForFields(descriptor, message_meta)
  _AddPropertiesForExtensions(descriptor, message_meta)
  _AddStaticMethods(message_meta)
  _AddMessageMethods(descriptor, message_meta)
  _AddPrivateHelperMethods(message_meta)
  message_meta.__index = property_getter(message_meta)
  message_meta.__newindex = property_setter(message_meta)
  return ns
end

return protobuf
