# Path: rental/availability.py
# class Availability:
#     """
#     Helper class to determine the availability of a list of items
# 
#     An item becomes only unavailable when it is parted of an approved rental
#     request. All other items, especially in rental requests that are still
#     pending, are considered available.
# 
#     :author: Leo Tappe
#     """
# 
#     def __init__(self, start_date, return_date, depot_id,
#                  conflicting_states=[Rental.STATE_APPROVED]):
#         self.start_date = start_date
#         self.return_date = return_date
#         self.depot_id = depot_id
#         self.conflicting_states = conflicting_states
# 
#     def get_availability_intervals(self, item):
#         """
#         Split the given time frame into a sequence of intervals
#         with the corresponding amount of available elements.
# 
#         :author: Leo Tappe
# 
#         :param item: the item that you want to rent
#         :return: a list of lists of the form [from, to, num_available]
#         """
# 
#         interval_borders = []
#         intervals = []
# 
#         rentals = Rental.objects.filter(
#             start_date__lt=self.return_date,
#             return_date__gt=self.start_date,
#             depot_id=self.depot_id,
#             state__in=self.conflicting_states,
#             items=item
#         ).prefetch_related(
#             Prefetch(
#                 'itemrental_set',
#                 queryset=ItemRental.objects.filter(item=item),
#                 to_attr='relevant_rental'
#             )
#         )
# 
#         # collect all the datetimes where a relevant rental starts / gets returned
#         for rental in rentals:
#             if rental.start_date > self.start_date:
#                 interval_borders.append(rental.start_date)
#             if rental.return_date < self.return_date:
#                 interval_borders.append(rental.return_date)
# 
#         # insert start and end date
#         interval_borders.append(self.start_date)
#         interval_borders.append(self.return_date)
# 
#         # sort these points
#         interval_borders.sort()
# 
#         # create intervals, initialize with full availability
#         for begin, end in zip(interval_borders, interval_borders[1:]):
#             intervals.append(Interval(begin, end, item.quantity))
# 
#         # for each rental, modify availability during occupied intervals accordingly
#         for rental in rentals:
#             for interval in intervals:
#                 if rental.start_date < interval.end and rental.return_date > interval.begin:
#                     interval.value -= rental.relevant_rental[0].quantity
# 
#         return intervals
# 
# Path: rental/state_transitions.py
# def allowed_transitions(managed_by_user, current_state):
#     """
#     Return the list of valid transitions from the current state
#     based on the user's permissions
# 
#     :author: Benedikt Seidl
#     """
# 
#     if managed_by_user:
#         return STATE_TRANSITIONS_MANAGER[current_state]
#     else:
#         return STATE_TRANSITIONS_USER[current_state]
# 
# Path: rental/models.py
# class Rental(models.Model):
#     """
#     A rental defines the amount of items and has a start and return date.
#     Only items from one depot can be requested for rental at once.
# 
#     When requesting a rental, the user has to enter his full name and email
#     address as well as describing the purpose of his rental. If they are
#     logged in, the user's id will be stored as well.
# 
#     After creating a rental request, it is in the PENDING state. From this,
#     it can be either APPROVED or DECLINED by a depot manager or REVOKED by
#     the requesting user. If all items were returned correctly, it can be
#     set to RETURNED to finish the rental process.
# 
#     :author: Benedikt Seidl
#     """
# 
#     STATE_PENDING = '1'
#     STATE_APPROVED = '2'
#     STATE_DECLINED = '3'
#     STATE_REVOKED = '4'
#     STATE_RETURNED = '5'
#     STATES = (
#         (STATE_PENDING, _('pending')),
#         (STATE_APPROVED, _('approved')),
#         (STATE_DECLINED, _('declined')),
#         (STATE_REVOKED, _('revoked')),
#         (STATE_RETURNED, _('returned')),
#     )
# 
#     uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
#     depot = models.ForeignKey(Depot, on_delete=models.CASCADE)
#     items = models.ManyToManyField(Item, through='ItemRental')
#     firstname = models.CharField(max_length=32)
#     lastname = models.CharField(max_length=32)
#     email = models.EmailField()
#     purpose = models.CharField(max_length=256)
#     user = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL)
#     start_date = models.DateTimeField()
#     return_date = models.DateTimeField()
#     state = models.CharField(max_length=1, choices=STATES, default=STATE_PENDING)
# 
#     def clean(self):
#         if not self.depot.active:
#             raise ValidationError({'depot': 'The depot has to be active.'})
# 
#         if self.start_date > self.return_date:
#             raise ValidationError({
#                 'start_date': 'The start date must be before the return date.'
#             })
# 
#         if self.start_date < datetime.now() and self.state == self.STATE_PENDING:
#             raise ValidationError({
#                 'start_date': 'The start date must be in the future for new rentals.'
#             })
# 
#     def __str__(self):
#         return 'Rental by %s %s' % (self.firstname, self.lastname)


from django.core.exceptions import ValidationError
from django.http import HttpResponseForbidden
from django.shortcuts import redirect, get_object_or_404
from django.views import View
from rental.availability import Availability
from rental.state_transitions import allowed_transitions
from rental.models import Rental


class RentalStateView(View):
    """
    Change the state of a given rental

    If given an invalid state, this shows a 403 Forbidden response.

    :author: Florian Stamer
    """

    def check_availability(self, rental):

