import Cocoa
import objc

gNumDaysInMonth = (0, 31, 28, 31, 30, 21, 30, 31, 31, 30, 31, 30, 31)


def isLeap(year):
    return ((year % 4) == 0 and ((year % 100) != 0)) or (year % 400) == 0


class CalendarMatrix(Cocoa.NSMatrix):
    lastMonthButton = objc.IBOutlet()
    monthName = objc.IBOutlet()
    nextMonthButton = objc.IBOutlet()

    __slots__ = ("_selectedDay", "_startOffset")

    def initWithFrame_(self, frameRect):
        self._selectedDay = None
        self._startOffset = 0

        cell = Cocoa.NSButtonCell.alloc().initTextCell_("")
        now = Cocoa.NSCalendarDate.date()

        cell.setShowsStateBy_(Cocoa.NSOnOffButton)
        self.initWithFrame_mode_prototype_numberOfRows_numberOfColumns_(
            frameRect, Cocoa.NSRadioModeMatrix, cell, 5, 7
        )

        count = 0
        for i in range(6):
            for j in range(7):
                val = self.cellAtRow_column_(i, j)
                if val:
                    val.setTag_(count)
                count += 1

        self._selectedDay = Cocoa.NSCalendarDate.dateWithYear_month_day_hour_minute_second_timeZone_(  # noqa: B950
            now.yearOfCommonEra(),
            now.monthOfYear(),
            now.dayOfMonth(),
            0,
            0,
            0,
            Cocoa.NSTimeZone.localTimeZone(),
        )
        return self

    @objc.IBAction
    def choseDay_(self, sender):
        prevSelDate = self.selectedDay()
        selDay = self.selectedCell().tag() - self._startOffset + 1

        selDate = (
            Cocoa.NSCalendarDate.dateWithYear_month_day_hour_minute_second_timeZone_(
                prevSelDate.yearOfCommonEra(),
                prevSelDate.monthOfYear(),
                selDay,
                0,
                0,
                0,
                Cocoa.NSTimeZone.localTimeZone(),
            )
        )
        self.setSelectedDay_(selDate)
        self.highlightTodayIfVisible()

        if self.delegate().respondsToSelector_("calendarMatrix:didChangeToDate:"):
            self.delegate().calendarMatrix_didChangeToDate_(self, selDate)

    @objc.IBAction
    def monthChanged_(self, sender):
        thisDate = self.selectedDay()
        currentYear = thisDate.yearOfCommonEra()
        currentMonth = thisDate.monthOfYear()

        if sender is self.nextMonthButton:
            if currentMonth == 12:
                currentMonth = 1
                currentYear += 1
            else:
                currentMonth += 1
        else:
            if currentMonth == 1:
                currentMonth = 12
                currentYear -= 1
            else:
                currentMonth -= 1

        self.setSelectedDay_(
            Cocoa.NSCalendarDate.dateWithYear_month_day_hour_minute_second_timeZone_(
                currentYear, currentMonth, 1, 0, 0, 0, Cocoa.NSTimeZone.localTimeZone()
            )
        )
        self.refreshCalendar()
        self.choseDay_(self)

    def setSelectedDay_(self, newDay):
        self._selectedDay = newDay

    def selectedDay(self):
        return self._selectedDay

    def refreshCalendar(self):
        selDate = self.selectedDay()
        currentMonth = selDate.monthOfYear()
        currentYear = selDate.yearOfCommonEra()

        firstOfMonth = (
            Cocoa.NSCalendarDate.dateWithYear_month_day_hour_minute_second_timeZone_(
                currentYear, currentMonth, 1, 0, 0, 0, Cocoa.NSTimeZone.localTimeZone()
            )
        )
        self.monthName.setStringValue_(
            firstOfMonth.descriptionWithCalendarFormat_("%B %Y")
        )
        daysInMonth = gNumDaysInMonth[currentMonth]

        if (currentMonth == 2) and isLeap(currentYear):
            daysInMonth += 1

        self._startOffset = firstOfMonth.dayOfWeek()

        dayLabel = 1

        for i in range(42):
            cell = self.cellWithTag_(i)
            if cell is None:
                continue

            if i < self._startOffset or i >= (daysInMonth + self._startOffset):
                # blank out unused cells in the matrix
                cell.setBordered_(False)
                cell.setEnabled_(False)
                cell.setTitle_("")
                cell.setCellAttribute_to_(Cocoa.NSCellHighlighted, False)
            else:
                # Fill in valid days in the matrix
                cell.setBordered_(True)
                cell.setEnabled_(True)
                cell.setFont_(Cocoa.NSFont.systemFontOfSize_(12))
                cell.setTitle_(str(dayLabel))
                dayLabel += 1
                cell.setCellAttribute_to_(Cocoa.NSCellHighlighted, False)

        self.selectCellWithTag_(selDate.dayOfMonth() + self._startOffset - 1)
        self.highlightTodayIfVisible()

    def highlightTodayIfVisible(self):
        now = Cocoa.NSCalendarDate.date()
        selDate = self.selectedDay()

        if (
            selDate.yearOfCommonEra() == now.yearOfCommonEra()
            and selDate.monthOfYear() == now.monthOfYear()
            and selDate.dayOfMonth() == now.dayOfMonth()
        ):
            aCell = self.cellWithTag_(now.dayOfMonth() + self._startOffset - 1)
            aCell.setHighlightsBy_(Cocoa.NSMomentaryChangeButton)
            aCell.setCellAttribute_to_(Cocoa.NSCellHighlighted, True)

    def awakeFromNib(self):
        self.setTarget_(self)
        self.setAction_("choseDay:")
        self.setAutosizesCells_(True)
        self.refreshCalendar()
        self.choseDay_(self)
