import asyncio
import serial
from winsdk.windows.media.control import GlobalSystemMediaTransportControlsSessionManager
from ctypes import cast, POINTER
from comtypes import CLSCTX_ALL
from pycaw.pycaw import AudioUtilities, IAudioEndpointVolume
import atexit
import sys

# Serial setup
SERIAL_PORT = "COM8"
BAUD_RATE = 9600
ser = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=0.1)

# --- Global Volume Interface ---
volume_interface = None

def init_volume():
    global volume_interface
    devices = AudioUtilities.GetSpeakers()
    interface = devices.Activate(IAudioEndpointVolume._iid_, CLSCTX_ALL, None)
    volume_interface = cast(interface, POINTER(IAudioEndpointVolume))
    return volume_interface

def release_volume():
    global volume_interface
    if volume_interface:
        try:
            volume_interface.Release()
            volume_interface = None
            print("[DEBUG] Volume interface released cleanly")
        except:
            pass

atexit.register(release_volume)
init_volume()  # Initialize global volume interface

# Volume control functions
def get_volume():
    return volume_interface.GetMasterVolumeLevelScalar()

def set_volume(vol):
    vol = max(0.0, min(1.0, vol))
    volume_interface.SetMasterVolumeLevelScalar(vol, None)

def change_volume(delta):
    set_volume(get_volume() + delta)
    print(f"[DEBUG] Volume changed to {int(get_volume() * 100)}%")

def set_volume_from_arduino(vol):
    """Set system volume from Arduino VOLUME:<num> message"""
    vol = max(0, min(100, vol))  # clamp 0-100
    set_volume(vol / 100)
    print(f"[DEBUG] Volume set from Arduino: {vol}%")

# Media control
async def get_session():
    manager = await GlobalSystemMediaTransportControlsSessionManager.request_async()
    return manager.get_current_session()

async def get_media_info(session):
    if session is None:
        return None, None
    info = await session.try_get_media_properties_async()
    return info.title, info.artist

async def get_playback_status(session):
    if session is None:
        return "STOPPED"
    status = session.get_playback_info().playback_status
    if status.name == "PLAYING":
        return "PLAY"
    elif status.name == "PAUSED":
        return "PAUSE"
    else:
        return "STOPPED"

# Async input from Arduino
async def serial_input(queue, stop_event):
    loop = asyncio.get_event_loop()
    while not stop_event.is_set():
        await asyncio.sleep(0.05)
        try:
            if ser.in_waiting > 0:
                line = ser.readline().decode(errors="ignore").strip().upper()
                if line:
                    print(f"[DEBUG] Received from Arduino: '{line}'")
                    await queue.put(line)
        except Exception as e:
            print("[ERROR] Serial read error:", e)

# Media monitoring loop
async def media_loop(session, stop_event):
    last_song = ""
    last_artist = ""
    last_status = ""
    last_volume = -1

    while not stop_event.is_set():
        if session is None:
            session = await get_session()
        if session is not None:
            title, artist = await get_media_info(session)
            status = await get_playback_status(session)
            volume = int(get_volume() * 100)

            if title != last_song:
                ser.write(f"SONG:{title}\n".encode())
                print(f"[DEBUG] Sent to Arduino: SONG:{title}")
                last_song = title
            if artist != last_artist:
                ser.write(f"ARTIST:{artist}\n".encode())
                print(f"[DEBUG] Sent to Arduino: ARTIST:{artist}")
                last_artist = artist
            if status != last_status:
                ser.write(f"STATUS:{status}\n".encode())
                print(f"[DEBUG] Sent to Arduino: STATUS:{status}")
                last_status = status
            if volume != last_volume:
                ser.write(f"VOLUME:{volume}\n".encode())
                print(f"[DEBUG] Sent to Arduino: VOLUME:{volume}")
                last_volume = volume

        await asyncio.sleep(1)

# Command processing loop
async def command_loop(session, queue, stop_event):
    while not stop_event.is_set():
        cmd = await queue.get()
        
        # Handle playback / volume commands
        if cmd == "VOLUP":
            change_volume(0.05)
        elif cmd == "VOLDOWN":
            change_volume(-0.05)
        elif cmd == "NEXT" or cmd == "SKIP":
            if session:
                await session.try_skip_next_async()
                print(f"[DEBUG] Executed command: {cmd}")
        elif cmd == "PREV" or cmd == "REWIND":
            if session:
                await session.try_skip_previous_async()
                print(f"[DEBUG] Executed command: {cmd}")
        elif cmd == "PAUSE":
            if session:
                await session.try_pause_async()
                print(f"[DEBUG] Executed command: PAUSE")
        elif cmd == "PLAY":
            if session:
                await session.try_play_async()
                print(f"[DEBUG] Executed command: PLAY")
        elif cmd.startswith("REQ:"):
            request = cmd[4:]
            title, artist = await get_media_info(session)
            status = await get_playback_status(session)
            volume = int(get_volume() * 100)

            if request == "SONG":
                ser.write(f"SONG:{title}\n".encode())
                print(f"[DEBUG] Responded to REQ: SONG -> {title}")
            elif request == "ARTIST":
                ser.write(f"ARTIST:{artist}\n".encode())
                print(f"[DEBUG] Responded to REQ: ARTIST -> {artist}")
            elif request == "STATUS":
                ser.write(f"STATUS:{status}\n".encode())
                print(f"[DEBUG] Responded to REQ: STATUS -> {status}")
            elif request == "VOLUME":
                ser.write(f"VOLUME:{volume}\n".encode())
                print(f"[DEBUG] Responded to REQ: VOLUME -> {volume}")
        elif cmd.startswith("VOLUME:"):
            try:
                vol = int(cmd.split(":")[1])
                set_volume_from_arduino(vol)
            except ValueError:
                print(f"[ERROR] Invalid volume value from Arduino: {cmd}")
        elif cmd == "QUIT":
            stop_event.set()
            print("[DEBUG] Exiting...")

# Main function
async def main():
    session = await get_session()
    stop_event = asyncio.Event()
    queue = asyncio.Queue()

    await asyncio.gather(
        media_loop(session, stop_event),
        command_loop(session, queue, stop_event),
        serial_input(queue, stop_event)
    )

if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        ser.close()
        sys.exit()
