# vi: filetype=python
import sys
import subprocess
from enum import Enum
from threading import Thread
from PyQt6.QtWidgets import (
    QApplication,
    QSystemTrayIcon,
    QMenu,
    QWidget,
    QVBoxLayout,
    QMessageBox,
)
from PyQt6.QtGui import QIcon, QPixmap, QColor, QAction, QPainter
from PyQt6.QtCore import QTimer, Qt


class State(Enum):
    IDLE = 0
    PENDING = 1
    BREAK = 2


S = 1000
M = 60 * S

DURATION = {
    State.IDLE: 15*M,
    State.PENDING: 1*M,
    State.BREAK: 15*S,
}
TOOLTIP = {
    State.IDLE: "Waiting for next break",
    State.PENDING: "Click to take a break",
    State.BREAK: "Taking a break",
}
COLOR = {
    State.IDLE: "#608c58",
    State.PENDING: "#d32a5d",
    State.BREAK: "#7e95d3",
    QWidget: "rgba(40, 20, 40, 200)",
}
DONE = '/run/current-system/sw/share/sounds/freedesktop/stereo/complete.oga'
POP = '/run/current-system/sw/share/sounds/freedesktop/stereo/message.oga'
audio = True


def m_ceil(n):
    return (n + M - 1) // M


def circle_icon(color, text=None):
    pixmap = QPixmap(64, 64)
    pixmap.fill(Qt.GlobalColor.transparent)

    painter = QPainter(pixmap)
    painter.setRenderHint(QPainter.RenderHint.Antialiasing)

    painter.setBrush(QColor(color))
    painter.setPen(Qt.PenStyle.NoPen)
    painter.drawEllipse(2, 2, 60, 60)

    if text:
        painter.setPen(QColor("white" if color != "white" else "black"))
        font = painter.font()
        font.setPixelSize(34)
        font.setBold(True)
        painter.setFont(font)
        painter.drawText(pixmap.rect(), Qt.AlignmentFlag.AlignCenter, text)
    painter.end()
    return QIcon(pixmap)


def play_sound(sound=DONE, n=1):
    global audio
    audio = True

    def play():
        for _ in range(n):
            if audio:
                subprocess.call(['paplay', sound])
    Thread(target=play).start()


class BreakTimer(QSystemTrayIcon):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.state = State.IDLE

        self.timer = QTimer(self)
        self.timer.timeout.connect(self.on_timeout)
        self.activated.connect(self.on_tray_click)
        self.icon_color = ""
        self.icon_text = ""
        self.color = ""
        self.text = ""
        self.icon_timer = QTimer(self)
        self.icon_timer.timeout.connect(self.update_ui)
        self.icon_timer.start(S)

        self.overlay = TransparentOverlay()
        screen_geometry = app.primaryScreen().virtualGeometry()
        self.overlay.setGeometry(screen_geometry)

        menu = QMenu()
        self.p_ac = QAction("Pause Timer", menu)
        self.p_ac.triggered.connect(self.pause_timers)
        menu.addAction(self.p_ac)
        b_ac = QAction("Break Now", menu)
        b_ac.triggered.connect(self.start_break)
        menu.addAction(b_ac)
        q_ac = QAction("Quit", menu)
        q_ac.triggered.connect(sys.exit)
        menu.addAction(q_ac)
        self.setContextMenu(menu)

        self.start_idle()

    def update_icon(self):
        if self.color != self.icon_color or self.text != self.icon_text:
            self.icon_color = self.color
            self.icon_text = self.text
            self.setIcon(circle_icon(self.color, self.text))

    def update_ui(self):
        if self.state is State.IDLE:
            self.text = str(m_ceil(self.timer.remainingTime()) or self.text)
            self.update_icon()
        elif self.state is State.PENDING:
            if self.color == "white":
                self.color = COLOR[State.PENDING]
            else:
                self.color = "white"
            self.update_icon()

    def set_state(self, state, text):
        global audio
        audio = False
        self.state = state
        self.color = COLOR[self.state]
        self.text = str(text)
        self.update_icon()
        self.setToolTip(TOOLTIP[self.state])
        self.timer.start(DURATION[self.state])

    def start_idle(self):
        self.set_state(State.IDLE, DURATION[State.IDLE]//M)

    def start_break(self):
        self.set_state(State.BREAK, "...")
        self.overlay.show()
        self.overlay.grabKeyboard()

    def on_timeout(self):
        if self.state is State.IDLE:
            self.set_state(State.PENDING, 0)
            play_sound()
        elif self.state is State.PENDING:
            n = int(self.text or 0) + 1
            self.text = str(n)
            self.update_icon()
            if n < 5:
                play_sound(n=n)
            else:
                self.inactive_popup()
        elif self.state is State.BREAK:
            self.overlay.hide()
            self.start_idle()
            play_sound(POP)
        self.showMessage('Timer', 'Timer is complete')

    def inactive_popup(self):
        self.pause_timers()
        popup = QMessageBox()
        popup.setWindowTitle("Break Timer")
        popup.setText("Timer paused due to inactivity")
        resume = popup.addButton("Resume", QMessageBox.ButtonRole.AcceptRole)
        resume.clicked.connect(self.start_idle)
        quit = popup.addButton("Quit", QMessageBox.ButtonRole.RejectRole)
        quit.clicked.connect(sys.exit)
        popup.exec()

    def on_tray_click(self, reason):
        if reason == QSystemTrayIcon.ActivationReason.Trigger:
            if self.state is State.PENDING:
                self.start_break()

    def pause_timers(self):
        if self.timer.isActive():
            self.remaining_time = self.timer.remainingTime()
            self.color = "gray"
            self.update_icon()
            self.timer.stop()
            self.icon_timer.stop()
            self.p_ac.setText("Resume Timer")
        else:
            self.color = COLOR[self.state]
            self.update_icon()
            self.timer.start(self.remaining_time)
            self.icon_timer.start()
            self.p_ac.setText("Pause Timer")


class TransparentOverlay(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowFlags(Qt.WindowType.BypassWindowManagerHint)
        self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        bg = QWidget()
        bg.setStyleSheet(f"background-color: {COLOR[QWidget]};")
        layout.addWidget(bg)
        self.setLayout(layout)

    def keyPressEvent(self, event):
        if event.key() == Qt.Key.Key_Escape:
            self.hide()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    app.setQuitOnLastWindowClosed(False)
    reminder = BreakTimer()
    reminder.show()
    sys.exit(app.exec())
