# -*- encoding: utf-8 -*-
# bijoe - BI dashboard
# Copyright (C) 2015  Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.


from django import forms
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext as _
from django.utils.safestring import mark_safe
from django.utils import six
from django.forms import ModelForm, TextInput, NullBooleanField
from django.conf import settings

try:
    from django_select2.forms import Select2MultipleWidget

    def build_select2_multiple_widget():
        return Select2MultipleWidget()

except ImportError:
    from django_select2.widgets import Select2MultipleWidget

    def build_select2_multiple_widget():
        return Select2MultipleWidget(select2_options={'width': '100%'})


from . import models


class VisualizationForm(ModelForm):
    class Meta:
        model = models.Visualization
        exclude = ('parameters',)
        widgets = {
            'name': TextInput,
        }

DATE_RANGES = [
    {
        'value': '3_last_months',
        'label': _('3 last months'),
        'start': u"les 3 derniers mois",
        'end': u"maintenant",
    },
    {
        'value': 'this_year',
        'label': _('this year'),
        'start': u"cette année",
        'end': u"l\'année prochaine",
    },
    {
        'value': 'last_year',
        'label': _('last year'),
        'start': u'l\'année dernière',
        'end': u'cette année',
    },
    {
        'value': 'this_quarter',
        'label': _('this quarter'),
        'start': u'ce trimestre',
        'end': "le prochain trimestre",
    },
    {
        'value': 'last_quarter',
        'label': _('last quarter'),
        'start': u'le dernier trimestre',
        'end': u'ce trimestre',
    },
    {
        'value': 'since_1jan_last_year',
        'label': _('since 1st january last year'),
        'start': u'l\'année dernière',
        'end': u'maintenant',
    },
]


def get_date_range_choices():
    return [('', '---')] + [(r['value'], r['label'])
                            for r in getattr(settings, 'BIJOE_DATE_RANGES', DATE_RANGES)]


class DateRangeWidget(forms.MultiWidget):
    def __init__(self, attrs=None):
        attrs = attrs.copy() if attrs else {}
        attrs.update({'type': 'date', 'autocomplete': 'off'})
        attrs1 = attrs.copy()
        attrs1['placeholder'] = _(u'start')
        attrs2 = attrs.copy()
        attrs2['placeholder'] = _(u'end')
        widgets = (
            forms.DateInput(attrs=attrs1),
            forms.DateInput(attrs=attrs2),
            forms.Select(choices=get_date_range_choices()),
        )
        super(DateRangeWidget, self).__init__(widgets, attrs=attrs)

    def decompress(self, value):
        if not value:
            return None, None, None
        for date_range in DATE_RANGES:
            if (value['start'], value['end']) == (date_range['start'], date_range['end']):
                return None, None, date_range['value']
        return value['start'], value['end'], None

    def render(self, name, value, attrs=None):
        output = super(DateRangeWidget, self).render(name, value, attrs=attrs)
        _id = self.build_attrs(attrs).get('id', None)
        if _id:
            output += mark_safe("<script>$(function () { bijoe_date_range('#%s'); });</script>" %
                                _id)
        return output

    class Media:
        js = ('js/bijoe.daterange.js',)


class DateRangeField(forms.MultiValueField):
    widget = DateRangeWidget

    def __init__(self, *args, **kwargs):
        # Or define a different message for each field.
        fields = (
            forms.DateField(required=False),
            forms.DateField(required=False),
            forms.ChoiceField(choices=get_date_range_choices(), required=False)
        )
        super(DateRangeField, self).__init__(fields=fields, require_all_fields=False, *args,
                                             **kwargs)

    def compress(self, values):
        if not values:
            return None
        named_range = values[2]
        if not named_range:
            return {'start': values[0], 'end': values[1]}
        for r in DATE_RANGES:
            if r['value'] == named_range:
                return {'start': r['start'], 'end': r['end']}
        return {'start': None, 'end': None}


class CubeForm(forms.Form):
    representation = forms.ChoiceField(
        label=_(u'Presentation'),
        choices=[('table', _('table')),
                 ('graphical', _('chart'))],
        widget=forms.RadioSelect())

    def __init__(self, *args, **kwargs):
        self.cube = cube = kwargs.pop('cube')
        self.base_fields = self.base_fields.copy()

        dimension_choices = [('', '')] + [
            (dimension.name, dimension.label)
            for dimension in cube.dimensions if dimension.type not in ('datetime', 'date')]
        # loop
        self.base_fields['loop'] = forms.ChoiceField(
            label=_('Loop by'),
            choices=dimension_choices,
            required=False)

        # filters
        for dimension in cube.dimensions:
            if not dimension.filter:
                continue
            field_name = 'filter__%s' % dimension.name
            if dimension.type == 'date':
                self.base_fields[field_name] = DateRangeField(
                    label=dimension.label.capitalize(), required=False)
            elif dimension.type == 'bool':
                self.base_fields[field_name] = NullBooleanField(
                    label=dimension.label.capitalize(), required=False)
            else:
                members = []
                for _id, label in dimension.members:
                    members.append((_id, six.text_type(_id), label))
                members.append((None, '__none__', _('None')))

                def coercion_function(members):
                    def f(v):
                        for value, s, label in members:
                            if v == s:
                                return value
                        return None
                    return f

                self.base_fields[field_name] = forms.TypedMultipleChoiceField(
                    label=dimension.label.capitalize(),
                    choices=[(s, label) for v, s, label in members],
                    coerce=coercion_function(members),
                    required=False,
                    widget=build_select2_multiple_widget())

        # group by
        self.base_fields['drilldown_x'] = forms.ChoiceField(
            label=_('Group by horizontaly'),
            choices=dimension_choices,
            required=False)

        self.base_fields['drilldown_y'] = forms.ChoiceField(
            label=_('Group by vertically'),
            choices=dimension_choices,
            required=False)

        # measures
        choices = [(measure.name, measure.label)
                   for measure in cube.measures if measure.type != 'point']
        self.base_fields['measure'] = forms.ChoiceField(
            label=_('Measure'), choices=choices)

        super(CubeForm, self).__init__(*args, **kwargs)

    def clean(self):
        cleaned_data = super(CubeForm, self).clean()

        loop = cleaned_data.get('loop')
        drilldown_x = cleaned_data.get('drilldown_x')
        drilldown_y = cleaned_data.get('drilldown_y')

        if loop and (loop == drilldown_x or loop == drilldown_y):
            raise ValidationError({'loop': _('You cannot use the same dimension for looping and'
                                             ' grouping')})
        return cleaned_data
