from django.views.generic import DetailView, UpdateView, CreateView
from django.core.urlresolvers import reverse
from django.views.generic import TemplateView
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.http import HttpResponseRedirect
from django.db import transaction
from django.utils.translation import ugettext as _
from django.utils.html import mark_safe

from utils.view_utils import list_breadcrumb, default_detail_crumb, edit_breadcrumb, add_breadcrumb
from core.models import WinoseBaseWithTrackingModel
from utils.view_utils import pimp_form_fields
from utils.decorators import autostrip
from users.access_checks import LoginRequiredMixin


class ModelListViewBase(TemplateView):
    template_name = 'model_list.html'
    show_active = None
    serializer = None

    def bread(self):
        t = list_breadcrumb(self.serializer.Meta.model.overview_url,
                            self.serializer.Meta.model)
        return [t]

    def post(self, request, *args, **kwargs):
        try:
            if request.method == 'POST':
                arr = request.POST.get('pk_list', None)
                model = self.serializer.Meta.model
                mapped_int_pk = list(map(int, arr.split(',')))

                items = model.objects.filter(id__in=mapped_int_pk)

                with transaction.atomic():
                    for item in items:
                        item.toggle_is_active()

        except Exception as exp:
            print(exp)

        return HttpResponseRedirect(redirect_to=reverse(self.serializer.Meta.model.overview_url))

    def get_query(self):
        return self.serializer.Meta.model.objects.all()

    def get_context_data(self, **kwargs):
        ctx = super(ModelListViewBase, self).get_context_data(**kwargs)
        verbose_names = list()

        ctx['verbose_names'] = verbose_names
        ctx['base_url'] = reverse(self.serializer.Meta.model.overview_url)
        if self.serializer.Meta.model.create_url is not None:
            ctx['add_url'] = reverse(self.serializer.Meta.model.create_url)

        active = self.request.GET.get('a', 'True') == 'True'
        ser = self.serializer(self.get_query().filter(is_active=active), many=True)

        # NOTE: fixed date time format regardless of i18n things, for easier sorting in tables
        # is only applied here, the rest should respect the current locale settings and such things
        from wnsq.base_settings import TABLE_DATETIME_FORMAT

        ctx['DATETIMEFORMAT'] = TABLE_DATETIME_FORMAT

        for fn in ser.fields:
            try:
                verbose_names.append(self.serializer.Meta.model._meta.get_field_by_name(fn)[0].verbose_name)
            except Exception as who_cares:
                # TODO: this could be done way better.
                verbose_names.append(WinoseBaseWithTrackingModel.verbose_for_computed(fn))

        ctx['object_list'] = ser.data
        ctx['archive_query'] = not active

        if hasattr(self, 'additional_buttons'):
            ctx['additional_buttons'] = self.additional_buttons

        return ctx

    @staticmethod
    def datatables_translation():
        translate_dict = {
            "paginate": {
                'previous': _("Previous"),
                'next': _("Next")
            },
            "search": _("Search"),
            "zeroRecords": _("no matching items found"),
            "lengthMenu": _("Show _MENU_ items."),
        }
        return mark_safe(str(translate_dict))

