#!/usr/bin/python3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GLib, Pango

GST_OK = False
try:
    gi.require_version('Gst', '1.0')
    from gi.repository import Gst
    Gst.init(None)
    GST_OK = True
except Exception:
    pass

def play_beep():
    if not GST_OK: return
    try:
        pipe = Gst.parse_launch('audiotestsrc freq=880 num-buffers=8 ! audioconvert ! autoaudiosink')
        pipe.set_state(Gst.State.PLAYING)
        GLib.timeout_add(500, lambda: pipe.set_state(Gst.State.NULL) or False)
    except Exception: pass

class App(Gtk.Window):
    def __init__(self):
        super().__init__(title='Audio Timer')
        self.set_default_size(380, 280)
        self.running = False
        self.remaining = 0
        self.timer_id = None
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
        vbox.set_margin_top(16); vbox.set_margin_bottom(16)
        vbox.set_margin_start(16); vbox.set_margin_end(16)
        self.add(vbox)
        # spinbuttons
        spin_box = Gtk.Box(spacing=10)
        spin_box.set_halign(Gtk.Align.CENTER)
        self.h_spin = Gtk.SpinButton.new_with_range(0, 23, 1)
        self.m_spin = Gtk.SpinButton.new_with_range(0, 59, 1)
        self.s_spin = Gtk.SpinButton.new_with_range(0, 59, 1)
        self.m_spin.set_value(1)
        for s, lbl in [(self.h_spin,'h'), (self.m_spin,'m'), (self.s_spin,'s')]:
            s.set_width_chars(3)
            spin_box.pack_start(s, False, False, 0)
            spin_box.add(Gtk.Label(label=lbl))
        vbox.pack_start(spin_box, False, False, 0)
        # display
        self.display = Gtk.Label()
        self.display.override_font(Pango.FontDescription('Monospace Bold 42'))
        self.display.set_text('01:00:00')
        vbox.pack_start(self.display, True, True, 0)
        # buttons
        btn_box = Gtk.Box(spacing=10)
        btn_box.set_halign(Gtk.Align.CENTER)
        self.start_btn = Gtk.Button(label='▶ Start')
        self.start_btn.connect('clicked', self.toggle)
        btn_box.pack_start(self.start_btn, False, False, 0)
        reset_btn = Gtk.Button(label='↺ Reset')
        reset_btn.connect('clicked', self.reset)
        btn_box.pack_start(reset_btn, False, False, 0)
        vbox.pack_start(btn_box, False, False, 0)
        self.status = Gtk.Label(label='Set time and press Start')
        vbox.pack_start(self.status, False, False, 0)
    def toggle(self, *_):
        if not self.running:
            h = int(self.h_spin.get_value())
            m = int(self.m_spin.get_value())
            s = int(self.s_spin.get_value())
            self.remaining = h*3600 + m*60 + s
            if self.remaining == 0: return
            self.running = True
            self.start_btn.set_label('⏹ Stop')
            self.timer_id = GLib.timeout_add(1000, self.tick)
        else:
            self.running = False
            self.start_btn.set_label('▶ Start')
            if self.timer_id: GLib.source_remove(self.timer_id)
            self.status.set_text('Stopped')
    def tick(self):
        if not self.running: return False
        self.remaining -= 1
        h, rem = divmod(self.remaining, 3600)
        m, s = divmod(rem, 60)
        self.display.set_text(f'{h:02d}:{m:02d}:{s:02d}')
        if self.remaining <= 0:
            self.running = False
            self.start_btn.set_label('▶ Start')
            self.status.set_markup('<span foreground="red" weight="bold">Time\'s up!</span>')
            play_beep()
            return False
        return True
    def reset(self, *_):
        self.running = False
        if self.timer_id: GLib.source_remove(self.timer_id)
        self.start_btn.set_label('▶ Start')
        self.status.set_text('Set time and press Start')
        m = int(self.m_spin.get_value())
        self.display.set_text(f'00:{m:02d}:00')

win = App()
win.connect('destroy', Gtk.main_quit)
win.show_all()
Gtk.main()
