"""Standalone Windows-volume worker — run as a SUBPROCESS by wivexx_api so that a native
pycaw/COM access-violation (a dead/odd default playback device can trigger one) only kills
THIS process, never the main Wivexx app. It prints exactly one confirmation line on stdout
and exits 0; on any failure it prints `ERR:<reason>` and exits 1.

Usage:  python wivexx_volume_worker.py "<command text>"
"""
import re
import sys


def _iface():
    """A pycaw master-volume interface for the default playback device. No manual
    CoInitialize — comtypes lazily inits the (STA) apartment, which is what works for the
    audio endpoint API; forcing MTA caused RPC_E_CHANGED_MODE + flaky behaviour."""
    from ctypes import cast, POINTER
    from pycaw.pycaw import AudioUtilities, IAudioEndpointVolume
    devices = AudioUtilities.GetSpeakers()
    if hasattr(devices, "Activate"):                    # legacy pycaw
        import comtypes
        interface = devices.Activate(IAudioEndpointVolume._iid_, comtypes.CLSCTX_ALL, None)
    else:                                               # modern pycaw exposes it directly
        interface = devices.EndpointVolume
    return cast(interface, POINTER(IAudioEndpointVolume))


def do_volume(vol, t):
    """Apply the volume command in `t` (lower-cased) and return a spoken confirmation."""
    # ── Mute / unmute ────────────────────────────────────────────────────────
    if re.search(r"\bunmute\b", t):
        vol.SetMute(0, None)
        return "Audio unmuted, sir."
    if (re.search(r"\b(mute|silence|quiet)\b", t)
            or re.search(r"\bcut\s+(?:the\s+|my\s+)?(?:sound|audio|volume|music)\b", t)):  # v1.53
        vol.SetMute(1, None)
        return "Audio muted, sir."

    # ── Absolute presets ─────────────────────────────────────────────────────
    if re.search(r"\b(max|maximum|maximise|maximize|full)\b", t):
        vol.SetMute(0, None)
        vol.SetMasterVolumeLevelScalar(1.0, None)
        return "Volume at maximum, sir."
    if re.search(r"\bhalf\b", t):
        vol.SetMute(0, None)
        vol.SetMasterVolumeLevelScalar(0.5, None)
        return "Volume set to half, sir."
    if re.search(r"\b(min|minimum)\b", t):
        vol.SetMasterVolumeLevelScalar(0.0, None)
        return "Volume at minimum, sir."

    # ── Relative / explicit-percentage ───────────────────────────────────────
    cur = float(vol.GetMasterVolumeLevelScalar())
    num_m = re.search(r"(\d{1,3})(?:\s*percent|%|\b)", t)
    pct = max(0, min(100, int(num_m.group(1)))) if num_m else None

    wants_set = bool(re.search(r"\b(set|to|at|volume\s+\d+|house\s+volume|make\s+it)\b", t))
    wants_up = bool(re.search(r"\b(up|raise|increase|boost|louder|higher)\b", t))
    wants_down = bool(re.search(r"\b(down|lower|decrease|reduce|drop|quieter|softer|tour\s+down)\b", t))

    delta = 0.10
    if re.search(r"\b(a\s+lot|way)\b", t):
        delta = 0.25
    elif re.search(r"\b(a\s+bit|a\s+little|slightly)\b", t):
        delta = 0.05
    elif pct is not None:
        delta = pct / 100.0

    if wants_set and pct is not None and not (wants_up or wants_down):
        vol.SetMute(0, None)
        vol.SetMasterVolumeLevelScalar(pct / 100.0, None)
        return "Volume set to %d percent, sir." % pct
    if wants_up:
        new = min(1.0, cur + delta)
        vol.SetMute(0, None)
        vol.SetMasterVolumeLevelScalar(new, None)
        return "Volume raised to %d percent, sir." % round(new * 100)
    if wants_down:
        new = max(0.0, cur - delta)
        vol.SetMasterVolumeLevelScalar(new, None)
        return "Volume lowered to %d percent, sir." % round(new * 100)
    if pct is not None:
        vol.SetMute(0, None)
        vol.SetMasterVolumeLevelScalar(pct / 100.0, None)
        return "Volume set to %d percent, sir." % pct

    return "The volume is at %d percent, sir." % round(cur * 100)


def main():
    text = (sys.argv[1] if len(sys.argv) > 1 else "").lower()
    vol = _iface()
    sys.stdout.write(do_volume(vol, text))
    sys.stdout.flush()


if __name__ == "__main__":
    try:
        main()
    except Exception as e:
        sys.stdout.write("ERR:" + str(e)[:160])
        sys.exit(1)
