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

import json
import os.path

from django import forms
from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ValidationError
from django.template.loader import render_to_string
from django.template.defaultfilters import filesizeformat

from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Div, HTML, Field, Fieldset

from ..base.models import (Request, Profile, ProfileOption,
        ProfileOptionChoice, DeliveryPlace, DocumentUsage, DocumentLicence)
from ..base.models.request import minimum_delivery_timedelta

from ajax_select.fields import AutoCompleteField

import utils
from ..utils import ellipsize
import app_settings
from ..base import app_settings as base_app_settings
import widgets


def make_description(model):
    qs = model.objects.all()
    if not any([o.description for o in qs]):
        return {}
    return { 'rel': 'popover',
             'data-content': render_to_string('field_popover_description.html',
                {'objects': qs}),
             'data-title': _('Aide') }

mousewheel_popover = {
    'rel': 'popover',
    'data-title': _('Aide'), 
    'data-content': _('Vous pouvez augmentez ce nombre avec votre molette de souris.')
}

class BaseModelForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(BaseModelForm, self).__init__(*args, **kwargs)
        for field in getattr(self.Meta, 'required', ()):
            self.fields[field].required = True
        self.helper = FormHelper()
        self.helper.form_tag = False

    def full_clean(self):
        oldvalues = {}
        for k, v in self.fields.iteritems():
            oldvalues[k] = v.required
            v.required = False
        result = super(BaseModelForm, self).full_clean()
        for k, v in oldvalues.iteritems():
            self.fields[k].required = v
        return result

    def pprint_data_default_hook(self, field, data):
        required = getattr(self.Meta, 'required', ())
        value = getattr(self.instance, field, '') or ''
        missing = not bool(value) and field in required
        return (self.fields[field].label, u'%s' % (getattr(self.instance, field, None) or ''), missing)

    def pprint_data(self, data):
        ret = []
        for field in self._meta.fields:
            hook_name = 'pprint_data_%s' % field
            hook = getattr(self, hook_name, None)
            if hook is None:
                ret.append(self.pprint_data_default_hook(field, data))
            else:
                ret.append(hook(data))
        return ret

class DocumentUploadForm(BaseModelForm):
    class Meta:
        model = Request 
        fields = ('uploadfile', )
        widgets = {
                'uploadfile': widgets.ClearableFileInputNoURL,
        }

    @classmethod
    def display_name(self):
        return _(u'Envoi du document')

    def __init__(self, *args, **kwargs):
        super(DocumentUploadForm, self).__init__(*args, **kwargs)
        self.fields['uploadfile'].label = _(u'Document')
        self.fields['uploadfile'].help_text = _(u'Taille maximum autorisée: %s.') % \
                filesizeformat(app_settings.MAX_DOCUMENT_SIZE)

        # crispy
        self.helper.layout = Layout(
                Field('uploadfile'),
                )
    def clean(self):
        cleaned_data = super(DocumentUploadForm, self).clean()
        uploaded_file = cleaned_data.get('uploadfile')
        if uploaded_file is not None and uploaded_file is not False:
            if uploaded_file.size > app_settings.MAX_DOCUMENT_SIZE:
                raise ValidationError(_('Fichier trop gros; la taille maximum autorisée est de %s.'
                    % filesizeformat(app_settings.MAX_DOCUMENT_SIZE)))
            try:
                utils.fill_document_attributes_from_pdf_file(self.instance, uploaded_file)
            except ValueError, e:
                raise ValidationError(_(e.args[0]))
        return cleaned_data

    def save(self, *args, **kwargs):
        return super(DocumentUploadForm, self).save(*args, **kwargs)

    def pprint_data_uploadfile(self, data):
        '''
        get the "cleaned" datas from the form, return a list of tuples
        [ (u'description', u'value'), ... ]
        '''
        if self.instance.uploadfile:
            name = os.path.basename(str(self.instance.uploadfile))
        else:
            name = _(u'sous forme papier')
        return (_(u'Document'), name, False)


