import functools
import warnings


class AVDeprecationWarning(DeprecationWarning):
    pass


class AttributeRenamedWarning(AVDeprecationWarning):
    pass


class MethodDeprecationWarning(AVDeprecationWarning):
    pass


# DeprecationWarning is not printed by default (unless in __main__). We
# really want these to be seen, but also to use the "correct" base classes.
# So we're putting a filter in place to show our warnings. The users can
# turn them back off if they want.
warnings.filterwarnings("default", "", AVDeprecationWarning)


class renamed_attr:
    """Proxy for renamed attributes (or methods) on classes.
    Getting and setting values will be redirected to the provided name,
    and warnings will be issues every time.

    """

    def __init__(self, new_name):
        self.new_name = new_name
        self._old_name = None

    def old_name(self, cls):
        if self._old_name is None:
            for k, v in vars(cls).items():
                if v is self:
                    self._old_name = k
                    break
        return self._old_name

    def __get__(self, instance, cls):
        old_name = self.old_name(cls)
        warnings.warn(
            "{0}.{1} is deprecated; please use {0}.{2}.".format(
                cls.__name__,
                old_name,
                self.new_name,
            ),
            AttributeRenamedWarning,
            stacklevel=2,
        )
        return getattr(instance if instance is not None else cls, self.new_name)

    def __set__(self, instance, value):
        old_name = self.old_name(instance.__class__)
        warnings.warn(
            "{0}.{1} is deprecated; please use {0}.{2}.".format(
                instance.__class__.__name__,
                old_name,
                self.new_name,
            ),
            AttributeRenamedWarning,
            stacklevel=2,
        )
        setattr(instance, self.new_name, value)


class method:
    def __init__(self, func):
        functools.update_wrapper(self, func, ("__name__", "__doc__"))
        self.func = func

    def __get__(self, instance, cls):
        warning = MethodDeprecationWarning(
            f"{cls.__name__}.{self.func.__name__} is deprecated."
        )
        warnings.warn(warning, stacklevel=2)
        return self.func.__get__(instance, cls)
