# The SmartDict class fixes a couple of issues with using the content
# of os.environ or similar dicts of strings as Python variables:
#
# 1) Undefined variables should return False rather than raising KeyError.
#
# 2) String values of 'False', '0', etc., should evaluate to False
#    (not just the empty string).
#
# #1 is solved by overriding __getitem__, and #2 is solved by using a
# proxy class for values and overriding __nonzero__ on the proxy.
# Everything else is just to (a) make proxies behave like normal
# values otherwise, (b) make sure any dict operation returns a proxy
# rather than a normal value, and (c) coerce values written to the
# dict to be strings.


from convert import *

class SmartDict(dict):

    class Proxy(str):
        def __int__(self):
            return int(to_integer(str(self)))
        def __long__(self):
            return long(to_integer(str(self)))
        def __float__(self):
            return float(to_integer(str(self)))
        def __nonzero__(self):
            return to_bool(str(self))
        def convert(self, other):
            t = type(other)
            if t == bool:
                return bool(self)
            if t == int:
                return int(self)
            if t == long:
                return long(self)
            if t == float:
                return float(self)
            return str(self)
        def __lt__(self, other):
            return self.convert(other) < other
        def __le__(self, other):
            return self.convert(other) <= other
        def __eq__(self, other):
            return self.convert(other) == other
        def __ne__(self, other):
            return self.convert(other) != other
        def __gt__(self, other):
            return self.convert(other) > other
        def __ge__(self, other):
            return self.convert(other) >= other

        def __add__(self, other):
            return self.convert(other) + other
        def __sub__(self, other):
            return self.convert(other) - other
        def __mul__(self, other):
            return self.convert(other) * other
        def __div__(self, other):
            return self.convert(other) / other
        def __truediv__(self, other):
            return self.convert(other) / other

        def __radd__(self, other):
            return other + self.convert(other)
        def __rsub__(self, other):
            return other - self.convert(other)
        def __rmul__(self, other):
            return other * self.convert(other)
        def __rdiv__(self, other):
            return other / self.convert(other)
        def __rtruediv__(self, other):
            return other / self.convert(other)


    # __getitem__ uses dict.get() to return 'False' if the key is not
    # found (rather than raising KeyError).  Note that this does *not*
    # set the key's value to 'False' in the dict, so that even after
    # we call env['foo'] we still get a meaningful answer from "'foo'
    # in env" (which calls dict.__contains__, which we do not
    # override).
    def __getitem__(self, key):
        return self.Proxy(dict.get(self, key, 'False'))

    def __setitem__(self, key, item):
        dict.__setitem__(self, key, str(item))

    def values(self):
        return [ self.Proxy(v) for v in dict.values(self) ]

    def itervalues(self):
        for value in dict.itervalues(self):
            yield self.Proxy(value)

    def items(self):
        return [ (k, self.Proxy(v)) for k,v in dict.items(self) ]

    def iteritems(self):
        for key,value in dict.iteritems(self):
            yield key, self.Proxy(value)

    def get(self, key, default='False'):
        return self.Proxy(dict.get(self, key, str(default)))

    def setdefault(self, key, default='False'):
        return self.Proxy(dict.setdefault(self, key, str(default)))