# -*- coding: utf-8 -*-

import socketio
import sounddevice as sd
import subprocess
import threading
import queue
import sys

# --- 設定 ---
SERVER_URL = 'wss://www.winway.tw:8765'
SAMPLE_RATE = 48000  # 播放的取樣率，必須與 ffmpeg 輸出一致
CHANNELS = 1         # 聲道數 (單聲道)
DTYPE = 'int16'      # 音訊資料型別，必須與 ffmpeg 輸出一致

# --- FFmpeg 指令 ---
# -f webm: 明確指定輸入格式為 WebM (瀏覽器 MediaRecorder 常見格式)，避免 ffmpeg 猜測失敗
# -i pipe:0: 從標準輸入讀取資料
# -f s16le: 輸出格式為 signed 16-bit little-endian PCM
# -ar {SAMPLE_RATE}: 設定音訊取樣率
# -ac {CHANNELS}: 設定聲道數
# -: 將結果輸出到標準輸出
ffmpeg_command = [
    'ffmpeg',
    '-f', 'webm',
    '-i', 'pipe:0',
    '-f', 's16le',
    '-ar', str(SAMPLE_RATE),
    '-ac', str(CHANNELS),
    '-'
]

# 用於在執行緒之間傳遞解碼後音訊的佇列
audio_queue = queue.Queue()

def play_audio_from_queue():
    """
    此函式在獨立的執行緒中執行。
    它從佇列中讀取解碼後的音訊並透過 sounddevice 播放。
    """
    with sd.RawOutputStream(samplerate=SAMPLE_RATE, channels=CHANNELS, dtype=DTYPE) as stream:
        print("✅ 音訊輸出裝置已就緒，等待音訊資料...")
        while True:
            data = audio_queue.get()
            if data is None:  # 收到 None 時，表示串流結束
                break
            stream.write(data)
            # print(f"🎧 正在播放 {len(data)} bytes 的音訊...") # 取消註解此行可看見詳細播放日誌
        print("⏹️ 音訊輸出串流結束。")

def main():
    sio = socketio.Client()

    try:
        print("▶️  正在啟動 ffmpeg 解碼程序...")
        ffmpeg_process = subprocess.Popen(
            ffmpeg_command,
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE # 捕獲錯誤輸出以便除錯
        )
        print("✅ ffmpeg 程序已啟動。")
    except FileNotFoundError:
        print("\n" + "="*60)
        print("❌ 錯誤: 找不到 'ffmpeg' 指令。")
        print("   請先在您的作業系統中安裝 ffmpeg，並確保其路徑已加入環境變數。")
        print("   - Windows: https://ffmpeg.org/download.html")
        print("   - macOS (Homebrew): brew install ffmpeg")
        print("   - Linux (apt): sudo apt-get install ffmpeg")
        print("="*60 + "\n")
        return

    # 啟動音訊播放執行緒
    player_thread = threading.Thread(target=play_audio_from_queue)
    player_thread.daemon = True
    player_thread.start()

    # 啟動一個執行緒來讀取 ffmpeg 的輸出，並放入佇列
    def read_ffmpeg_output():
        while True:
            decoded_data = ffmpeg_process.stdout.read(2048) # 讀取解碼後的 PCM 資料
            if not decoded_data:
                break
            # print(f"⚙️ FFmpeg 解碼輸出 {len(decoded_data)} bytes...") # 取消註解此行可看見詳細解碼日誌
            audio_queue.put(decoded_data)
        # 檢查 ffmpeg 是否有任何錯誤訊息
        stderr_output = ffmpeg_process.stderr.read()
        if stderr_output:
            print(f"\n--- FFmpeg 錯誤訊息 ---\n{stderr_output.decode(sys.stderr.encoding, errors='ignore')}")
        print("⏹️ FFmpeg 輸出讀取結束。")

    ffmpeg_reader_thread = threading.Thread(target=read_ffmpeg_output)
    ffmpeg_reader_thread.daemon = True
    ffmpeg_reader_thread.start()

    @sio.event
    def connect():
        print(f"✅ 成功連接到伺服器，SID: {sio.sid}")

    @sio.event
    def connect_error(data):
        print(f"❌ 連接失敗: {data}")

    @sio.event
    def disconnect():
        print("🔌 已從伺服器斷開。")

    @sio.on('audio_broadcast')
    def on_audio_broadcast(data):
        """核心函式：收到廣播的音訊後，將其寫入 ffmpeg 程序進行解碼。"""
        print(f"📡 收到 {len(data)} bytes 的廣播音訊資料。")
        if ffmpeg_process.stdin.closed:
            return # No need to ack if we're closed
        try:
            ffmpeg_process.stdin.write(data)
        except (BrokenPipeError, OSError):
            print("⚠️ ffmpeg 程序已終止，無法寫入更多資料。")
            return # No need to ack if we're closed
        # Acknowledge receipt to the server. This is crucial for the server-side
        # logic that waits for the header to be delivered before adding a new
        # client to the main broadcast room.
        return True

    try:
        print(f"🔗 正在連接到 {SERVER_URL}...")
        # Add a timeout to prevent hanging and catch the specific error
        sio.connect(SERVER_URL, transports=['websocket'], wait_timeout=10)
        sio.wait()
    except socketio.exceptions.ConnectionError as e:
        print(f"\n" + "="*60)
        print(f"❌ 連接伺服器失敗: {e}")
        print(f"   請檢查伺服器 ({SERVER_URL}) 是否正在運行，")
        print(f"   以及網路連線是否正常。")
        print(f"   如果伺服器有 CORS 設定，請確認客戶端已被允許連線。")
        print("="*60 + "\n")
    except KeyboardInterrupt:
        print("\n🛑 使用者中斷程式。")
    finally:
        print("🧹 正在清理資源...")
        if 'ffmpeg_process' in locals() and ffmpeg_process.poll() is None:
            ffmpeg_process.stdin.close() # 關閉 ffmpeg 的輸入，讓它自然結束
            ffmpeg_process.wait()
        audio_queue.put(None) # 通知播放執行緒結束
        player_thread.join()
        if sio.connected:
            sio.disconnect()
        print("👋 程式結束。")

if __name__ == '__main__':
    main()