import websockets
import asyncio
import keyboard
import time
import ctypes
import sys

# Configuration
ESP_IP = "192.168.254.33"  # Replace with your ESP's IP
WEBSOCKET_PORT = 81
RECONNECT_DELAY = 5  # Seconds to wait before reconnecting

# SNES to Keyboard mapping
BUTTON_MAPPING = {
    0: 'z',    # B
    1: 'a',    # Y
    2: 'enter', # SELECT
    3: 'space', # START
    4: 'up',    # UP
    5: 'down',  # DOWN
    6: 'left',  # LEFT
    7: 'right', # RIGHT
    8: 'x',     # A
    9: 's',     # X
    10: 'q',    # L
    11: 'w'     # R
}

def is_admin():
    """Check if the script is running as administrator"""
    try:
        return ctypes.windll.shell32.IsUserAnAdmin()
    except:
        return False

class GamepadController:
    def __init__(self):
        self.previous_state = [0] * 12
        self.connected = False

    async def connect_to_esp(self):
        while True:
            try:
                print(f"⌛ Connecting to ESP8266 at ws://{ESP_IP}:{WEBSOCKET_PORT}")
                async with websockets.connect(f"ws://{ESP_IP}:{WEBSOCKET_PORT}") as websocket:
                    self.connected = True
                    print("✅ Successfully connected to ESP8266 game controller!")
                    await self.handle_controller(websocket)
            except Exception as e:
                print(f"❌ Connection error: {e}")
                self.connected = False
                print(f"⏳ Retrying in {RECONNECT_DELAY} seconds...")
                time.sleep(RECONNECT_DELAY)

    async def handle_controller(self, websocket):
        while self.connected:
            try:
                message = await websocket.recv()
                current_state = [int(c) for c in message]
                
                for i in range(12):
                    if current_state[i] != self.previous_state[i]:
                        if current_state[i] == 1:
                            keyboard.press(BUTTON_MAPPING[i])
                            print(f"🔘 Button {i} pressed ({BUTTON_MAPPING[i]})")
                        else:
                            keyboard.release(BUTTON_MAPPING[i])
                
                self.previous_state = current_state.copy()
                
            except Exception as e:
                print(f"⚠️ Error in handler: {e}")
                self.connected = False
                break

if __name__ == "__main__":
    print("🎮 Windows Gamepad Controller - Press Ctrl+C to exit")
    print(f"Target ESP IP: {ESP_IP}")
    
    # Admin check
    if not is_admin():
        print("\n❌ ERROR: This script requires Administrator privileges!")
        print("   Please right-click the script and select 'Run as administrator'")
        input("Press Enter to exit...")
        sys.exit(1)
    
    controller = GamepadController()
    
    try:
        asyncio.get_event_loop().run_until_complete(controller.connect_to_esp())
    except KeyboardInterrupt:
        print("\n🛑 Shutting down...")
    finally:
        # Release all keys on exit
        for key in set(BUTTON_MAPPING.values()):
            keyboard.release(key)