import copy


def merge(*args, **kwargs):
  """Merge together an iterable of configs in order.

  The first instance of a key determines its type. The last instance of a key
  determines its value.

  Example:

  args[0] = {
    'layers': 1,
    'dropout': 0.5
  }
  kwargs = {
    dropout': 0
  }

  Final result:
  {
    'layers': 1,
    'dropout': 0.0  # note the type change
  }

  Args:
    *args: List of dict-like objects to merge in order.
    **kwargs: Any additional values to add. Treated like as a final additional
      dict to merge.

  Returns:
    dict resulting from merging all configs together.

  Raises:
    TypeError: if there is a type mismatch between the same key across dicts.
    ValueError: If _merge_validate is specified and there is a type mismatch.
  """
  assert args
  validate = False
  if '_merge_validate' in kwargs:
    validate = kwargs['_merge_validate']
    del kwargs['_merge_validate']
  config = copy.deepcopy(args[0])
  configs = list(args)
  configs.append(kwargs)
  for c in configs[1:]:
    for k, v in c.iteritems():
      if isinstance(v, dict):
        v = copy.deepcopy(v)
      if k in config:
        value_type = type(config[k])

        if config[k] is not None and v is not None and not isinstance(
            v, value_type):
          v = value_type(v)

        if isinstance(v, dict):
          config[k] = merge(config[k], v, _merge_validate=validate)
        else:
          config[k] = v
      else:
        if validate:
          raise ValueError('Target did not contain key %s' % k)
        config[k] = v

  return copy.deepcopy(config)