"""wivexx_splash.py — Wivexx launch splash (v1.18)

A tiny, stdlib-only splash window so launching Wivexx feels instant instead of
~20 s of nothing while the heavy modules (Whisper, torch, the Claude client) and
the rest of boot load.  It is spawned as a SEPARATE process at the very top of
wivexx_api.py — before those heavy imports — so it can paint immediately.

It shows the living DOT + the `wivexx.` wordmark + a cycling status line, and
closes the moment the real GUI is ready (a sentinel file path is passed as argv[1]
and written by wivexx_gui once the main window is up), or after a safety timeout.

Imports tkinter + stdlib only — nothing that takes time to load.
"""
import math
import os
import random
import sys
import time
import tkinter as tk
import tkinter.font as tkfont

INK   = "#0D0D0D"
VOLT  = "#C1F73A"
BONE  = "#F2F0EA"
STONE = "#8A8A85"
LINE  = "#262624"

CLOSE_FILE = sys.argv[1] if len(sys.argv) > 1 else os.path.join(
    os.environ.get("TEMP", "."), "wivexx_splash.close")
MAX_SECONDS = 75.0          # never hang past this even if the signal never comes
W, H = 340, 300

_MESSAGES = ["waking up", "loading voice", "tuning the mic", "almost there"]


def _dim(rgb, a):
    r, g, b = rgb
    ir, ig, ib = (13, 13, 13)
    return "#%02x%02x%02x" % (int(r * a + ir * (1 - a)),
                              int(g * a + ig * (1 - a)),
                              int(b * a + ib * (1 - a)))


class Splash:
    def __init__(self) -> None:
        self.root = tk.Tk()
        self.root.overrideredirect(True)
        try:
            self.root.attributes("-topmost", True)
        except Exception:
            pass
        sw = self.root.winfo_screenwidth()
        sh = self.root.winfo_screenheight()
        self.root.geometry(f"{W}x{H}+{(sw - W)//2}+{(sh - H)//2}")
        self.root.configure(bg=LINE)            # 1px hairline frame
        self.cv = tk.Canvas(self.root, bg=INK, highlightthickness=0)
        self.cv.place(x=1, y=1, width=W - 2, height=H - 2)

        # draggable (borderless windows have no title bar to grab)
        self._drag_dx = 0
        self._drag_dy = 0
        for w in (self.root, self.cv):
            w.bind("<Button-1>", self._start_move)
            w.bind("<B1-Motion>", self._on_move)

        # wordmark font (measured so `wivexx.` centres as one block)
        self.wm_font = tkfont.Font(family="Segoe UI", size=20, weight="bold")
        self._w_word = self.wm_font.measure("wivexx")
        self._w_dot = self.wm_font.measure(".")

        self.t0 = time.time()
        self.phase = 0.0
        self.blink_until = 0.0
        self.next_blink = time.time() + 2.0
        self.dash = 0.0
        self.fade = 1.0
        self.closing = False
        self._tick()
        self.root.mainloop()

    def _start_move(self, e) -> None:
        self._drag_dx = e.x_root - self.root.winfo_rootx()
        self._drag_dy = e.y_root - self.root.winfo_rooty()

    def _on_move(self, e) -> None:
        self.root.geometry(f"+{e.x_root - self._drag_dx}+{e.y_root - self._drag_dy}")

    def _tick(self) -> None:
        now = time.time()
        # close when the main GUI signals ready, or after the safety timeout
        if not self.closing and (os.path.exists(CLOSE_FILE)
                                 or now - self.t0 > MAX_SECONDS):
            self.closing = True
        if self.closing:
            self.fade -= 0.08
            try:
                self.root.attributes("-alpha", max(0.0, self.fade))
            except Exception:
                pass
            if self.fade <= 0.0:
                try:
                    self.root.destroy()
                except Exception:
                    pass
                return

        self._draw(now)
        try:
            self.root.after(33, self._tick)
        except Exception:
            pass

    def _draw(self, now: float) -> None:
        cv = self.cv
        cv.delete("all")
        cx, cy = W / 2, H / 2 - 26
        base_r = 30

        self.phase += 0.05
        breathe = math.sin(self.phase)
        bob = breathe * base_r * 0.10
        squash = breathe * 0.06
        rx = base_r * (1 + squash)
        ry = base_r * (1 - squash)
        dy = cy + bob

        if now >= self.next_blink:
            self.blink_until = now + 0.12
            self.next_blink = now + random.uniform(2.5, 5.0)

        # ground reflection
        for k, a in ((1.0, 0.06), (0.7, 0.12)):
            cv.create_oval(cx - base_r * 1.5 * k, cy + base_r * 1.2 - 9 * k,
                           cx + base_r * 1.5 * k, cy + base_r * 1.2 + 9 * k,
                           fill=_dim((193, 247, 58), a), outline="")
        # glow
        for k, a in ((1.6, 0.06), (1.3, 0.10), (1.12, 0.18)):
            cv.create_oval(cx - rx * k, dy - ry * k, cx + rx * k, dy + ry * k,
                           fill=_dim((193, 247, 58), a), outline="")
        # the DOT
        cv.create_oval(cx - rx, dy - ry, cx + rx, dy + ry, fill=VOLT, outline="")
        # highlight / blink
        if now >= self.blink_until:
            hl = rx * 0.26
            cv.create_oval(cx - rx * 0.28 - hl, dy - ry * 0.32 - hl,
                           cx - rx * 0.28 + hl, dy - ry * 0.32 + hl,
                           fill=_dim((242, 240, 234), 0.85), outline="")
        else:
            cv.create_rectangle(cx - rx, dy - ry * 0.14, cx + rx, dy + ry * 0.14,
                                fill=INK, outline="")

        # wordmark — centred as one block (`wivexx` + volt `.`)
        total = self._w_word + self._w_dot
        x0 = cx - total / 2
        ty = cy + base_r + 26
        cv.create_text(x0, ty, text="wivexx", fill=BONE,
                       font=self.wm_font, anchor="w")
        cv.create_text(x0 + self._w_word, ty, text=".", fill=VOLT,
                       font=self.wm_font, anchor="w")

        # cycling status line
        msg = _MESSAGES[int((now - self.t0) / 2.0) % len(_MESSAGES)]
        cv.create_text(cx, H - 56, text=msg + "…", fill=STONE,
                       font=("Consolas", 10), anchor="c")

        # thin indeterminate volt progress bar
        self.dash = (self.dash + 4) % (W - 80)
        bx0, bx1, by = 40, W - 40, H - 34
        cv.create_line(bx0, by, bx1, by, fill=LINE, width=2)
        seg = 70
        x0 = bx0 + self.dash
        x1 = min(bx1, x0 + seg)
        cv.create_line(x0, by, x1, by, fill=VOLT, width=2)


if __name__ == "__main__":
    try:
        Splash()
    except Exception:
        pass
