#!/usr/bin/env python3
"""
G1 Interactive Voice Player
----------------------------
Lists WAV files and lets you pick which ones to play on the G1 built-in speaker.
Uses _CallRequestWithParamAndBin with format params (the working method).

Usage (run ON the robot):
  python3 g1_interactive_player.py "/home/unitree/SanadVoice/recorded voices"
  python3 g1_interactive_player.py "/home/unitree/SanadVoice/recorded voices" --volume 80
"""

import sys
import time
import wave
import json
import argparse
import numpy as np
from pathlib import Path
from unitree_sdk2py.core.channel import ChannelFactoryInitialize
from unitree_sdk2py.g1.audio.g1_audio_client import AudioClient
from unitree_sdk2py.g1.audio.g1_audio_api import (
    ROBOT_API_ID_AUDIO_START_PLAY,
    ROBOT_API_ID_AUDIO_STOP_PLAY,
)

TARGET_RATE = 16000
APP_NAME = "p"


def load_wav(path: str):
    with wave.open(path, "rb") as wf:
        rate = wf.getframerate()
        nch = wf.getnchannels()
        audio = np.frombuffer(wf.readframes(wf.getnframes()), dtype=np.int16)

    if nch == 2:
        audio = audio.reshape(-1, 2).mean(axis=1).astype(np.int16)

    if rate != TARGET_RATE:
        tl = int(len(audio) * TARGET_RATE / rate)
        audio = np.interp(
            np.linspace(0, len(audio), tl, endpoint=False),
            np.arange(len(audio)),
            audio.astype(np.float64),
        ).astype(np.int16)

    return audio


def play_pcm(client, pcm: np.ndarray):
    # Single call with a unique stream_id — chunked repeats with the same sid
    # are dropped by the audio service, per voice_note.txt.
    client._Call(ROBOT_API_ID_AUDIO_STOP_PLAY, json.dumps({"app_name": APP_NAME}))
    time.sleep(0.2)

    pcm_bytes = pcm.tobytes()
    duration = len(pcm) / TARGET_RATE
    sid = f"s_{int(time.time() * 1000)}"
    param = json.dumps({
        "app_name": APP_NAME,
        "stream_id": sid,
        "sample_rate": TARGET_RATE,
        "channels": 1,
        "bits_per_sample": 16,
    })

    start = time.time()
    client._CallRequestWithParamAndBin(
        ROBOT_API_ID_AUDIO_START_PLAY, param, list(pcm_bytes)
    )
    time.sleep(duration + 0.3)
    client._Call(ROBOT_API_ID_AUDIO_STOP_PLAY, json.dumps({"app_name": APP_NAME}))
    return time.time() - start


def show_menu(wavs):
    print(f"\n{'#':<4} {'Name':<45} {'Duration':>8}")
    print("-" * 59)
    for i, (p, dur) in enumerate(wavs):
        print(f"{i:<4} {p.stem:<45} {dur:>6.1f}s")
    print("-" * 59)
    print(f"{'a':<4} {'Play ALL':<45}")
    print(f"{'m':<4} {'Show menu':<45}")
    print(f"{'q':<4} {'Quit':<45}")
    print()


def main():
    parser = argparse.ArgumentParser(description="G1 Interactive Voice Player")
    parser.add_argument("directory", help="Directory containing WAV files")
    parser.add_argument("--volume", "-v", type=int, default=100, help="Volume 0-100")
    parser.add_argument("--interface", "-i", default="eth0", help="Network interface")

    args = parser.parse_args()

    wav_dir = Path(args.directory)
    if not wav_dir.is_dir():
        print(f"Not a directory: {wav_dir}")
        sys.exit(1)

    wav_files = []
    for p in sorted(wav_dir.glob("*.wav")):
        try:
            with wave.open(str(p), "rb") as wf:
                dur = wf.getnframes() / wf.getframerate()
            wav_files.append((p, dur))
        except Exception:
            pass

    if not wav_files:
        print(f"No WAV files found in {wav_dir}")
        sys.exit(1)

    print(f"Connecting to G1 speaker...")
    ChannelFactoryInitialize(0, args.interface)
    client = AudioClient()
    client.SetTimeout(10.0)
    client.Init()
    client.SetVolume(args.volume)
    print(f"Ready. Volume: {args.volume}%")

    show_menu(wav_files)

    while True:
        try:
            choice = input("Pick a number (or a/m/q): ").strip().lower()
        except (KeyboardInterrupt, EOFError):
            print("\nBye.")
            break

        if choice == "q":
            print("Bye.")
            break

        if choice == "m":
            show_menu(wav_files)
            continue

        if choice == "a":
            print(f"\nPlaying all {len(wav_files)} files...\n")
            for i, (p, dur) in enumerate(wav_files):
                print(f"  [{i+1}/{len(wav_files)}] {p.stem} ({dur:.1f}s)...", end=" ", flush=True)
                pcm = load_wav(str(p))
                elapsed = play_pcm(client, pcm)
                print(f"done ({elapsed:.1f}s)")
                time.sleep(0.5)
            print("\nAll done.\n")
            continue

        indices = []
        for part in choice.replace(" ", "").split(","):
            if "-" in part:
                try:
                    a, b = part.split("-", 1)
                    indices.extend(range(int(a), int(b) + 1))
                except ValueError:
                    pass
            else:
                try:
                    indices.append(int(part))
                except ValueError:
                    pass

        if not indices:
            print("Invalid. Enter number, range (2-5), list (1,3,5), a, m, or q.")
            continue

        for idx in indices:
            if idx < 0 or idx >= len(wav_files):
                print(f"  #{idx} out of range, skipping.")
                continue

            p, dur = wav_files[idx]
            print(f"  Playing: {p.stem} ({dur:.1f}s)...", end=" ", flush=True)
            pcm = load_wav(str(p))
            elapsed = play_pcm(client, pcm)
            print(f"done ({elapsed:.1f}s)")

        print()


if __name__ == "__main__":
    main()