# Path: rental/availability.py
# class Availability:
#     """
#     Helper class to determine the availability of a list of items
# 
#     An item becomes only unavailable when it is parted of an approved rental
#     request. All other items, especially in rental requests that are still
#     pending, are considered available.
# 
#     :author: Leo Tappe
#     """
# 
#     def __init__(self, start_date, return_date, depot_id,
#                  conflicting_states=[Rental.STATE_APPROVED]):
#         self.start_date = start_date
#         self.return_date = return_date
#         self.depot_id = depot_id
#         self.conflicting_states = conflicting_states
# 
#     def get_availability_intervals(self, item):
#         """
#         Split the given time frame into a sequence of intervals
#         with the corresponding amount of available elements.
# 
#         :author: Leo Tappe
# 
#         :param item: the item that you want to rent
#         :return: a list of lists of the form [from, to, num_available]
#         """
# 
#         interval_borders = []
#         intervals = []
# 
#         rentals = Rental.objects.filter(
#             start_date__lt=self.return_date,
#             return_date__gt=self.start_date,
#             depot_id=self.depot_id,
#             state__in=self.conflicting_states,
#             items=item
#         ).prefetch_related(
#             Prefetch(
#                 'itemrental_set',
#                 queryset=ItemRental.objects.filter(item=item),
#                 to_attr='relevant_rental'
#             )
#         )
# 
#         # collect all the datetimes where a relevant rental starts / gets returned
#         for rental in rentals:
#             if rental.start_date > self.start_date:
#                 interval_borders.append(rental.start_date)
#             if rental.return_date < self.return_date:
#                 interval_borders.append(rental.return_date)
# 
#         # insert start and end date
#         interval_borders.append(self.start_date)
#         interval_borders.append(self.return_date)
# 
#         # sort these points
#         interval_borders.sort()
# 
#         # create intervals, initialize with full availability
#         for begin, end in zip(interval_borders, interval_borders[1:]):
#             intervals.append(Interval(begin, end, item.quantity))
# 
#         # for each rental, modify availability during occupied intervals accordingly
#         for rental in rentals:
#             for interval in intervals:
#                 if rental.start_date < interval.end and rental.return_date > interval.begin:
#                     interval.value -= rental.relevant_rental[0].quantity
# 
#         return intervals
# 
# Path: rental/state_transitions.py
# def allowed_transitions(managed_by_user, current_state):
#     """
#     Return the list of valid transitions from the current state
#     based on the user's permissions
# 
#     :author: Benedikt Seidl
#     """
# 
#     if managed_by_user:
#         return STATE_TRANSITIONS_MANAGER[current_state]
#     else:
#         return STATE_TRANSITIONS_USER[current_state]
# 
# Path: rental/models.py
# class Rental(models.Model):
#     """
#     A rental defines the amount of items and has a start and return date.
#     Only items from one depot can be requested for rental at once.
# 
#     When requesting a rental, the user has to enter his full name and email
#     address as well as describing the purpose of his rental. If they are
#     logged in, the user's id will be stored as well.
# 
#     After creating a rental request, it is in the PENDING state. From this,
#     it can be either APPROVED or DECLINED by a depot manager or REVOKED by
#     the requesting user. If all items were returned correctly, it can be
#     set to RETURNED to finish the rental process.
# 
#     :author: Benedikt Seidl
#     """
# 
#     STATE_PENDING = '1'
#     STATE_APPROVED = '2'
#     STATE_DECLINED = '3'
#     STATE_REVOKED = '4'
#     STATE_RETURNED = '5'
#     STATES = (
#         (STATE_PENDING, _('pending')),
#         (STATE_APPROVED, _('approved')),
#         (STATE_DECLINED, _('declined')),
#         (STATE_REVOKED, _('revoked')),
#         (STATE_RETURNED, _('returned')),
#     )
# 
#     uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
#     depot = models.ForeignKey(Depot, on_delete=models.CASCADE)
#     items = models.ManyToManyField(Item, through='ItemRental')
#     firstname = models.CharField(max_length=32)
#     lastname = models.CharField(max_length=32)
#     email = models.EmailField()
#     purpose = models.CharField(max_length=256)
#     user = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL)
#     start_date = models.DateTimeField()
#     return_date = models.DateTimeField()
#     state = models.CharField(max_length=1, choices=STATES, default=STATE_PENDING)
# 
#     def clean(self):
#         if not self.depot.active:
#             raise ValidationError({'depot': 'The depot has to be active.'})
# 
#         if self.start_date > self.return_date:
#             raise ValidationError({
#                 'start_date': 'The start date must be before the return date.'
#             })
# 
#         if self.start_date < datetime.now() and self.state == self.STATE_PENDING:
#             raise ValidationError({
#                 'start_date': 'The start date must be in the future for new rentals.'
#             })
# 
#     def __str__(self):
#         return 'Rental by %s %s' % (self.firstname, self.lastname)


# Path: rental/views/rental_state_view.py
from django.core.exceptions import ValidationError
from django.http import HttpResponseForbidden
from django.shortcuts import redirect, get_object_or_404
from django.views import View
from rental.availability import Availability
from rental.state_transitions import allowed_transitions
from rental.models import Rental



class RentalStateView(View):
    """
    Change the state of a given rental

    If given an invalid state, this shows a 403 Forbidden response.

    :author: Florian Stamer
    """

    def check_availability(self, rental):

        availability = Availability(rental.start_date, rental.return_date, rental.depot_id)