"""
Monkey patched json_format to allow best effort decoding of Any fields.
Use the additional flag (strict_any_handling=False) to trigger the
best-effort behavior. Omit the flag, or just use the original json_format
module fot the strict behavior.
"""

from google.protobuf import json_format

class _PatchedPrinter(json_format._Printer):

    def __init__(self, including_default_value_fields=False,
                 preserving_proto_field_name=False,
                 strict_any_handling=False):
        super(_PatchedPrinter, self).__init__(including_default_value_fields,
                                              preserving_proto_field_name)
        self.strict_any_handling = strict_any_handling

    def _BestEffortAnyMessageToJsonObject(self, msg):
        try:
            res = self._AnyMessageToJsonObject(msg)
        except TypeError:
            res = self._RegularMessageToJsonObject(msg, {})
        return res


def MessageToDict(message,
                  including_default_value_fields=False,
                  preserving_proto_field_name=False,
                  strict_any_handling=False):
    """Converts protobuf message to a JSON dictionary.

    Args:
      message: The protocol buffers message instance to serialize.
      including_default_value_fields: If True, singular primitive fields,
          repeated fields, and map fields will always be serialized.  If
          False, only serialize non-empty fields.  Singular message fields
          and oneof fields are not affected by this option.
      preserving_proto_field_name: If True, use the original proto field
          names as defined in the .proto file. If False, convert the field
          names to lowerCamelCase.
      strict_any_handling: If True, converion will error out (like in the
          original method) if an Any field with value for which the Any type
          is not loaded is encountered. If False, the conversion will leave
          the field un-packed, but otherwise will continue.

    Returns:
      A dict representation of the JSON formatted protocol buffer message.
    """
    printer = _PatchedPrinter(including_default_value_fields,
                              preserving_proto_field_name,
                              strict_any_handling=strict_any_handling)
    # pylint: disable=protected-access
    return printer._MessageToJsonObject(message)


def MessageToJson(message,
                  including_default_value_fields=False,
                  preserving_proto_field_name=False,
                  strict_any_handling=False):
  """Converts protobuf message to JSON format.

  Args:
    message: The protocol buffers message instance to serialize.
    including_default_value_fields: If True, singular primitive fields,
        repeated fields, and map fields will always be serialized.  If
        False, only serialize non-empty fields.  Singular message fields
        and oneof fields are not affected by this option.
    preserving_proto_field_name: If True, use the original proto field
        names as defined in the .proto file. If False, convert the field
        names to lowerCamelCase.
    strict_any_handling: If True, converion will error out (like in the
        original method) if an Any field with value for which the Any type
        is not loaded is encountered. If False, the conversion will leave
        the field un-packed, but otherwise will continue.

  Returns:
    A string containing the JSON formatted protocol buffer message.
  """
  printer = _PatchedPrinter(including_default_value_fields,
                            preserving_proto_field_name,
                            strict_any_handling=strict_any_handling)
  return printer.ToJsonString(message)


json_format._WKTJSONMETHODS['google.protobuf.Any'] = [
    '_BestEffortAnyMessageToJsonObject',
    '_ConvertAnyMessage'
]

json_format._Printer._BestEffortAnyMessageToJsonObject = \
    json_format._Printer._AnyMessageToJsonObject
