#!/usr/bin/env python3
import calendar
import datetime
from pathlib import Path

import gi

gi.require_version("Gtk", "3.0")
gi.require_version("Gdk", "3.0")
from gi.repository import Gdk, Gio, GLib, Gtk


APP_ID = "br.com.escolacelita.Calendar"
THEME_FILE = Path.home() / ".config" / "celita" / "theme"
MONTHS = (
    "Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho",
    "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro",
)
WEEKDAYS_LONG = (
    "segunda-feira", "terça-feira", "quarta-feira", "quinta-feira",
    "sexta-feira", "sábado", "domingo",
)
WEEKDAYS_SHORT = ("D", "S", "T", "Q", "Q", "S", "S")


def dark_theme_enabled():
    try:
        saved = THEME_FILE.read_text(encoding="utf-8").strip().lower()
        if saved in ("light", "dark"):
            return saved == "dark"
    except OSError:
        pass
    name = Gtk.Settings.get_default().get_property("gtk-theme-name") or ""
    return "dark" in name.lower()


class CalendarWindow(Gtk.ApplicationWindow):
    def __init__(self, application):
        super().__init__(application=application)
        today = datetime.date.today()
        self.shown_year = today.year
        self.shown_month = today.month
        self.selected = today
        self.allow_focus_close = False

        self.set_title("Data e hora")
        self.set_name("celita-calendar-window")
        self.set_decorated(False)
        self.set_resizable(False)
        self.set_skip_taskbar_hint(True)
        self.set_skip_pager_hint(True)
        self.set_keep_above(True)
        self.set_type_hint(Gdk.WindowTypeHint.POPUP_MENU)
        self.set_app_paintable(True)
        self.connect("key-press-event", self.on_key_press)
        self.connect("focus-out-event", self.on_focus_out)

        screen = self.get_screen()
        visual = screen.get_rgba_visual()
        if visual and screen.is_composited():
            self.set_visual(visual)

        self.provider = Gtk.CssProvider()
        self.provider.load_from_data(self.css().encode("utf-8"))
        Gtk.StyleContext.add_provider_for_screen(
            screen, self.provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
        )

        shell = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
        shell.get_style_context().add_class("calendar-shell")
        shell.set_size_request(390, 480)
        self.add(shell)

        header = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
        header.get_style_context().add_class("time-header")
        header.set_margin_start(28)
        header.set_margin_end(28)
        header.set_margin_top(22)
        header.set_margin_bottom(20)
        self.time_label = Gtk.Label(xalign=0)
        self.time_label.get_style_context().add_class("time-label")
        self.date_label = Gtk.Label(xalign=0)
        self.date_label.get_style_context().add_class("date-label")
        header.pack_start(self.time_label, False, False, 0)
        header.pack_start(self.date_label, False, False, 2)
        shell.pack_start(header, False, False, 0)

        divider = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL)
        divider.get_style_context().add_class("calendar-divider")
        shell.pack_start(divider, False, False, 0)

        month_row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=4)
        month_row.set_margin_start(24)
        month_row.set_margin_end(18)
        month_row.set_margin_top(14)
        month_row.set_margin_bottom(8)
        self.month_label = Gtk.Label(xalign=0)
        self.month_label.get_style_context().add_class("month-label")
        month_row.pack_start(self.month_label, True, True, 0)
        month_row.pack_end(self.nav_button("go-next-symbolic", 1), False, False, 0)
        month_row.pack_end(self.nav_button("go-previous-symbolic", -1), False, False, 0)
        shell.pack_start(month_row, False, False, 0)

        week_grid = Gtk.Grid(column_homogeneous=True)
        week_grid.set_margin_start(18)
        week_grid.set_margin_end(18)
        for column, title in enumerate(WEEKDAYS_SHORT):
            label = Gtk.Label(label=title)
            label.get_style_context().add_class("weekday")
            week_grid.attach(label, column, 0, 1, 1)
        shell.pack_start(week_grid, False, False, 0)

        self.days_grid = Gtk.Grid(column_homogeneous=True, row_homogeneous=True)
        self.days_grid.set_margin_start(18)
        self.days_grid.set_margin_end(18)
        self.days_grid.set_margin_bottom(14)
        shell.pack_start(self.days_grid, True, True, 0)

        self.update_clock()
        self.render_month()
        GLib.timeout_add_seconds(1, self.update_clock)

    @staticmethod
    def css():
        if dark_theme_enabled():
            return r"""
            #celita-calendar-window { background-color: transparent; }
            .calendar-shell {
                background-color: rgba(31, 35, 43, 0.99);
                color: #f4f6f8;
                border: 1px solid rgba(255,255,255,0.13);
                box-shadow: 0 10px 34px rgba(0,0,0,0.46);
            }
            .time-label { color: #f7f8fa; font-size: 42px; font-weight: 300; }
            .date-label { color: #71c2f5; font-size: 14px; }
            .calendar-divider { background: rgba(255,255,255,0.15); min-height: 1px; }
            .month-label { color: #f3f5f7; font-size: 15px; font-weight: 600; }
            .nav-button, .day-button {
                color: #f0f2f4; background: transparent; background-image: none;
                border: 0; border-radius: 0; box-shadow: none;
            }
            .nav-button { min-width: 36px; min-height: 30px; padding: 0; }
            .nav-button:hover, .day-button:hover { background: rgba(255,255,255,0.10); }
            .weekday { color: #d8dce1; font-size: 11px; padding: 6px 0; }
            .day-button { min-width: 42px; min-height: 38px; padding: 0; font-size: 12px; }
            .day-button.adjacent { color: #737a84; }
            .day-button.today { border: 2px solid #24a1e8; }
            .day-button.selected { color: #fff; background: #0878d1; }
            """
        return r"""
        #celita-calendar-window { background-color: transparent; }
        .calendar-shell {
            background-color: rgba(249, 250, 252, 0.99);
            color: #20242a;
            border: 1px solid rgba(30,36,44,0.18);
            box-shadow: 0 10px 34px rgba(0,0,0,0.24);
        }
        .time-label { color: #20242a; font-size: 42px; font-weight: 300; }
        .date-label { color: #0878d1; font-size: 14px; }
        .calendar-divider { background: rgba(30,36,44,0.16); min-height: 1px; }
        .month-label { color: #242930; font-size: 15px; font-weight: 600; }
        .nav-button, .day-button {
            color: #242930; background: transparent; background-image: none;
            border: 0; border-radius: 0; box-shadow: none;
        }
        .nav-button { min-width: 36px; min-height: 30px; padding: 0; }
        .nav-button:hover, .day-button:hover { background: rgba(21,29,38,0.08); }
        .weekday { color: #4f555d; font-size: 11px; padding: 6px 0; }
        .day-button { min-width: 42px; min-height: 38px; padding: 0; font-size: 12px; }
        .day-button.adjacent { color: #a6abb2; }
        .day-button.today { border: 2px solid #1687d9; }
        .day-button.selected { color: #fff; background: #0878d1; }
        """

    def nav_button(self, icon_name, direction):
        button = Gtk.Button()
        button.set_relief(Gtk.ReliefStyle.NONE)
        button.get_style_context().add_class("nav-button")
        button.set_image(Gtk.Image.new_from_icon_name(icon_name, Gtk.IconSize.MENU))
        button.connect("clicked", self.change_month, direction)
        return button

    def update_clock(self):
        now = datetime.datetime.now()
        self.time_label.set_text(now.strftime("%H:%M:%S"))
        weekday = WEEKDAYS_LONG[now.weekday()]
        self.date_label.set_text(
            f"{weekday}, {now.day} de {MONTHS[now.month - 1].lower()} de {now.year}"
        )
        return True

    def render_month(self):
        for child in self.days_grid.get_children():
            self.days_grid.remove(child)
        self.month_label.set_text(f"{MONTHS[self.shown_month - 1]} de {self.shown_year}")
        weeks = calendar.Calendar(firstweekday=6).monthdatescalendar(
            self.shown_year, self.shown_month
        )
        while len(weeks) < 6:
            last = weeks[-1][-1]
            weeks.append([last + datetime.timedelta(days=offset) for offset in range(1, 8)])
        today = datetime.date.today()
        for row, week in enumerate(weeks[:6]):
            for column, day in enumerate(week):
                button = Gtk.Button(label=str(day.day))
                button.set_relief(Gtk.ReliefStyle.NONE)
                context = button.get_style_context()
                context.add_class("day-button")
                if day.month != self.shown_month:
                    context.add_class("adjacent")
                if day == today:
                    context.add_class("today")
                if day == self.selected:
                    context.add_class("selected")
                button.connect("clicked", self.select_day, day)
                self.days_grid.attach(button, column, row, 1, 1)
        self.days_grid.show_all()

    def change_month(self, _button, direction):
        month_index = self.shown_year * 12 + self.shown_month - 1 + direction
        self.shown_year, month_zero = divmod(month_index, 12)
        self.shown_month = month_zero + 1
        self.render_month()

    def select_day(self, _button, day):
        self.selected = day
        self.shown_year = day.year
        self.shown_month = day.month
        self.render_month()

    def present_widget(self):
        display = Gdk.Display.get_default()
        monitor = display.get_primary_monitor() or display.get_monitor(0)
        workarea = monitor.get_workarea()
        width, height = 390, 480
        self.resize(width, height)
        self.move(
            workarea.x + workarea.width - width - 8,
            workarea.y + workarea.height - height - 8,
        )
        self.show_all()
        self.present()
        self.allow_focus_close = False
        GLib.timeout_add(500, self.enable_focus_close)

    def enable_focus_close(self):
        self.allow_focus_close = True
        return False

    def on_key_press(self, _widget, event):
        if event.keyval == Gdk.KEY_Escape:
            self.destroy()
            return True
        return False

    def on_focus_out(self, *_args):
        GLib.timeout_add(150, self.close_if_unfocused)
        return False

    def close_if_unfocused(self):
        if self.allow_focus_close and self.get_visible() and not self.is_active():
            self.destroy()
        return False


class CalendarApplication(Gtk.Application):
    def __init__(self):
        super().__init__(application_id=APP_ID, flags=Gio.ApplicationFlags.FLAGS_NONE)
        self.window = None

    def do_activate(self):
        if self.window is not None and self.window.get_visible():
            self.window.destroy()
            return
        self.window = CalendarWindow(self)
        self.window.connect("destroy", self.window_destroyed)
        self.window.present_widget()

    def window_destroyed(self, *_args):
        self.window = None


if __name__ == "__main__":
    raise SystemExit(CalendarApplication().run(None))
