from django import forms
from django.conf import settings
from django.core import urlresolvers
from django.views import i18n
from django.utils import safestring
from django.utils.translation import ugettext_lazy as _, get_language
from django.contrib.staticfiles.storage import staticfiles_storage

import recurrence
from recurrence import exceptions

# Django 1.5+ compatibility
try:
    import json
except ImportError:
    import django.utils.simplejson as json


class RecurrenceWidget(forms.Textarea):
    def __init__(self, attrs=None, **kwargs):
        self.js_widget_options = kwargs
        defaults = {'class': 'recurrence-widget'}
        if attrs is not None:
            defaults.update(attrs)
        super(RecurrenceWidget, self).__init__(defaults)

    def render(self, name, value, attrs=None):
        if value is None:
            value = ''
        elif isinstance(value, recurrence.Recurrence):
            value = recurrence.serialize(value)

        widget_init_js = (
            '<script type="text/javascript">'
            'recurrence.language_code =\'%s\';'
            'new recurrence.widget.Widget(\'%s\', %s);'
            '</script>'
        ) % (get_language(), attrs['id'], json.dumps(self.js_widget_options))

        return safestring.mark_safe(u'%s\n%s' % (
            super(RecurrenceWidget, self).render(name, value, attrs),
            widget_init_js))

    def get_media(self):
        js = [
            staticfiles_storage.url('recurrence/js/recurrence.js'),
            staticfiles_storage.url('recurrence/js/recurrence-widget.js'),
        ]
        i18n_media = find_recurrence_i18n_js_catalog()
        if i18n_media:
            js.insert(0, i18n_media)

        return forms.Media(
            js=js, css={
                'all': (
                    staticfiles_storage.url('recurrence/css/recurrence.css'),
                ),
            },
        )
    media = property(get_media)


class RecurrenceField(forms.CharField):
    """
    A Field that accepts the recurrence related parameters of rfc2445.

    Values are deserialized into `recurrence.base.Recurrence` objects.
    """
    widget = RecurrenceWidget
    default_error_messages = {
        'invalid_frequency': _(
            u'Invalid frequency.'),
        'max_rrules_exceeded': _(
            u'Max rules exceeded. The limit is %(limit)s'),
        'max_exrules_exceeded': _(
            u'Max exclusion rules exceeded. The limit is %(limit)s'),
        'max_rdates_exceeded': _(
            u'Max dates exceeded. The limit is %(limit)s'),
        'max_exdates_exceeded': _(
            u'Max exclusion dates exceeded. The limit is %(limit)s'),
    }

    def __init__(
        self,
        frequencies=None, accept_dtstart=True, accept_dtend=True,
        max_rrules=None, max_exrules=None, max_rdates=None, max_exdates=None,
        *args, **kwargs
    ):
        """
        Create a recurrence field.

        A `RecurrenceField` takes the same parameters as a `CharField`
        field with some additional paramaters.

        :Parameters:
            `frequencies` : sequence
                A sequence of the frequency constants specifying which
                frequencies are valid for input. By default all
                frequencies are valid.

            `accept_dtstart` : bool
                Whether to accept a dtstart value passed in the input.

            `accept_dtend` : bool
                Whether to accept a dtend value passed in the input.

            `max_rrules` : int
                The max number of rrules to accept in the input. A
                value of ``0`` means input of rrules is disabled.

            `max_exrules` : int
                The max number of exrules to accept in the input. A
                value of ``0`` means input of exrules is disabled.

            `max_rdates` : int
                The max number of rdates to accept in the input. A
                value of ``0`` means input of rdates is disabled.

            `max_exdates` : int
                The max number of exdates to accept in the input. A
                value of ``0`` means input of exdates is disabled.
        """
        self.accept_dtstart = accept_dtstart
        self.accept_dtend = accept_dtend
        self.max_rrules = max_rrules
        self.max_exrules = max_exrules
        self.max_rdates = max_rdates
        self.max_exdates = max_exdates
        if frequencies is not None:
            self.frequencies = frequencies
        else:
            self.frequencies = (
                recurrence.YEARLY, recurrence.MONTHLY,
                recurrence.WEEKLY, recurrence.DAILY,
                recurrence.HOURLY, recurrence.MINUTELY,
                recurrence.SECONDLY,
            )
        super(RecurrenceField, self).__init__(*args, **kwargs)

    def clean(self, value):
        """
        Validates that ``value`` deserialized into a
        `recurrence.base.Recurrence` object falls within the
        parameters specified to the `RecurrenceField` constructor.
        """
        try:
            recurrence_obj = recurrence.deserialize(value)
        except exceptions.DeserializationError as error:
            raise forms.ValidationError(error.args[0])

        if not self.accept_dtstart:
            recurrence_obj.dtstart = None
        if not self.accept_dtend:
            recurrence_obj.dtend = None

        if self.max_rrules is not None:
            if len(recurrence_obj.rrules) > self.max_rrules:
                raise forms.ValidationError(
                    self.error_messages['max_rrules_exceeded'] % {
                        'limit': self.max_rrules
                    }
                )
        if self.max_exrules is not None:
            if len(recurrence_obj.exrules) > self.max_exrules:
                raise forms.ValidationError(
                    self.error_messages['max_exrules_exceeded'] % {
                        'limit': self.max_exrules
                    }
                )
        if self.max_rdates is not None:
            if len(recurrence_obj.rdates) > self.max_rdates:
                raise forms.ValidationError(
                    self.error_messages['max_rdates_exceeded'] % {
                        'limit': self.max_rdates
                    }
                )
        if self.max_exdates is not None:
            if len(recurrence_obj.exdates) > self.max_exdates:
                raise forms.ValidationError(
                    self.error_messages['max_exdates_exceeded'] % {
                        'limit': self.max_exdates
                    }
                )

        for rrule in recurrence_obj.rrules:
            if rrule.freq not in self.frequencies:
                raise forms.ValidationError(
                    self.error_messages['invalid_frequency'])
        for exrule in recurrence_obj.exrules:
            if exrule.freq not in self.frequencies:
                raise forms.ValidationError(
                    self.error_messages['invalid_frequency'])

        return recurrence_obj


_recurrence_javascript_catalog_url = None


def find_recurrence_i18n_js_catalog():
    # used cached version
    global _recurrence_javascript_catalog_url
    if _recurrence_javascript_catalog_url:
        return _recurrence_javascript_catalog_url

    # first try to use the dynamic form of the javascript_catalog view
    try:
        return urlresolvers.reverse(
            i18n.javascript_catalog, kwargs={'packages': 'recurrence'})
    except urlresolvers.NoReverseMatch:
        pass

    # then scan the entire urlconf for a javascript_catalague pattern
    # that manually selects recurrence as one of the packages to include
    def check_urlpatterns(urlpatterns):
        for pattern in urlpatterns:
            if hasattr(pattern, 'url_patterns'):
                match = check_urlpatterns(pattern.url_patterns)
                if match:
                    return match
            elif (pattern.callback == i18n.javascript_catalog and
                  'recurrence' in pattern.default_args.get('packages', [])):
                if pattern.name:
                    return urlresolvers.reverse(pattern.name)
                else:
                    return urlresolvers.reverse(pattern.callback)

    root_urlconf = __import__(settings.ROOT_URLCONF, {}, {}, [''])
    url = check_urlpatterns(root_urlconf.urlpatterns)
    # cache it for subsequent use
    _recurrence_javascript_catalog_url = url
    return url
