require 'json'
require 'protocol_buffers'
require 'protocol_buffers/compiler'

class ProtocolBuffers::Message

  def to_json(*args)

    json = to_json_ready_object()

    json.to_json(*args)
  end

  def to_json_ready_object()
    hash = {}

    # simpler version, includes all fields in the output, using the default
    # values if unset. also includes empty repeated fields as empty arrays.
    # fields.each do |tag, field|
    #   hash[field.name] = value_for_tag(field.tag)
    # end

    # prettier output, only includes non-empty repeated fields and set fields
    fields.each do |tag, field|
      if field.repeated?
        value = value_for_tag(field.tag)
        hash[field.name] = value unless value.empty?
      else
        if value_for_tag?(field.tag)
            value = value_for_tag(field.tag)
          if field.instance_of? ProtocolBuffers::Field::EnumField # if value is enum, resolve string value as ruby const
              hash[field.name] = field.value_to_name[value]
          else 
            proxy_class = field.instance_variable_get(:@proxy_class)
            if proxy_class.nil?
                hash[field.name] = value
            else
                hash[field.name] = value.to_json_ready_object
            end
          end
        end
      end
    end

    return hash
  end

  def self.json_create(hash)
    # initialize takes a hash of { attribute_name => value } so you can just
    # pass the hash into the constructor. but we're supposed to be showing off
    # reflection, here. plus, that raises an exception if there is an unknown
    # key in the hash.
    # new(hash)

    message = new
    fields.each do |tag, field|
      if value = hash[field.name.to_s]
        if value.instance_of? Hash # if the value is a Hash, descend down into PB hierachy
            inner_class = field.instance_variable_get(:@proxy_class)
            value = inner_class.json_create(value)
            message.set_value_for_tag(field.tag, value)
        elsif field.instance_of? ProtocolBuffers::Field::EnumField # if value is enum, resolve string value as ruby const
            message.set_value_for_tag(field.tag, field.instance_variable_get(:@proxy_enum).const_get(value))
        else
            message.set_value_for_tag(field.tag, value)
        end
      end
    end
    message
  end
end
