import six
import warnings


def _convert_dict(to_convert,
                  conversions,
                  defaults=None,
                  key_warnings=False,
                  retain_keys=False):
  """Applies a set of conversion rules to a dict.

  Args:
    to_convert: A dictionary of key/value pairs to convert.
    conversions: A dictionary giving the mapping from key names in "to_convert"
      to how those keys and their values will be handled. Key/value pairs in
      "to_convert" will be modified in a way that depends on how the key
      appears in "conversions". If "to_convert" contains a key/value mapping
      of "k"->"v", then:
      - If "conversions" contains "k"->"X" then the result will contain
        "X"->"v".
      - If "conversions" contains "k"->None then the result will not contain an
        entry for "k".
      - If "conversions" contains "k"->("X", f) then the result will contain
        "X"->f("v")
      - If "conversions" does not contain an entry for "k" then the result
        will not contain an entry for "k" unless retain_keys is true;
        if key_warnings is True then a warning will be printed.
      - If two or more distinct input keys are converted to the same output key,
        one of the resulting values will appear in the result, the others
        will be dropped, and a warning will be printed.
    defaults: Values to insert in the result if the result of conversion does
      not contain these keys.
    key_warnings: Whether to print warnings for input keys that are not mapped
      to anything in the output.
    retain_keys: Whether or not to retain the state of dict.  If false, any keys
      that don't show up in the conversions dict will be dropped from result.

  Returns:
    The "to_convert" dict with keys renamed, values converted, and defaults
    added.
  """
  result = {}
  for key, value in six.iteritems(to_convert):
    if key in conversions:
      conversion = conversions[key]
      if conversion is not None:
        if isinstance(conversion, tuple):
          key = conversion[0]
          value = conversion[1](value)
        else:
          key = conversion
        if key in result:
          warnings.warn(
              'Multiple request parameters converted to {}'.format(key))
        result[key] = value
    elif retain_keys:
      result[key] = value
    elif key_warnings:
      warnings.warn('Unrecognized key {} ignored'.format(key))
  if defaults:
    for default_key, default_value in six.iteritems(defaults):
      if default_key not in result:
        result[default_key] = default_value
  return result