class DetailedModelViewBase(DetailView):
    template_name = 'detail_view.html'

    # list of field names which should be skipped in the detail view
    skip_fields = []

    def post(self, request, *args, **kwargs):
        redir = request.POST.get('reload_url', '/')
        try:
            obj = self.get_object()
            obj.toggle_is_active()
        except:
            pass
        return HttpResponseRedirect(redirect_to=redir)

    # various detail views offer related tables and need to provide access to the
    # translations inside the template
    @staticmethod
    def datatables_translation():
        return ModelListViewBase.datatables_translation()

    def bread(self):
        base = [list_breadcrumb(self.model.overview_url, self.model)]
        base.append(default_detail_crumb(self.model.detail_url, self.object))
        return base

    def get_context_data(self, **kwargs):
        ctx = super(DetailedModelViewBase, self).get_context_data(**kwargs)

        object_info = list()

        # retain ordering as fields were declared in model
        for f in self.model._meta.fields:
            # skip pk and is_active and every field the view defines as not to be viewed
            if f.name == 'id' or f.name == 'is_active' or f.name in self.skip_fields:
                continue

            verbose = f.verbose_name

            if hasattr(self.object, 'get_' + f.name + '_display'):
                value = getattr(self.object, 'get_' + f.name + '_display')
            else:
                value = getattr(self.object, f.name)

            provides_link = False

            link_url = None
            fk = None  # for foreign key links, we also need to provide the key itself

            if isinstance(f, models.ForeignKey) and value:
                link_url = f.rel.to.detail_url
                fk = value.pk
                provides_link = True
            elif isinstance(f, models.FileField) and value:
                link_url = value.url
                provides_link = True
            elif isinstance(f, models.BooleanField):  # be more fancy for bools
                if value:
                    value = '\N{CHECK MARK}'
                else:
                    value = '\N{BALLOT X}'

            object_info.append((verbose, value, provides_link, link_url, fk))

        if self.object.update_url is not None:
            ctx['edit_url'] = reverse(self.object.update_url, kwargs={'pk': self.object.pk})

        if self.object.create_url is not None:
            ctx['create_url'] = reverse(self.object.create_url)

        ctx['object_info'] = object_info
        # NOTE: this fails for models with the same name in different apps!
        # looks up the contentType for the history link, which doesn't know
        # about the actual model
        ctx['contentType'] = ContentType.objects.get(model=self.model.__name__.lower()).pk

        if isinstance(self.object, WinoseBaseWithTrackingModel):
            ts = self.object.change_set.all().order_by('-timestamp')

            if ts:
                ctx['last_modified'] = ts[0].timestamp

        return ctx


# simple generic form view, set form class appropriately
# set success and back urls appropriately
class SimpleUpdateViewBase(UpdateView):
    template_name = 'simple_form.html'

    def bread(self):
        base = [list_breadcrumb(self.model.overview_url, self.model)]
        base.append(default_detail_crumb(self.model.detail_url, self.object))
        base.append(edit_breadcrumb())
        return base

    def get_form_class(self):
        # apply autostrip decorator to form class
        return autostrip(super(SimpleUpdateViewBase, self).get_form_class())

    # both cancel and back take you to the detail page
    def get_success_url(self):
        return reverse(self.object.detail_url, kwargs={'pk': self.object.id})

    def get_context_data(self, **kwargs):
        ctx = super(SimpleUpdateViewBase, self).get_context_data(**kwargs)
        ctx['pimped_form'] = pimp_form_fields(ctx['form'])
        ctx['form_back_url'] = self.get_success_url()
        return ctx


# nearly the same as above, but for create
# somewhat generic create view. set success url to control
# where to go on success. if its is not set, we try to
# determine the url by abusing the naming scheme
class SimpleCreateViewBase(CreateView):
    template_name = 'simple_form.html'

    def bread(self):
        base = [list_breadcrumb(self.model.overview_url, self.model)]
        base.append(add_breadcrumb())
        return base

    def get_success_url(self):
        return reverse(self.object.detail_url, kwargs={'pk': self.object.pk})

    def get_form_class(self):
        return autostrip(super(SimpleCreateViewBase, self).get_form_class())

    def get_context_data(self, **kwargs):
        ctx = super(SimpleCreateViewBase, self).get_context_data(**kwargs)
        ctx['pimped_form'] = pimp_form_fields(ctx['form'])
        if self.request.GET.get('backTo'):
            ctx['form_back_url'] = self.request.GET.get('backTo')
        else:
            ctx['form_back_url'] = reverse(self.model.overview_url)
        return ctx


