#!/usr/bin/env python3
"""Generate PeerSpeak's default notification chimes as small mono 16-bit WAVs.

Stdlib only (wave/struct/math) so there's no build-time dependency. Re-run to
regenerate the committed assets:

    python3 assets/sounds/generate_chimes.py

Each chime is a short sequence of sine notes with a soft attack (no click) and
an exponential decay (a gentle bell-like tail), plus a quiet second harmonic so
it reads as a "chime" rather than a flat beep. Distinct melodic shapes make the
events recognizable without looking: ascending = arrival, descending = leaving.
"""
import math
import os
import struct
import wave

RATE = 48000          # match PeerSpeak's audio graph
AMPLITUDE = 0.38      # clearly audible, not harsh (fraction of full scale)
ATTACK_S = 0.006      # short fade-in to kill the start click
DECAY = 7.0           # exponential decay rate (higher = shorter tail)

# Note frequencies (Hz)
G4, C5, E5, G5 = 392.00, 523.25, 659.25, 783.99


def note(freq, dur):
    """One enveloped note: fundamental + soft 2nd harmonic, attack + decay."""
    n = int(RATE * dur)
    out = []
    for i in range(n):
        t = i / RATE
        env = math.exp(-DECAY * t)
        if t < ATTACK_S:
            env *= t / ATTACK_S
        sample = math.sin(2 * math.pi * freq * t) + 0.3 * math.sin(2 * math.pi * 2 * freq * t)
        out.append(AMPLITUDE * env * sample / 1.3)
    return out


def chime(notes):
    """Concatenate (freq, dur) notes into one sample buffer."""
    buf = []
    for freq, dur in notes:
        buf.extend(note(freq, dur))
    return buf


def write_wav(path, samples):
    with wave.open(path, "w") as w:
        w.setnchannels(1)
        w.setsampwidth(2)
        w.setframerate(RATE)
        frames = b"".join(
            struct.pack("<h", max(-32767, min(32767, int(s * 32767)))) for s in samples
        )
        w.writeframes(frames)
    print(f"wrote {path} ({len(samples) / RATE * 1000:.0f} ms)")


CHIMES = {
    # You joined a room: bright ascending triad — welcoming.
    "self-join.wav": [(C5, 0.11), (E5, 0.11), (G5, 0.30)],
    # Someone else joined: gentle two-note rise — softer than your own join.
    "peer-join.wav": [(G4, 0.13), (C5, 0.28)],
    # Someone left: two-note fall — the mirror of an arrival.
    "peer-leave.wav": [(C5, 0.13), (G4, 0.28)],
    # Reconnecting to peer: soft single Bb4 note.
    "reconnect-attempt.wav": [(466.16, 0.20)],
    # Reconnected to peer: bright two-note rise (C5 -> G5).
    "reconnected.wav": [(C5, 0.10), (G5, 0.25)],
    # Left a room: descending triad.
    "self-leave.wav": [(G5, 0.10), (E5, 0.10), (C5, 0.25)],
    # Mute/unmute toggle: tiny clean blip.
    "mic-toggle.wav": [(E5, 0.08)],
    # Reconnect gave up: disappointing low two-note fall.
    "reconnect-failed.wav": [(C5, 0.15), (349.23, 0.30)],
    # Our chat message entered the room: a tiny bright acknowledgement.
    "chat-sent.wav": [(1046.50, 0.06)],
    # A peer message arrived: a soft two-note lift, distinct but unobtrusive.
    "chat-received.wav": [(E5, 0.07), (G5, 0.11)],
    # A saved contact came online: a light, higher two-note arrival.
    "contact-online.wav": [(E5, 0.09), (880.00, 0.18)],
    # A saved contact went offline: the same tonal family falling away.
    "contact-offline.wav": [(E5, 0.09), (440.00, 0.18)],
}


def main():
    here = os.path.dirname(os.path.abspath(__file__))
    for name, notes in CHIMES.items():
        write_wav(os.path.join(here, name), chime(notes))


if __name__ == "__main__":
    main()
