# Calendar.social
# Copyright (C) 2018 Gergely Polonkai
#
# 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 <https://www.gnu.org/licenses/>.

"""Gregorian calendar system for Calendar.social
"""

from datetime import datetime, timedelta

from flask_babelex import lazy_gettext as _

from . import CalendarSystem


class GregorianCalendar(CalendarSystem):
    """Gregorian calendar system for Calendar.social
    """

    __name__ = _('Gregorian')

    __has_months__ = True

    START_DAY = 0
    END_DAY = 7

    month_names = (
        _('January'),
        _('February'),
        _('March'),
        _('April'),
        _('May'),
        _('June'),
        _('July'),
        _('August'),
        _('September'),
        _('October'),
        _('November'),
        _('December'),
    )

    day_names = (
        _('Monday'),
        _('Tuesday'),
        _('Wednesday'),
        _('Thursday'),
        _('Friday'),
        _('Saturday'),
        _('Sunday'),
    )

    def __init__(self, timestamp):
        self.timestamp = datetime.utcfromtimestamp(timestamp)

    @property
    def month(self):
        return self.timestamp.strftime("%B, %Y")

    @property
    def days(self):
        day_list = []

        month_first = self.timestamp.replace(day=1)

        if self.timestamp.month == 12:
            month_last = month_first.replace(day=31)
        else:
            month_last = month_first.replace(month=month_first.month + 1) - timedelta(days=1)

        pad_before = (7 - self.START_DAY + month_first.weekday()) % 7
        pad_after = (6 - month_last.weekday() + self.START_DAY) % 7

        first_display = month_first - timedelta(days=pad_before)
        last_display = month_last + timedelta(days=pad_after)
        current = first_display

        while current <= last_display:
            day_list.append(current)
            current += timedelta(days=1)

        return day_list

    @property
    def prev_year(self):
        """Returns the timestamp of the same date in the previous year
        """

        return self.timestamp.replace(year=self.timestamp.year - 1)

    @property
    def prev_year_year(self):
        """The number of the previous year
        """

        return self.timestamp.replace(year=self.timestamp.year - 1).year

    @property
    def prev_month(self):
        """Returns the timestamp of the same day in the previous month
        """

        if self.timestamp.month == 1:
            return self.prev_year.replace(month=12)

        return self.timestamp.replace(month=self.timestamp.month - 1)

    @property
    def prev_month_name(self):
        """The name of the previous month
        """

        if self.timestamp.month == 1:
            timestamp = self.prev_year.replace(month=12)
        else:
            timestamp = self.timestamp.replace(month=self.timestamp.month - 1)

        return self.month_names[timestamp.month - 1]

    @property
    def next_month(self):
        """Returns the timestamp of the same day in the next month
        """

        if self.timestamp.month == 12:
            return self.next_year.replace(month=1)

        return self.timestamp.replace(month=self.timestamp.month + 1)

    @property
    def next_month_name(self):
        """The name of the next month
        """

        if self.timestamp.month == 12:
            timestamp = self.prev_year.replace(month=1)
        else:
            timestamp = self.timestamp.replace(month=self.timestamp.month + 1)

        return self.month_names[timestamp.month - 1]

    @property
    def next_year(self):
        """Returns the timestamp of the same date in the next year
        """

        return self.timestamp.replace(year=self.timestamp.year + 1)

    @property
    def next_year_year(self):
        """The number of the next year
        """

        return self.timestamp.replace(year=self.timestamp.year + 1).year

    @property
    def has_today(self):
        """``True`` if the set month holds today’s date
        """

        now = datetime.utcnow()
        month_start_timestamp = self.timestamp.replace(day=1,
                                                       hour=0,
                                                       minute=0,
                                                       second=0,
                                                       microsecond=0)

        if self.timestamp.month == 12:
            next_month = 1
        else:
            next_month = self.timestamp.month + 1

        month_end_timestamp = month_start_timestamp.replace(month=next_month)

        return month_start_timestamp <= now < month_end_timestamp

    @staticmethod
    def day_events(date, user=None):
        """Returns all events for a given day
        """

        from ..models import Event, EventVisibility, Invitation, Profile, Response

        events = Event.query

        if user:
            events = events.outerjoin(Invitation) \
                           .outerjoin(Response) \
                           .join(Profile, Event.profile) \
                           .filter(Profile.user == user)

        start_timestamp = date.replace(hour=0, minute=0, second=0, microsecond=0)
        end_timestamp = start_timestamp + timedelta(days=1)

        events = events.filter((Event.start_time <= end_timestamp) &
                               (Event.end_time >= start_timestamp)) \
                       .order_by('start_time', 'end_time')

        if user is None:
            events = events.filter(Event.visibility == EventVisibility.public)
        else:
            events = events.filter((Event.visibility == EventVisibility.public) |
                                   (Event.profile == user.profile) |
                                   (Invitation.invitee == user.profile) |
                                   (Response.profile == user.profile))

        return events