class HomepageView(LoginRequiredMixin, TemplateView):
    template_name = 'homepage.html'

    def bread(self):
        return [{'url': reverse('homepage'), 'label': _('homepage')}]

    def get_context_data(self, **kwargs):
        ctx = super(HomepageView, self).get_context_data(**kwargs)

        from samples.models import Sample

        next_samples = list()
        next_samples_query = Sample.objects.filter()[:5]

        for s in next_samples_query:
            next_samples.append(dict())
            next_samples[-1]['sample_id'] = s.id
            next_samples[-1]['sample'] = str(s)

        ctx['next_samples'] = next_samples
        ctx['active_samples'] = Sample.objects.filter(is_active=True).count()

        # lab data

        from panelists.models import Panelist

        active_p = Panelist.objects.filter(is_active=True)

        ctx['active_panelists'] = active_p.count()

        # expiring reference gases
        from referencegases.models import ReferenceGas
        from datetime import datetime

        exp_ref = ReferenceGas.objects.filter(is_active=True, expires_at__isnull=False).order_by('expires_at').values('expires_at',
                                                                                                      'id',
                                                                                                      'name')[:5]
        for refg in exp_ref:
            refg['expires_in'] = (refg['expires_at'] - datetime.now().date()).days
            refg['substance'] = ReferenceGas.objects.get(id=refg['id']).get_substance_display()

        ctx['expiring_refgases'] = exp_ref

        # lab performance value
        from panelists.qualification import lab_qualification_values

        ctx.update(lab_qualification_values() or {})

        # expiring calibrations
        from devices.models import Calibration

        calibs = Calibration.objects.filter(is_active=True).order_by('valid_to').values('id', 'valid_to', 'created_for')[:3]

        for c in calibs:
            c['expires_in'] = (c['valid_to'] - datetime.now().date()).days

        ctx['expiring_calibs'] = calibs

        from clients.models import Client, ClientOffice
        from projects.models import Project
        from measurements.models import Measurement

        ctx['active_clients'] = Client.objects.filter(is_active=True).count()
        ctx['active_offices'] = ClientOffice.objects.filter(is_active=True).count()
        ctx['active_projects'] = Project.objects.filter(is_active=True).count()
        ctx['active_measurements'] = Measurement.objects.filter(is_active=True).count()

        return ctx


class AboutView(TemplateView):
    template_name = 'about.html'

    def get_context_data(self, **kwargs):
        from wnsq.settings_dev import WINOSE_MAJOR, WINOSE_MINOR, WINOSE_REVISION

        ctx = super(AboutView, self).get_context_data(**kwargs)
        ctx['ver_major'] = WINOSE_MAJOR
        ctx['ver_minor'] = WINOSE_MINOR
        ctx['ver_rev'] = WINOSE_REVISION
        return ctx


# pull a couple of translations into the context to avoid duplications here and there
def verbose_name_context(request):
    from projects.models import Project
    from samples.models import Sample
    from panelists.models import Panelist
    from clients.models import Client, ClientOffice, ClientCategory, ContactPerson
    from measurements.models import Measurement
    from devices.models import Olfactometer
    from referencegases.models import ReferenceGas
    from bags.models import Bag

    return {
        'projects_vnp': Project._meta.verbose_name_plural,
        'samples_vnp': Sample._meta.verbose_name_plural,
        'panel_vnp': Panelist._meta.verbose_name_plural,
        'clients_vnp': Client._meta.verbose_name_plural,
        'offices_vnp': ClientOffice._meta.verbose_name_plural,
        'categories_vnp': ClientCategory._meta.verbose_name_plural,
        'measurements_vnp': Measurement._meta.verbose_name_plural,
        'contacts_vnp': ContactPerson._meta.verbose_name_plural,
        'bags_vnp': Bag._meta.verbose_name_plural,
        'olfactometers_vnp': Olfactometer._meta.verbose_name_plural,
        'refgas_vnp': ReferenceGas._meta.verbose_name_plural,
    }