class DocumentDetailsForm(BaseModelForm):
    class Meta:
        model = Request
        fields = ('name', 'nb_pages', 'usage', )
        required = fields

    @classmethod
    def display_name(self):
        return _(u'Détails sur le document')

    def pprint_data_nb_pages(self, data):
        t = self.pprint_data_default_hook('nb_pages', data)
        return t[:2] + (self.instance.nb_pages <= 0,)

    def clean_nb_pages(self):
        if self.cleaned_data['nb_pages'] is None:
            raise ValidationError('Le nombre de pages doit être un nombre')
        return self.cleaned_data['nb_pages']

    def save(self):
        if self.cleaned_data['usage'] and self.cleaned_data['usage'].no_diffusion:
            self.instance.licence = DocumentLicence(id=1)
        return super(DocumentDetailsForm, self).save()

    def __init__(self, *args, **kwargs):
        super(DocumentDetailsForm, self).__init__(*args, **kwargs)
        self.helper.layout = Layout(
                Field('name', css_class='span12'),
                Field('nb_pages', css_class='input-mini wheelable-input', **mousewheel_popover),
                Field('usage', css_class='span6',
                    **make_description(DocumentUsage)),
                )

class ReproOriginForm(BaseModelForm):
    sponsor = AutoCompleteField('ldap-users', help_text=None, label=_(u'Commanditaire'), required=False)

    class Meta:
        model = Request
        fields = ('sponsor', 'entity')
        widgets = { 'entity': widgets.MillerColumns}
        required = ('entity', )
    
    class Media:
        css = {
                'all': ('css/ajax_select.css',),
        }
        js = ('js/ajax_select.js',)

    @classmethod
    def display_name(self):
        return _(u'Affectation de la demande')

    def __init__(self, *args, **kwargs):
        self.frequent_entities = kwargs.pop('frequent_entities', [])
        super(ReproOriginForm, self).__init__(*args, **kwargs)
        self.fields['entity'].help_text = _(u'Sélectionnez le destinataire dans les listes, selon le budget à imputer.')
        # crispy
        self.helper = FormHelper()
        self.helper.form_tag = False
        self.helper.layout = Layout(
                Field('sponsor', css_class='span12'),
                Field('entity', template='field_entity.html',
                    **{'data-frequent-values': json.dumps(self.frequent_entities)}))

    def save(self, *args, **kwargs):
        if self.instance.entity:
            codes = self.instance.entity.accounting_codes()
            if len(codes) == 1:
                self.instance.financial_code = codes[0].code
        super(ReproOriginForm, self).save(*args, **kwargs)


