from six.moves import collections_abc


# Borrowed from https://github.com/psf/requests
class CaseInsensitiveDict(collections_abc.MutableMapping):
    """
    A case-insensitive `dict`-like object.

    Implements all methods and operations of `MutableMapping` as well as dict's
    `copy`. Also provides `lower_items`.

    All keys are expected to be strings. The structure remembers the case of the
    last key to be set, and `iter(instance)`, `keys()`, `items()`,
    `iterkeys()`, and `iteritems()` will contain case-sensitive keys. However,
    querying and contains testing is case insensitive:

        cid = CaseInsensitiveDict()
        cid['Accept'] = 'application/json'
        cid['aCCEPT'] == 'application/json'  # True
        list(cid) == ['Accept']  # True

    For example, `headers['content-encoding']` will return the value of a
    `'Content-Encoding'` response header, regardless of how the header name
    was originally stored.

    If the constructor, `.update`, or equality comparison operations are given
    keys that have equal `.lower()`s, the behavior is undefined.
    """

    def __init__(self, data=None, **kwargs):
        self._store = dict()
        if data is None:
            data = {}
        self.update(data, **kwargs)

    def __setitem__(self, key, value):
        # Use the lowercased key for lookups, but store the actual
        # key alongside the value.
        self._store[key.lower()] = (key, value)

    def __getitem__(self, key):
        return self._store[key.lower()][1]

    def __delitem__(self, key):
        del self._store[key.lower()]

    def __iter__(self):
        return (casedkey for casedkey, mappedvalue in self._store.values())

    def __len__(self):
        return len(self._store)

    def lower_items(self):
        """Like items(), but with all lowercase keys."""
        return ((lowerkey, keyval[1]) for (lowerkey, keyval) in self._store.items())

    def __eq__(self, other):
        if isinstance(other, collections_abc.Mapping):
            other = CaseInsensitiveDict(other)
        else:  # pragma: no cover
            return NotImplemented
        # Compare insensitively
        return dict(self.lower_items()) == dict(other.lower_items())

    def copy(self):
        return CaseInsensitiveDict(self._store.values())

    def __repr__(self):
        return str(dict(self.items()))
