#    Copyright (C) 2017  Alban Gruin
#
#    celcatsanitizer 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.
#
#    celcatsanitizer 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 celcatsanitizer.  If not, see <http://www.gnu.org/licenses/>.

import datetime

from django.db.models import Count, Max
from django.db.models.functions import ExtractWeek, ExtractYear, Length
from django.http import Http404
from django.shortcuts import get_object_or_404, render

from .models import Course, Group, Room, Timetable, Year
from .utils import get_current_week, get_current_or_next_week, get_week, group_courses

import edt

def index(request):
    years = Year.objects.order_by("name")
    return render(request, "index.html", {"elements": years})

def mention_list(request, year_slug):
    year = get_object_or_404(Year, slug=year_slug)
    timetables = Timetable.objects.order_by("name").filter(year=year)

    return render(request, "index.html", {"year": year, "elements": timetables})

def group_list(request, year_slug, timetable_slug):
    timetable = get_object_or_404(Timetable, year__slug=year_slug, slug=timetable_slug)
    groups = Group.objects.filter(source=timetable.source, hidden=False).order_by("name")

    start, _ = get_week(*get_current_week())
    end = start + datetime.timedelta(weeks=4)

    groups_weeks = Course.objects.get_weeks(begin__gte=start, begin__lt=end, groups__in=groups)

    for group in groups:
        for group_week in groups_weeks:
            if group.corresponds_to(group_week["groups__mention"],
                                    group_week["groups__semester"],
                                    group_week["groups__subgroup"]):
                if not hasattr(group, "weeks"):
                    group.weeks = []

                date, _ = get_week(group_week["year"], group_week["week"])
                if date not in group.weeks:
                    group.weeks.append(date)

        if hasattr(group, "weeks"):
            group.weeks.sort()

    return render(request, "group_list.html", {"timetable": timetable, "groups": groups})

def timetable_common(request, obj, year=None, week=None, timetable=None):
    current_year, current_week = get_current_or_next_week()
    is_old_timetable, provided_week = False, True

    if year is None or week is None:
        year, week = current_year, current_week
        provided_week = False
    elif (int(year), int(week)) < (current_year, current_week):
        is_old_timetable = True

    start, end = get_week(year, week)

    courses = Course.objects.get_courses(obj, begin__gte=start, begin__lt=end)
    if not courses.exists() and provided_week:
        raise Http404

    last_update = courses.aggregate(Max("last_update"))["last_update__max"]
    grouped_courses = group_courses(courses)

    return render(request, "timetable.html", {"group": obj, "courses": grouped_courses,
                                              "last_update": last_update,
                                              "year": year, "week": int(week),
                                              "is_old_timetable": is_old_timetable,
                                              "group_mode": isinstance(obj, Group),
                                              "timetable": timetable})

def timetable(request, year_slug, timetable_slug, group_slug, year=None, week=None):
    timetable = get_object_or_404(Timetable, year__slug=year_slug, slug=timetable_slug)
    group = get_object_or_404(Group, slug=group_slug, source=timetable.source)

    return timetable_common(request, group, year, week, timetable)

def calendars(request, year_slug, timetable_slug, group_slug):
    timetable = get_object_or_404(Timetable, year__slug=year_slug,
                                  slug=timetable_slug)
    group = get_object_or_404(Group, source=timetable.source, slug=group_slug)
    groups = Group.objects.get_parents(group).annotate(length=Length("subgroup")) \
                                             .order_by("length")

    return render(request, "calendars.html", {"timetable": timetable,
                                              "group": group, "groups": groups})

def rooms(request):
    # On récupère les dates allant de cette semaine à dans un mois
    start, _ = get_week(*get_current_week())
    end = start + datetime.timedelta(weeks=4)

    # Récupération des salles et de toutes les semaines où elles sont
    # concernées.
    # Cette requête associe chaque salle à toutes les semaines où un
    # cours s’y déroule. Le résultat est trié par le nom de la salle
    # et par semaine.
    # TODO optimiser cette requête, elle me semble un peu lente
    rooms = Room.objects.filter(course__begin__gte=start, course__begin__lt=end) \
                        .order_by("name") \
                        .annotate(year=ExtractYear("course__begin"),
                                  week=ExtractWeek("course__begin"),
                                  c=Count("*"))

    # Regroupement des semaines dans une liste de chaque objet salle
    rooms_weeks = []
    for room in rooms:
        # Si on a pas traité de salle ou que la salle courante
        # dans le résultat de la requête est différente de la dernière
        # dans la liste des salles traitées
        if len(rooms_weeks) == 0 or rooms_weeks[-1].id != room.id:
            # On lui affecte un tableau et on l’ajoute dans
            # la liste des salles à traiter
            room.weeks = []
            rooms_weeks.append(room)

        # On récupère le premier jour de la semaine
        date, _ = get_week(room.year, room.week)
        # Et on le rajoute dans la liste des semaines de la salle.
        rooms_weeks[-1].weeks.append(date)

    # Rendu de la page.
    return render(request, "group_list.html", {"groups": rooms_weeks})

def room_timetable(request, room_slug, year=None, week=None):
    room = get_object_or_404(Room, slug=room_slug)
    return timetable_common(request, room, year, week)

def ctx_processor(request):
    return {"celcatsanitizer_version": edt.VERSION}