"""

"""
from __future__ import unicode_literals
import six

from django.views import generic

from .exceptions import RulesLightException
from .registry import registry


__all__ = ('class_decorator',)


def patch_get_object(cls, suffix, override):
    old_get_object = cls.get_object

    def new_get_object(self, *args, **kwargs):
        obj = old_get_object(self, *args, **kwargs)

        if self.get_object._rule_override:
            rule_name = self.get_object._rule_override
        else:
            rule_name = '%s.%s.%s' % (obj.__class__._meta.app_label,
                obj.__class__._meta.module_name, self.get_object._rule_suffix)

        registry.require(self.request.user, rule_name, obj)

        return obj

    new_get_object._rule_suffix = suffix
    new_get_object._rule_override = override
    cls.get_object = new_get_object


class class_decorator(object):
    """
    Can be used to secure class based views.

    If the view has ``model=YourModel``, it will support:

    - ``CreateView``, it will decorate ``get_form()``, to run
      ``rules_light.require('yourapp.yourmodel.create')``,
    - ``UpdateView``, it will decorate ``get_object()``, to run
      ``rules_light.require('yourapp.yourmodel.update', obj)``,
    - ``DeleteView``, it will decorate ``get_object()``, to run
      ``rules_light.require('yourapp.yourmodel.delete', obj)``,
    - ``DetailView``, it will decorate ``get_object()``, to run
      ``rules_light.require('yourapp.yourmodel.read', obj)``,
    - others views, if the rule name is specified in the decorator for example
      ``@class_decorator('some_rule')``, then it will decorate ``dispatch()``,
    - Else it raises an exception.
    """
    rule = None

    def __new__(self, *args):
        if hasattr(args[0], 'as_view'):
            cls = args[0]
        elif isinstance(args[0], six.string_types):
            if six.PY2:
                decorator_name = b'new_class_decorator'
            elif six.PY3:
                decorator_name = 'new_class_decorator'

            new_class_decorator = type(decorator_name,
                (class_decorator,), {'rule': args[0]})
            return new_class_decorator
        elif hasattr(args[0], '__call__'):
            raise Exception("No function support")
        else:
            raise Exception("What?")

        if issubclass(cls, generic.CreateView):
            old_get_form = cls.get_form

            def new_get_form(self, form_class, *args, **kwargs):
                model = form_class.Meta.model
                rule_name = '%s.%s.create' % (model._meta.app_label,
                    model._meta.module_name)

                registry.require(self.request.user, rule_name)

                return old_get_form(self, form_class, *args, **kwargs)

            cls.get_form = new_get_form

        elif issubclass(cls, generic.UpdateView):
            patch_get_object(cls, 'update', self.rule)

        elif issubclass(cls, generic.DetailView):
            patch_get_object(cls, 'read', self.rule)

        elif issubclass(cls, generic.DeleteView):
            patch_get_object(cls, 'delete', self.rule)

        elif self.rule:
            old_dispatch = cls.dispatch

            def new_dispatch(self, request, *args, **kwargs):
                registry.require(request.user, self.dispatch._rule)
                return old_dispatch(self, request, *args, **kwargs)
            new_dispatch._rule = self.rule
            cls.dispatch = new_dispatch

        else:
            raise RulesLightException('Dont understand what to do')

        return cls