class ReproDetailsForm(BaseModelForm):
    class Meta:
        model = Request
        fields = ('copies', 'base_profile', 'details',)
        required = ('copies', 'base_profile')
        widgets = {
            'base_profile': forms.RadioSelect,
            'details': forms.Textarea,
        }

    @classmethod
    def display_name(self):
        return _(u"Choix d'impression")

    def __init__(self, *args, **kwargs):
        super(ReproDetailsForm, self).__init__(*args, **kwargs)
        l = ((u'%s' % p.id, p) for p in Profile.visibles.all()) 
        self.fields['base_profile'].choices = l

        # crispy
        self.helper = FormHelper()
        self.helper.form_tag = False
        self.helper.layout = Layout(
                Field('copies', css_class='input-mini wheelable-input',
                    **mousewheel_popover),
                Field('base_profile', template = 'radioprofile.html'),
                )

        self.helper.layout.fields.extend([
            HTML(u'<div class="accordion-group">'),
            HTML(u'<div class="accordion-heading"><a class="accordion-toggle" data-toggle="collapse" href="#profile-details">%s</a></div>' % _(u'Options')),
            HTML(u'<div id="profile-details" class="accordion-body collapse">'),
            ])

        self.optionfields = []
        # build "options" fields
        for option in ProfileOption.visibles.all():
            fieldname = 'profile_option_%s' % option.id
            self.optionfields.append(fieldname)
            choices = [(u'%s' % c.id, c.name) \
                    for c in option.profileoptionchoice_set.filter(visible=True).all()]
            if option.list_type == 'L': # Liste (choix unique)
                self.fields[fieldname] = forms.ChoiceField(
                    label = u'%s' % option.name,
                    choices = choices,
                    required = False,
                    help_text = option.description,
                    )
            elif option.list_type == 'R': # Radio (choix unique)
                self.fields[fieldname] = forms.ChoiceField(
                    label = u'%s' % option.name,
                    widget = forms.RadioSelect,
                    choices = choices,
                    required = False,
                    help_text = option.description,
                    )
            elif option.list_type == 'C': # Cases à cocher (choix multiple)
                self.fields[fieldname] = forms.MultipleChoiceField(
                    label = u'%s' % option.name,
                    widget = forms.CheckboxSelectMultiple,
                    choices = choices,
                    required = False,
                    help_text = option.description,
                    )
            elif option.list_type == 'LM': # Liste (choix multiple)
                self.fields[fieldname] = forms.MultipleChoiceField(
                    label = u'%s' % option.name,
                    choices = choices,
                    required = False,
                    help_text = option.description,
                    )

        # hack to display fields side-by-side (should be done in a template)
        for n in xrange((len(self.optionfields)+1)/2):
            left_field = Field(self.optionfields[n*2])
            try:
                right_field = Field(self.optionfields[n*2+1])
            except:
                right_field = HTML('')
            self.helper.layout.fields.append(Div(
                Div(left_field, css_class='span6'),
                Div(right_field, css_class='span6'),
                css_class="row",
                ))

        self.helper.layout.fields.extend([
            Field('details', css_class='span12',
                placeholder=_(u"Indiquez par exemple un autre format "
                    u"(A3, A5, …), une orientation du papier (paysage, "
                    u"portrait, plusieurs pages par feuille, …), une "
                    u"finition particulière …")),
            HTML(u'</div></div>'), # profile_details
            ])

    def save(self, *args, **kwargs):
        self.instance.choices.clear()
        chosen = set()
        for option in ProfileOption.visibles.all():
            fieldname = 'profile_option_%s' % option.id
            cleaned_data = self.cleaned_data
            value = cleaned_data.get(fieldname, None)
            if hasattr(value, 'append'):
                for v in value:
                    chosen.add(v)
            elif value is not None:
                chosen.add(value)
        chosen = filter(None, chosen)
        result = super(ReproDetailsForm, self).save(*args, **kwargs)
        self.instance.choices = ProfileOptionChoice.visibles.filter(id__in=chosen)
        return result


    def pprint_data_base_profile(self, data):
        profile = getattr(self.instance, 'base_profile', None)
        if not profile:
            base_profile = data.get('base_profile')
            if base_profile:
                profile = Profile.visibles.get(id=base_profile)
        value = u'%s' % profile if profile else ''
        return (_(u'Profil de base'), value, not bool(value))

    def pprint_data_copies(self, data):
        t = self.pprint_data_default_hook('copies', data)
        return t[:2] + (self.instance.copies < 1,)

    def pprint_data_choices(self, data):
        ret = []
        profile = getattr(self.instance, 'base_profile', None)
        if profile:
            ids = set(profile.choices.values_list('id', flat=True))

            def choice_ok(choice):
                return choice.id not in ids or \
                        choice.option.list_type in ('C', 'LM')

            if self.instance.id:
                for choice in self.instance.choices.all():
                    if choice_ok(choice):
                        ret.append((choice.option.name, choice.name, False))
            else:
                option_choices = filter(None, [ self.data.get(field) for field in self.optionfields ])
                for choice in ProfileOptionChoice.visibles.filter(id__in=option_choices).select_related():
                    if choice_ok(choice):
                        ret.append((choice.option.name, choice.name, False))
        return ret

    def pprint_data_details(self, data):
        t = self.pprint_data_default_hook('details', data)
        return t[0], ellipsize(t[1], 80), t[2]

    def pprint_data(self, data):
        ret = super(ReproDetailsForm, self).pprint_data(data)
        ret.extend(self.pprint_data_choices(data))
        return ret

    def clean_copies(self):
        if self.cleaned_data['copies'] is None:
            raise ValidationError('Le nombre de copies doit être un nombre')
        return self.cleaned_data['copies']


class DeliveryForm(BaseModelForm):
    class Meta:
        model = Request
        fields = ('delivery_place', 'delivery_date', 'contact_email',
                'contact_telephone1', 'contact_telephone2', 'contact_bureau')
        required = ('delivery_place', 'delivery_date')

    @classmethod
    def display_name(self):
        return _(u"Options de livraison")

    def check_delivery_date(self, delivery_date):
        start = self.instance.history_set.start_date()
        not_before = start + minimum_delivery_timedelta()
        return not (delivery_date < not_before.date())

    def pprint_data_delivery_date(self, data):
        t = self.pprint_data_default_hook('delivery_date', data)
        return t[:2] + (not self.check_delivery_date(self.instance.delivery_date),)

    def clean_delivery_date(self):
        delivery_date = self.cleaned_data['delivery_date']
        if not delivery_date or not self.check_delivery_date(delivery_date):
            msg = u"La date de livraison souhaitée doit être au minimum"
            days = base_app_settings.MINIMUM_DELIVERY_DELAY
            msg += " %s jour(s) plus tard que la date de la demande." % days
            raise ValidationError(msg)
        return delivery_date

    def __init__(self, *args, **kwargs):
        super(DeliveryForm, self).__init__(*args, **kwargs)
        self.fields['delivery_place'].empty_label = None

        # crispy
        self.helper = FormHelper()
        self.helper.form_tag = False
        self.helper.layout = Layout(
            Field('delivery_place', css_class='span6',
                **make_description(DeliveryPlace)),
            Field('delivery_date', css_class='span2 datepicker'),
            Fieldset(u'Contact',
                HTML('{% load editor %}{% editablecontent "_request_edit_delivery_contact_header" %}{% endeditablecontent %}'),
                Field(
                    'contact_email',
                    'contact_telephone1',
                    'contact_telephone2',
                    'contact_bureau')))


