# -*- coding: utf-8 -*-

"""
    Bimap - bidirectional mapping between code/value
"""


class BimapError(Exception):
    pass


class Bimap(object):
    """
        Bi-directional mapping between code/text.

        Initialised using:

            name:   Used for exceptions
            dict:   Dict mapping from code (numeric) to text
            error:  Error type to raise if key not found

        The class provides:

            * A 'forward' map (code->text) which is accessed through
              __getitem__ (bimap[code])
            * A 'reverse' map (code>value) which is accessed through
              __getattr__ (bimap.text)
            * A 'get' method which does a forward lookup (code->text)
              and returns a textual version of code if there is no
              explicit mapping (or default provided)

        >>> class TestError(Exception):
        ...     pass

        >>> TEST = Bimap('TEST',{1:'A', 2:'B', 3:'C'},TestError)
        >>> TEST[1]
        'A'
        >>> TEST.A
        1
        >>> TEST.X
        Traceback (most recent call last):
        ...
        TestError: TEST: Invalid reverse lookup: [X]
        >>> TEST[99]
        Traceback (most recent call last):
        ...
        TestError: TEST: Invalid forward lookup: [99]
        >>> TEST.get(99)
        '99'

    """

    def __init__(self, name, forward, error=AttributeError):
        self.name = name
        self.error = error
        self.forward = forward.copy()
        self.reverse = dict([(v, k) for (k, v) in list(forward.items())])

    def get(self, k, default=None):
        try:
            return self.forward[k]
        except KeyError:
            return default or str(k)

    def __getitem__(self, k):
        try:
            return self.forward[k]
        except KeyError:
            raise self.error("%s: Invalid forward lookup: [%s]" % (self.name, k))

    def __getattr__(self, k):
        try:
            # Python 3.7 inspect module (called by doctest) checks for __wrapped__ attribute
            if k == "__wrapped__":
                raise AttributeError()
            return self.reverse[k]
        except KeyError:
            raise self.error("%s: Invalid reverse lookup: [%s]" % (self.name, k))
