"""
A caseless dictionary implementation.
"""

from collections import UserDict
from collections.abc import Mapping


class CaselessDictionary(UserDict):
    """
    A dictionary-like object which ignores but preserves the case of strings.

    Example::

        >>> cdict = CaselessDictionary()

    Access is case-insensitive::

        >>> cdict['a'] = 1
        >>> cdict['A']
        1

    As is writing::

        >>> cdict['key'] = 123
        >>> cdict['KeY'] = 456
        >>> cdict['key']
        456

    And deletion::

        >>> del cdict['A']
        >>> 'a' in cdict
        False
        >>> 'A' in cdict
        False

    However, the case of keys is preserved (the case of overridden keys will be
    the first one which was set)::

        >>> cdict['aBcDeF'] = 1
        >>> sorted(list(cdict))
        ['aBcDeF', 'key']
    """

    def __init__(self, *args, **kwargs):
        super().__init__()
        temp_dict = dict(*args, **kwargs)
        for key, value in temp_dict.items():
            self[key] = value  # go through __setitem__

    def __getitem__(self, key):
        return self.data[CaselessString(key)]

    def __setitem__(self, key, value):
        if not isinstance(key, CaselessString):
            key = CaselessString(key)
        if isinstance(value, Mapping) and not isinstance(value, CaselessDictionary):
            value = CaselessDictionary(value)
        self.data[key] = value

    def __delitem__(self, key):
        del self.data[CaselessString(key)]

    def __contains__(self, key):
        return CaselessString(key) in self.data

    def keys(self):
        # convert back to ordinary strings
        return [str(k) for k in self.data]

    def items(self):
        # convert back to ordinary strings for keys
        return [(str(k), v) for k, v in self.data.items()]

    def setdefault(self, key, default=None):
        """
        Insert key with a value of default if key is not in the dictionary.

        Return the value for key
        """
        if key not in self:
            self[key] = default
        # Make sure to return self[key] and not default,
        # since the former will be converted to a CaselessDictionary in case of Mapping
        return self[key]


class CaselessString(str):
    """
    A case-insensitive string subclass for dict lookups.
    """

    def __hash__(self):
        return hash(self.lower())

    def __eq__(self, other):
        return self.lower() == other.lower()

    def __cmp__(self, other):
        return self.lower().__cmp__(other.lower())
