#!/usr/bin/env python2
# -*- coding: utf-8 -*-

"""Implements a case-insensitive (on keys) dictionary and
order-sensitive dictionary"""

# Copyright 2002, 2003 St James Software
#
# This file is part of translate.
#
# translate is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# translate is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.


def generalupper(str):
    """this uses the object's upper method - works with string and unicode"""
    if str is None:
        return str
    return str.upper()


class cidict(dict):

    def __init__(self, fromdict=None):
        """constructs the cidict, optionally using another dict to do so"""
        if fromdict is not None:
            self.update(fromdict)

    def __getitem__(self, key):
        if type(key) != str and type(key) != unicode:
            raise TypeError("cidict can only have str or unicode as key (got %r)" %
                            type(key))
        for akey in self.iterkeys():
            if akey.lower() == key.lower():
                return dict.__getitem__(self, akey)
        raise IndexError

    def __setitem__(self, key, value):
        if type(key) != str and type(key) != unicode:
            raise TypeError("cidict can only have str or unicode as key (got %r)" %
                            type(key))
        for akey in self.iterkeys():
            if akey.lower() == key.lower():
                return dict.__setitem__(self, akey, value)
        return dict.__setitem__(self, key, value)

    def update(self, updatedict):
        """D.update(E) -> None.
           Update D from E: for k in E.keys(): D[k] = E[k]"""
        for key, value in updatedict.iteritems():
            self[key] = value

    def __delitem__(self, key):
        if type(key) != str and type(key) != unicode:
            raise TypeError("cidict can only have str or unicode as key (got %r)" %
                            type(key))
        for akey in self.iterkeys():
            if akey.lower() == key.lower():
                return dict.__delitem__(self, akey)
        raise IndexError

    def __contains__(self, key):
        if type(key) != str and type(key) != unicode:
            raise TypeError("cidict can only have str or unicode as key (got %r)" %
                            type(key))
        for akey in self.iterkeys():
            if akey.lower() == key.lower():
                return 1
        return 0

    def has_key(self, key):
        return self.__contains__(key)

    def get(self, key, default=None):
        if key in self:
            return self[key]
        else:
            return default


class ordereddict(dict):
    """a dictionary which remembers its keys in the order in which they
    were given"""

    def __init__(self, *args):
        if len(args) == 0:
            super(ordereddict, self).__init__()
            self.order = []
        elif len(args) > 1:
            raise TypeError("ordereddict() takes at most 1 argument (%d given)" %
                            len(args))
        else:
            initarg = args[0]
            apply(super(ordereddict, self).__init__, args)
            if hasattr(initarg, "keys"):
                self.order = initarg.keys()
            else:
                # danger: could have duplicate keys...
                self.order = []
                checkduplicates = {}
                for key, value in initarg:
                    if not key in checkduplicates:
                        self.order.append(key)
                        checkduplicates[key] = None

    def __setitem__(self, key, value):
        alreadypresent = key in self
        result = dict.__setitem__(self, key, value)
        if not alreadypresent:
            self.order.append(key)
        return result

    def update(self, updatedict):
        """D.update(E) -> None.
        Update D from E: for k in E.keys(): D[k] = E[k]"""
        for key, value in updatedict.iteritems():
            self[key] = value

    def __delitem__(self, key):
        alreadypresent = key in self
        result = dict.__delitem__(self, key)
        if alreadypresent:
            del self.order[self.order.index(key)]
        return result

    def copy(self):
        """D.copy() -> a shallow copy of D"""
        thecopy = ordereddict(super(ordereddict, self).copy())
        thecopy.order = self.order[:]
        return thecopy

    def items(self):
        """D.items() -> list of D's (key, value) pairs, as 2-tuples"""
        return [(key, self[key]) for key in self.order]

    def iteritems(self):
        """D.iteritems() -> an iterator over the (key, value) items of D"""
        for key in self.order:
            yield (key, self[key])

    def iterkeys(self):
        """D.iterkeys() -> an iterator over the keys of D"""
        for key in self.order:
            yield key

    __iter__ = iterkeys

    def itervalues(self):
        """D.itervalues() -> an iterator over the values of D"""
        for key in self.order:
            yield self[key]

    def keys(self):
        """D.keys() -> list of D's keys"""
        return self.order[:]

    def popitem(self):
        """D.popitem() -> (k, v), remove and return some (key, value) pair
        as a 2-tuple; but raise KeyError if D is empty"""
        if len(self.order) == 0:
            raise KeyError("popitem(): ordered dictionary is empty")
        k = self.order.pop()
        v = self[k]
        del self[k]
        return (k, v)

    def pop(self, key):
        """remove entry from dict and internal list"""
        value = super(ordereddict, self).pop(key)
        del self.order[self.order.index(key)]
        return value