class CopyrigtsForm(BaseModelForm):
    class Meta:
        model = Request
        fields = ('copyright', 'licence')
        required = ('licence',)
        widgets = {
        }

    @classmethod
    def display_name(self):
        return _(u"Options de diffusion")

    def clean(self):
        cleaned_data = super(CopyrigtsForm, self).clean()
        copyright = cleaned_data.get('copyright')
        licence = cleaned_data.get('licence')
        if copyright is True and licence is not None \
                and licence.only_free_documents:
            raise ValidationError(_(u"Ce mode de diffusion n'est pas"
                u" compatible avec un document contenant un ou des "
                u"extrait(s) d'oeuvres(s) soumise(s) au droit "
                u"d'auteur"))
        return cleaned_data

    def __init__(self, *args, **kwargs):
        super(CopyrigtsForm, self).__init__(*args, **kwargs)
        if 'licence' in self.fields:
            self.fields['licence'].label = _(u'Diffusion de votre fichier')
            if len(self.fields['licence'].choices) < 2:
                f = self.fields['licence']
                f.empty_label = None
                f.initial = list(self.fields['licence'].choices)[0][0]
        if 'copyright' in self.fields:
            self.fields['copyright'].label = _(u'Droits de copie')
            if not self.no_diffusion():
                self.fields['copyright'].required = True

        # crispy
        self.helper = FormHelper()
        self.helper.form_tag = False
        self.helper.layout = Layout(
            Field('copyright', css_class="span8"),
            HTML('{% load editor %}{% editablecontent "_request_edit_copyright_copyright" %}{% endeditablecontent %}'),
            Field('licence', css_class="span8", **make_description(DocumentLicence)),
            HTML('{% load editor %}{% editablecontent "_request_edit_copyright_licence" %}{% endeditablecontent %}'),
        )

    def no_diffusion(self):
        return self.instance is not None and self.instance.is_from_remote_request or \
            (self.instance.usage and self.instance.usage.no_diffusion) \
            or self.instance.is_paper()

    def pprint_data_copyright(self, data):
        if self.no_diffusion():
            missing = False
        else:
            missing = self.instance.copyright is None
        value = ''
        if self.instance.copyright is not None:
            value = _(u'Oui') if self.instance.copyright else _('Non')
        return (self.fields['copyright'].label, value, missing)

class FinancialForm(BaseModelForm):
    class Meta:
        model = Request
        fields = ('financial_code', 'financial_comment')
        required = ('financial_code',)

    def __init__(self, *args, **kwargs):
        self.frequent_financial_codes = kwargs.pop(
                'frequent_financial_codes', [])
        super(FinancialForm, self).__init__(*args, **kwargs)
        self.helper = FormHelper()
        self.helper.form_tag = False
        self.helper.layout = Layout(
                Field('financial_code',  **{
                        'data-frequent-values': json.dumps(self.frequent_financial_codes)
                    }),
                Field('financial_comment', css_class="span12"))

    @classmethod
    def display_name(self):
        return _(u"Données financières")

class CostForm(BaseModelForm):
    class Meta:
        model = Request
        fields = ('cost',)
        required = fields

    def __init__(self, *args, **kwargs):
        super(CostForm, self).__init__(*args, **kwargs)
        self.fields['cost'].localize = True
        self.fields['cost'].widget.is_localized = True
        if self.instance and self.instance.id:
            help_text = _(u'Coût estimé: %s\xa0€') % self.instance.estimated_cost()
            self.fields['cost'].help_text = help_text

    @classmethod
    def display_name(self):
        return _(u"Coût réel")

class ValidationForm(forms.Form):

    @classmethod
    def display_name(self):
        return _(u"Validation")

    def __init__(self, *args, **kwargs):
        super(ValidationForm, self).__init__(*args, **kwargs)
        # crispy
        self.helper = FormHelper()
        self.helper.form_tag = False

    def pprint_data(self, data):
        return []

