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

from django.core.urlresolvers import reverse
from django.dispatch import Signal
from django.template import RequestContext
from django.template.loader import render_to_string

from .dispatch import SignalConnectMetaClass


class Widget(object):
    """
    Base class for a widget for the dashboard.

    Every app which wants to add widgets to the dashboard has to create a
    widget class subclassing from this base class. The name attribute has to
    be set. The __metaclass__ attribute (SignalConnectMetaClass) does the
    rest of the magic.

    For the appearance of the widget there are some optional attributes like
    verbose_name, permission_required, default_column, default_weight,
    default_active, template_name, context, icon_css_class,
    more_link_pattern_name, stylesheets and javascript_files.
    """
    __metaclass__ = SignalConnectMetaClass
    signal = Signal(providing_args=['request'])
    name = None
    verbose_name = None
    permission_required = None
    default_column = 1
    default_weight = 0
    default_active = True
    template_name = None
    context = None
    icon_css_class = None
    more_link_pattern_name = None
    stylesheets = None
    javascript_files = None

    def __init__(self, sender, request, **kwargs):
        """
        Initializes the widget instance. This is done when the signal is sent.

        Only the required request argument is used. Because of Django's signal
        API, we have to take also a sender argument and wildcard keyword
        arguments. But they are not used here.
        """
        self.request = request

    def __repr__(self):
        return repr(self.get_verbose_name())

    def __unicode__(self):
        return unicode(self.get_verbose_name())

    @classmethod
    def get_dispatch_uid(cls):
        """
        Returns the name as a unique string for each class. Returns None for
        the base class so it will not be connected to the signal.
        """
        return cls.name

    def get_verbose_name(self):
        """
        Returns a human readable name of the widget.
        """
        return self.verbose_name or self.name.capitalize()

    def check_permission(self):
        """
        Returns True if the request user is allowed to see the widget.
        """
        return self.permission_required is None or self.request.user.has_perm(self.permission_required)

    def is_active(self):
        """
        Returns True if the widget is active to be displayed.
        """
        session_widgets = self.request.session.get('widgets', {})
        return session_widgets.get(self.name, self.default_active)

    def get_html(self):
        """
        Returns the html code of the widget.

        This method also adds the widget itself to the context.
        """
        if self.template_name is not None:
            html = render_to_string(
                template_name=self.template_name,
                dictionary=self.get_context_data(widget=self),
                context_instance=RequestContext(self.request))
        else:
            raise NotImplementedError('A widget class must define either a get_html '
                                      'method or have template_name argument.')
        return html

    def get_context_data(self, **context):
        """
        Returns the context data for the widget template.
        """
        return_context = self.context or {}
        return_context.update(context)
        return return_context

    def get_icon_css_class(self):
        """
        Returns the css class name of the icon.
        """
        return self.icon_css_class or 'icon-%s' % self.name

    def get_url_for_more(self):
        """
        Returns the url for the link 'More ...' in the base template.
        """
        if self.more_link_pattern_name is not None:
            url = reverse(self.more_link_pattern_name)
        else:
            url = None
        return url

    def get_stylesheets(self):
        """
        Returns an interable of stylesheets to be loaded.
        """
        return iter(self.stylesheets or [])

    def get_javascript_files(self):
        """
        Returns an interable of javascript files to be loaded.
        """
        return iter(self.javascript_files or [])
