#!/usr/bin/env python3
"""
Flag Battle Royale - Python/Pygame Version
Complete rewrite matching HTML version exactly

Usage:
  python flag_battle_royale.py              # Normal mode with window
  python flag_battle_royale.py --headless   # Background streaming (no window/sound)
"""

import pygame
import math
import random
import json
import os
import io
import glob
import subprocess
import threading
import queue
import sys
from pathlib import Path
from dataclasses import dataclass
from enum import Enum
from typing import Dict, List, Optional, Tuple

# Check for headless mode
HEADLESS_MODE = '--headless' in sys.argv

# Initialize Pygame
if HEADLESS_MODE:
    # Use dummy video driver for headless mode
    os.environ['SDL_VIDEODRIVER'] = 'dummy'
    os.environ['SDL_AUDIODRIVER'] = 'dummy'

pygame.init()
if not HEADLESS_MODE:
    pygame.mixer.init(frequency=44100, size=-16, channels=2, buffer=512)

# ==================== CONSTANTS ====================
SCREEN_WIDTH = 1080
SCREEN_HEIGHT = 1920
FPS = 60

# Colors
COLOR_ARENA_BORDER = (42, 74, 106)
COLOR_GOLD = (255, 215, 0)
COLOR_SILVER = (192, 192, 192)
COLOR_BRONZE = (205, 127, 50)
COLOR_WHITE = (255, 255, 255)
COLOR_BLACK = (0, 0, 0)
COLOR_GREEN = (74, 222, 128)
COLOR_RED = (255, 71, 87)

# ==================== COUNTRIES DATA ====================
COUNTRIES = [
    {"code": "kr", "name": "South Korea"}, {"code": "jp", "name": "Japan"},
    {"code": "cn", "name": "China"}, {"code": "in", "name": "India"},
    {"code": "th", "name": "Thailand"}, {"code": "vn", "name": "Vietnam"},
    {"code": "my", "name": "Malaysia"}, {"code": "sg", "name": "Singapore"},
    {"code": "id", "name": "Indonesia"}, {"code": "ph", "name": "Philippines"},
    {"code": "tw", "name": "Taiwan"}, {"code": "pk", "name": "Pakistan"},
    {"code": "bd", "name": "Bangladesh"}, {"code": "np", "name": "Nepal"},
    {"code": "lk", "name": "Sri Lanka"}, {"code": "mm", "name": "Myanmar"},
    {"code": "kh", "name": "Cambodia"}, {"code": "mn", "name": "Mongolia"},
    {"code": "kz", "name": "Kazakhstan"}, {"code": "gb", "name": "United Kingdom"},
    {"code": "de", "name": "Germany"}, {"code": "fr", "name": "France"},
    {"code": "it", "name": "Italy"}, {"code": "es", "name": "Spain"},
    {"code": "nl", "name": "Netherlands"}, {"code": "pl", "name": "Poland"},
    {"code": "se", "name": "Sweden"}, {"code": "no", "name": "Norway"},
    {"code": "fi", "name": "Finland"}, {"code": "dk", "name": "Denmark"},
    {"code": "be", "name": "Belgium"}, {"code": "at", "name": "Austria"},
    {"code": "ch", "name": "Switzerland"}, {"code": "pt", "name": "Portugal"},
    {"code": "gr", "name": "Greece"}, {"code": "ie", "name": "Ireland"},
    {"code": "cz", "name": "Czech Republic"}, {"code": "ro", "name": "Romania"},
    {"code": "hu", "name": "Hungary"}, {"code": "ua", "name": "Ukraine"},
    {"code": "ru", "name": "Russia"}, {"code": "hr", "name": "Croatia"},
    {"code": "rs", "name": "Serbia"}, {"code": "is", "name": "Iceland"},
    {"code": "us", "name": "United States"}, {"code": "ca", "name": "Canada"},
    {"code": "mx", "name": "Mexico"}, {"code": "br", "name": "Brazil"},
    {"code": "ar", "name": "Argentina"}, {"code": "co", "name": "Colombia"},
    {"code": "cl", "name": "Chile"}, {"code": "pe", "name": "Peru"},
    {"code": "ve", "name": "Venezuela"}, {"code": "ec", "name": "Ecuador"},
    {"code": "uy", "name": "Uruguay"}, {"code": "cu", "name": "Cuba"},
    {"code": "cr", "name": "Costa Rica"}, {"code": "pa", "name": "Panama"},
    {"code": "jm", "name": "Jamaica"}, {"code": "za", "name": "South Africa"},
    {"code": "ng", "name": "Nigeria"}, {"code": "eg", "name": "Egypt"},
    {"code": "ke", "name": "Kenya"}, {"code": "ma", "name": "Morocco"},
    {"code": "gh", "name": "Ghana"}, {"code": "et", "name": "Ethiopia"},
    {"code": "tz", "name": "Tanzania"}, {"code": "dz", "name": "Algeria"},
    {"code": "tn", "name": "Tunisia"}, {"code": "ae", "name": "UAE"},
    {"code": "sa", "name": "Saudi Arabia"}, {"code": "il", "name": "Israel"},
    {"code": "tr", "name": "Turkey"}, {"code": "ir", "name": "Iran"},
    {"code": "iq", "name": "Iraq"}, {"code": "qa", "name": "Qatar"},
    {"code": "kw", "name": "Kuwait"}, {"code": "au", "name": "Australia"},
    {"code": "nz", "name": "New Zealand"}, {"code": "fj", "name": "Fiji"},
    {"code": "sk", "name": "Slovakia"}, {"code": "bg", "name": "Bulgaria"},
    {"code": "lt", "name": "Lithuania"}, {"code": "lv", "name": "Latvia"},
    {"code": "ee", "name": "Estonia"}, {"code": "si", "name": "Slovenia"},
    {"code": "ba", "name": "Bosnia"}, {"code": "al", "name": "Albania"},
    {"code": "mk", "name": "N. Macedonia"}, {"code": "me", "name": "Montenegro"},
]


@dataclass
class Config:
    arena_radius: int = 528
    flag_width: int = 77
    flag_height: int = 50
    flag_count: int = 80
    constant_speed: float = 7.2
    exit_gap_angle: float = 0.5
    gate_rotation_speed: float = 0.012
    flag_radius: int = 0
    border_width: int = 7
    wins_needed: int = 4


class GameState(Enum):
    IDLE = 0
    RUNNING = 1
    ROUND_END = 2
    SETTINGS = 3


@dataclass
class Flag:
    id: int
    code: str
    name: str
    x: float
    y: float
    vx: float
    vy: float
    alive: bool = True
    eliminating: bool = False
    opacity: float = 1.0
    eliminate_vx: float = 0.0
    eliminate_vy: float = 0.0


class FlagImageCache:
    """Flag image cache with download support"""

    def __init__(self):
        self.cache: Dict[str, pygame.Surface] = {}
        self.cache_dir = Path(__file__).parent / "flag_cache"
        self.cache_dir.mkdir(exist_ok=True)

    def _create_fallback(self, code: str, width: int, height: int) -> pygame.Surface:
        surface = pygame.Surface((width, height))
        hue = (ord(code[0]) * 47 + ord(code[1]) * 31) % 360
        c = 0.6 * 0.7
        x = c * (1 - abs((hue / 60) % 2 - 1))
        m = 0.6 - c
        if hue < 60: r, g, b = c, x, 0
        elif hue < 120: r, g, b = x, c, 0
        elif hue < 180: r, g, b = 0, c, x
        elif hue < 240: r, g, b = 0, x, c
        elif hue < 300: r, g, b = x, 0, c
        else: r, g, b = c, 0, x
        surface.fill((int((r + m) * 255), int((g + m) * 255), int((b + m) * 255)))
        font = pygame.font.Font(None, min(18, height))
        text = font.render(code.upper(), True, COLOR_WHITE)
        surface.blit(text, text.get_rect(center=(width // 2, height // 2)))
        return surface

    def load_flag(self, code: str, width: int, height: int) -> pygame.Surface:
        key = f"{code}_{width}_{height}"
        if key in self.cache:
            return self.cache[key]

        local_path = self.cache_dir / f"{code}.png"

        if local_path.exists():
            try:
                img = pygame.image.load(str(local_path)).convert_alpha()
                img = pygame.transform.smoothscale(img, (width, height))
                self.cache[key] = img
                return img
            except:
                pass

        # Try download
        try:
            import requests
            url = f"https://flagcdn.com/w80/{code.lower()}.png"
            resp = requests.get(url, timeout=3)
            if resp.status_code == 200:
                with open(local_path, 'wb') as f:
                    f.write(resp.content)
                img = pygame.image.load(io.BytesIO(resp.content)).convert_alpha()
                img = pygame.transform.smoothscale(img, (width, height))
                self.cache[key] = img
                return img
        except:
            pass

        fallback = self._create_fallback(code, width, height)
        self.cache[key] = fallback
        return fallback

    def preload_all(self, codes: List[str], width: int, height: int):
        for code in codes:
            self.load_flag(code, width, height)


class YouTubeStreamer:
    """YouTube Live streaming via FFmpeg with audio support"""

    def __init__(self, game_width: int, game_height: int, fps: int = 24):
        self.game_width = game_width
        self.game_height = game_height
        # Output resolution: 1080x1920 (vertical full HD)
        self.output_width = 1080
        self.output_height = 1920
        self.fps = fps
        self.process: Optional[subprocess.Popen] = None
        self.streaming = False
        self.stream_key: Optional[str] = None
        self.config_path = Path(__file__).parent / "stream_config.json"
        self.audio_system: Optional['AudioSystem'] = None

        # Frame queue for non-blocking sending
        self.frame_queue = None
        self.sender_thread = None
        self._load_config()

    def _load_config(self):
        """Load stream key from config"""
        if self.config_path.exists():
            try:
                with open(self.config_path) as f:
                    data = json.load(f)
                    self.stream_key = data.get("stream_key")
            except:
                pass

    def _save_config(self):
        """Save stream key to config"""
        with open(self.config_path, 'w') as f:
            json.dump({"stream_key": self.stream_key}, f)

    def set_stream_key(self, key: str):
        """Set YouTube stream key"""
        self.stream_key = key
        self._save_config()

    def set_audio_system(self, audio: 'AudioSystem'):
        """Set audio system reference for streaming audio"""
        self.audio_system = audio

    def start(self) -> bool:
        """Start streaming to YouTube with audio (BGM + SFX mixed)"""
        if not self.stream_key:
            print("No stream key set!")
            return False

        if self.streaming:
            return True

        rtmp_url = f"rtmp://a.rtmp.youtube.com/live2/{self.stream_key}"

        # Build FFmpeg command - optimized for real-time streaming
        cmd = ['ffmpeg', '-y', '-hide_banner']

        # Video input from pipe (input 0) - no -re flag for pipe input
        cmd.extend([
            '-f', 'rawvideo',
            '-pixel_format', 'rgb24',
            '-video_size', f'{self.game_width}x{self.game_height}',
            '-framerate', str(self.fps),
            '-i', 'pipe:0',
        ])

        # Use pre-mixed audio file (no real-time mixing = stable audio)
        has_audio = False
        if self.audio_system:
            mixed_path = self.audio_system.audio_dir / "stream_audio.aac"
            if mixed_path.exists():
                cmd.extend([
                    '-stream_loop', '-1',
                    '-i', str(mixed_path),
                ])
                has_audio = True

        # Simple filter - video format only
        if has_audio:
            cmd.extend([
                '-filter_complex', '[0:v]format=yuv420p[vout]',
                '-map', '[vout]', '-map', '1:a',
            ])
        else:
            cmd.extend([
                '-f', 'lavfi', '-i', 'anullsrc=r=44100:cl=stereo',
                '-filter_complex', '[0:v]format=yuv420p[vout]',
                '-map', '[vout]', '-map', '1:a',
            ])

        # Output settings - Hardware encoding (Apple VideoToolbox)
        cmd.extend([
            '-c:v', 'h264_videotoolbox',  # GPU 하드웨어 인코딩
            '-b:v', '8000k',
            '-maxrate', '8000k',
            '-bufsize', '16000k',
            '-g', '48',
            '-keyint_min', '24',
            '-realtime', '1',  # 실시간 인코딩 우선
            '-prio_speed', '1',  # 속도 우선
        ])

        # Audio: copy if pre-mixed, encode if silent
        if has_audio:
            cmd.extend(['-c:a', 'copy'])
        else:
            cmd.extend(['-c:a', 'aac', '-b:a', '128k'])

        cmd.extend(['-f', 'flv', rtmp_url])

        try:
            # Show stderr for debugging
            self.process = subprocess.Popen(
                cmd,
                stdin=subprocess.PIPE,
                stdout=subprocess.DEVNULL,
                stderr=subprocess.PIPE
            )

            # Start a thread to read stderr
            def read_stderr():
                try:
                    for line in self.process.stderr:
                        line = line.decode('utf-8', errors='ignore').strip()
                        if line and ('error' in line.lower() or 'warning' in line.lower()):
                            print(f"[FFmpeg] {line}")
                except:
                    pass

            self.stderr_thread = threading.Thread(target=read_stderr, daemon=True)
            self.stderr_thread.start()

            # Initialize frame queue and sender thread
            self.frame_queue = queue.Queue(maxsize=10)  # Buffer for smoother streaming
            self.streaming = True
            self.sender_thread = threading.Thread(target=self._frame_sender_loop, daemon=True)
            self.sender_thread.start()

            print(f"YouTube streaming started!")
            print(f"  Resolution: {self.output_width}x{self.output_height}")
            print(f"  FPS: {self.fps}, Video: 8000 Kbps (GPU accelerated)")
            print(f"  Audio: {'Pre-mixed (no re-encoding)' if has_audio else 'None'}")
            return True
        except FileNotFoundError:
            print("FFmpeg not found! Install FFmpeg to enable streaming.")
            return False
        except Exception as e:
            print(f"Failed to start streaming: {e}")
            return False

    def _frame_sender_loop(self):
        """Background thread that sends frames to FFmpeg"""
        while self.streaming:
            try:
                frame_data = self.frame_queue.get(timeout=1.0)
                if frame_data is None:  # Stop signal
                    break
                self.process.stdin.write(frame_data)
                self.process.stdin.flush()
            except queue.Empty:
                continue
            except BrokenPipeError:
                print("Stream connection lost - FFmpeg closed")
                self.streaming = False
                break
            except Exception as e:
                print(f"Stream error: {e}")
                break

    def send_frame(self, surface: pygame.Surface):
        """Send a frame to the stream (non-blocking via queue)"""
        if not self.streaming or not self.process:
            return

        try:
            # Convert pygame surface to raw RGB bytes
            frame_data = pygame.image.tostring(surface, 'RGB')

            # Non-blocking: drop frame if queue is full (prevents lag buildup)
            try:
                self.frame_queue.put_nowait(frame_data)
            except queue.Full:
                pass  # Drop frame to prevent lag
        except Exception as e:
            print(f"Frame capture error: {e}")

    def stop(self):
        """Stop streaming"""
        self.streaming = False

        # Signal sender thread to stop
        if self.frame_queue:
            try:
                self.frame_queue.put_nowait(None)
            except:
                pass

        # Wait for sender thread
        if self.sender_thread and self.sender_thread.is_alive():
            self.sender_thread.join(timeout=2)

        if self.process:
            try:
                self.process.stdin.close()
                self.process.terminate()
                self.process.wait(timeout=5)
            except:
                self.process.kill()
            self.process = None

        self.frame_queue = None
        print("Streaming stopped.")


class AudioSystem:
    """Audio system - loads from audio/ folder automatically"""

    def __init__(self):
        # SFX
        self.sfx_sound: Optional[pygame.mixer.Sound] = None
        self.sfx_channel: Optional[pygame.mixer.Channel] = None
        self.sfx_path: Optional[str] = None
        self.sfx_enabled = True
        self.sfx_volume = 0.5
        self.sfx_playing = False

        # BGM
        self.bgm_files: List[str] = []
        self.bgm_current_index = 0
        self.bgm_enabled = True
        self.bgm_volume = 0.3
        self.bgm_playing = False

        # Audio folder
        self.audio_dir = Path(__file__).parent / "audio"
        self.audio_dir.mkdir(exist_ok=True)

        # Set up BGM end event (only if not headless)
        if not HEADLESS_MODE:
            pygame.mixer.music.set_endevent(pygame.USEREVENT + 1)

        # Auto-load from folder
        self.scan_audio_folder()

        # Convert SFX for streaming (FFmpeg compatibility)
        self._prepare_sfx_for_streaming()

    def _prepare_sfx_for_streaming(self):
        """Prepare audio for streaming - create pre-mixed file"""
        # Always prepare mixed audio
        self._prepare_mixed_audio()

        if not self.sfx_path:
            return

        stream_path = self.audio_dir / "sfx_stream.wav"

        # Skip if already converted and newer than source
        if stream_path.exists():
            src_mtime = os.path.getmtime(self.sfx_path)
            dst_mtime = os.path.getmtime(stream_path)
            if dst_mtime >= src_mtime:
                return

        # Convert using afconvert (macOS)
        try:
            result = subprocess.run(
                ['afconvert', '-f', 'WAVE', '-d', 'LEI16', self.sfx_path, str(stream_path)],
                capture_output=True, timeout=10
            )
            if result.returncode == 0:
                print(f"Converted SFX for streaming: {stream_path.name}")
                # Regenerate mixed audio with new SFX
                self._prepare_mixed_audio(force=True)
        except Exception as e:
            print(f"Could not convert SFX for streaming: {e}")

    def _prepare_mixed_audio(self, force=False):
        """Pre-mix BGM + SFX into a single audio file for stable streaming"""
        if not self.bgm_files:
            return

        mixed_path = self.audio_dir / "stream_audio.aac"
        sfx_path = self.audio_dir / "sfx_stream.wav"

        # Check if we need to regenerate
        if not force and mixed_path.exists():
            mix_mtime = os.path.getmtime(mixed_path)
            bgm_mtime = os.path.getmtime(self.bgm_files[0])
            sfx_mtime = os.path.getmtime(sfx_path) if sfx_path.exists() else 0
            if mix_mtime >= bgm_mtime and mix_mtime >= sfx_mtime:
                return

        print("Creating pre-mixed audio for streaming...")

        try:
            if sfx_path.exists():
                # Mix BGM + SFX (30 minutes)
                cmd = [
                    'ffmpeg', '-y', '-hide_banner', '-loglevel', 'error',
                    '-stream_loop', '-1', '-i', self.bgm_files[0],
                    '-stream_loop', '-1', '-i', str(sfx_path),
                    '-filter_complex', '[0:a]volume=0.5[bgm];[1:a]volume=0.8[sfx];[bgm][sfx]amix=inputs=2:duration=first[out]',
                    '-map', '[out]',
                    '-t', '1800',
                    '-c:a', 'aac', '-b:a', '128k',
                    str(mixed_path)
                ]
            else:
                # BGM only (30 minutes)
                cmd = [
                    'ffmpeg', '-y', '-hide_banner', '-loglevel', 'error',
                    '-stream_loop', '-1', '-i', self.bgm_files[0],
                    '-t', '1800',
                    '-af', 'volume=0.5',
                    '-c:a', 'aac', '-b:a', '128k',
                    str(mixed_path)
                ]

            result = subprocess.run(cmd, capture_output=True, timeout=300)
            if result.returncode == 0:
                print(f"Created: {mixed_path.name} (30 min pre-mixed)")
            else:
                print(f"Failed to create mixed audio")
        except Exception as e:
            print(f"Could not create mixed audio: {e}")

    def scan_audio_folder(self):
        """Scan audio folder for SFX and BGM files"""
        # Look for SFX (sfx.mp3, sfx.wav, sfx.ogg)
        for ext in ['mp3', 'wav', 'ogg']:
            sfx_path = self.audio_dir / f"sfx.{ext}"
            if sfx_path.exists():
                self.load_sfx(str(sfx_path))
                break

        # Look for BGM (bgm*.mp3, bgm*.wav, bgm*.ogg)
        bgm_files = []
        for ext in ['mp3', 'wav', 'ogg']:
            bgm_files.extend(glob.glob(str(self.audio_dir / f"bgm*.{ext}")))
        bgm_files.sort()
        if bgm_files:
            self.bgm_files = bgm_files
            print(f"Loaded {len(bgm_files)} BGM file(s)")

    def load_sfx(self, path: str) -> bool:
        """Load SFX file"""
        try:
            self.sfx_path = path  # Always store path for streaming
            if not HEADLESS_MODE:
                self.sfx_sound = pygame.mixer.Sound(path)
                self.sfx_sound.set_volume(self.sfx_volume)
            print(f"Loaded SFX: {os.path.basename(path)}")
            return True
        except Exception as e:
            print(f"Failed to load SFX: {e}")
            return False

    def load_bgm(self, paths: List[str]):
        """Load BGM playlist"""
        self.bgm_files = [p for p in paths if os.path.exists(p)]
        self.bgm_current_index = 0

    def play_sfx(self):
        """Start SFX loop"""
        if HEADLESS_MODE or not self.sfx_sound or not self.sfx_enabled:
            return
        self.sfx_sound.set_volume(self.sfx_volume)
        self.sfx_channel = self.sfx_sound.play(-1)  # Loop forever
        self.sfx_playing = True

    def pause_sfx(self):
        """Pause SFX"""
        if HEADLESS_MODE:
            return
        if self.sfx_channel:
            self.sfx_channel.pause()
        self.sfx_playing = False

    def resume_sfx(self):
        """Resume SFX"""
        if HEADLESS_MODE or not self.sfx_enabled:
            return
        if self.sfx_channel:
            self.sfx_channel.unpause()
            self.sfx_playing = True
        elif self.sfx_sound:
            self.play_sfx()

    def stop_sfx(self):
        """Stop SFX"""
        if HEADLESS_MODE:
            return
        if self.sfx_channel:
            self.sfx_channel.stop()
        self.sfx_playing = False

    def play_bgm(self):
        """Start BGM playlist"""
        if HEADLESS_MODE or not self.bgm_files or not self.bgm_enabled:
            return
        try:
            pygame.mixer.music.load(self.bgm_files[self.bgm_current_index])
            pygame.mixer.music.set_volume(self.bgm_volume)
            pygame.mixer.music.play()
            self.bgm_playing = True
        except Exception as e:
            print(f"Failed to play BGM: {e}")

    def pause_bgm(self):
        """Pause BGM"""
        if HEADLESS_MODE:
            return
        pygame.mixer.music.pause()
        self.bgm_playing = False

    def resume_bgm(self):
        """Resume BGM"""
        if HEADLESS_MODE:
            return
        if self.bgm_enabled:
            pygame.mixer.music.unpause()
            self.bgm_playing = True

    def next_bgm(self):
        """Play next BGM track"""
        if HEADLESS_MODE or not self.bgm_files:
            return
        self.bgm_current_index = (self.bgm_current_index + 1) % len(self.bgm_files)
        self.play_bgm()

    def set_sfx_volume(self, vol: float):
        self.sfx_volume = vol
        if not HEADLESS_MODE and self.sfx_sound:
            self.sfx_sound.set_volume(vol)

    def set_bgm_volume(self, vol: float):
        self.bgm_volume = vol
        if not HEADLESS_MODE:
            pygame.mixer.music.set_volume(vol)

    def toggle_sfx(self, enabled: bool):
        self.sfx_enabled = enabled
        if HEADLESS_MODE:
            return
        if enabled and not self.sfx_playing:
            self.play_sfx()
        elif not enabled:
            self.pause_sfx()

    def toggle_bgm(self, enabled: bool):
        self.bgm_enabled = enabled
        if HEADLESS_MODE:
            return
        if enabled and self.bgm_files:
            self.play_bgm()
        elif not enabled:
            self.pause_bgm()


class Game:
    def __init__(self):
        self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
        pygame.display.set_caption("Flag Battle Royale")
        self.clock = pygame.time.Clock()

        # Systems
        self.flag_cache = FlagImageCache()
        self.audio = AudioSystem()
        self.streamer = YouTubeStreamer(SCREEN_WIDTH, SCREEN_HEIGHT, fps=24)
        self.streamer.set_audio_system(self.audio)  # Connect audio for streaming
        self.config = Config()
        self.config.flag_height = int(self.config.flag_width * 0.67)

        # Game data
        self.win_counts: Dict[str, int] = {}
        self.current_champion: Optional[str] = None
        self._load_save()

        # Game state
        self.state = GameState.IDLE
        self.flags: List[Flag] = []
        self.eliminated_flags: List[Flag] = []
        self.gate_angle = math.pi / 2
        self.last_winner: Optional[Flag] = None
        self.countdown = 5
        self.countdown_timer = 0
        self.new_champion_flag: Optional[str] = None  # For new champion notification

        # Layout
        self.center_x = SCREEN_WIDTH // 2
        self.center_y = 912  # Arena center moved down

        # Pre-render static surfaces
        self._init_surfaces()

        # Fonts (scaled for 1080x1920)
        self.font_large = pygame.font.Font(None, 115)
        self.font_medium = pygame.font.Font(None, 67)
        self.font_small = pygame.font.Font(None, 48)
        self.font_tiny = pygame.font.Font(None, 38)

        # Settings UI state
        self.settings_scroll = 0
        self.dragging_slider = None

        # Preload flags
        codes = [c["code"] for c in COUNTRIES]
        self.flag_cache.preload_all(codes, self.config.flag_width, self.config.flag_height)

    def _init_surfaces(self):
        """Pre-render static surfaces"""
        # Gradient background
        self.bg_surface = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT))
        for y in range(SCREEN_HEIGHT):
            ratio = y / SCREEN_HEIGHT
            r = int(91 + (232 - 91) * ratio)
            g = int(163 + (244 - 163) * ratio)
            b = int(217 + (248 - 217) * ratio)
            pygame.draw.line(self.bg_surface, (r, g, b), (0, y), (SCREEN_WIDTH, y))

        # Overlays
        self.overlay_dark = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.SRCALPHA)
        self.overlay_dark.fill((0, 0, 0, 220))

        self.overlay_settings = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.SRCALPHA)
        self.overlay_settings.fill((0, 0, 0, 235))

        # Leaderboard panel (scaled for 1080x1920)
        self.leaderboard_panel = pygame.Surface((432, 312), pygame.SRCALPHA)
        pygame.draw.rect(self.leaderboard_panel, (30, 60, 90, 230),
                        self.leaderboard_panel.get_rect(), border_radius=29)

        # Flag shadow
        self.flag_shadow = pygame.Surface((77, 53), pygame.SRCALPHA)
        self.flag_shadow.fill((0, 0, 0, 50))

    def _load_save(self):
        """Load saved data"""
        save_path = Path(__file__).parent / "save_data.json"
        if save_path.exists():
            try:
                with open(save_path) as f:
                    data = json.load(f)
                cfg = data.get("config", {})
                for k, v in cfg.items():
                    if hasattr(self.config, k):
                        setattr(self.config, k, v)
                self.win_counts = data.get("win_counts", {})
                self.current_champion = data.get("champion")

                # Audio settings (files are auto-loaded from /audio folder)
                audio = data.get("audio", {})
                self.audio.sfx_enabled = audio.get("sfx_enabled", True)
                self.audio.sfx_volume = audio.get("sfx_volume", 0.5)
                self.audio.bgm_enabled = audio.get("bgm_enabled", True)
                self.audio.bgm_volume = audio.get("bgm_volume", 0.3)
                self.audio.set_sfx_volume(self.audio.sfx_volume)
                self.audio.set_bgm_volume(self.audio.bgm_volume)
            except Exception as e:
                print(f"Load error: {e}")

    def _save_game(self):
        """Save game data"""
        save_path = Path(__file__).parent / "save_data.json"
        data = {
            "config": {
                "arena_radius": self.config.arena_radius,
                "flag_width": self.config.flag_width,
                "flag_height": self.config.flag_height,
                "flag_count": self.config.flag_count,
                "constant_speed": self.config.constant_speed,
                "exit_gap_angle": self.config.exit_gap_angle,
                "gate_rotation_speed": self.config.gate_rotation_speed,
                "flag_radius": self.config.flag_radius,
                "border_width": self.config.border_width,
                "wins_needed": self.config.wins_needed,
            },
            "win_counts": self.win_counts,
            "champion": self.current_champion,
            "audio": {
                "sfx_enabled": self.audio.sfx_enabled,
                "sfx_volume": self.audio.sfx_volume,
                "bgm_enabled": self.audio.bgm_enabled,
                "bgm_volume": self.audio.bgm_volume,
            }
        }
        with open(save_path, 'w') as f:
            json.dump(data, f)

    def spawn_flags(self):
        """Spawn flags in arena"""
        self.flags.clear()
        self.eliminated_flags.clear()
        self.gate_angle = math.pi / 2
        self.new_champion_flag = None  # Reset champion notification

        spawn_radius = self.config.arena_radius * 0.75
        shuffled = random.sample(COUNTRIES, min(self.config.flag_count, len(COUNTRIES)))
        collision_r = max(self.config.flag_width, self.config.flag_height) / 2

        for i, country in enumerate(shuffled):
            for _ in range(100):
                angle = random.random() * math.pi * 2
                dist = random.random() * spawn_radius * 0.7 + spawn_radius * 0.15
                x = self.center_x + math.cos(angle) * dist
                y = self.center_y + math.sin(angle) * dist
                if all(math.hypot(f.x - x, f.y - y) > collision_r * 2.2 for f in self.flags):
                    break

            vel_angle = random.random() * math.pi * 2
            self.flags.append(Flag(
                id=i, code=country["code"], name=country["name"],
                x=x, y=y,
                vx=math.cos(vel_angle) * self.config.constant_speed,
                vy=math.sin(vel_angle) * self.config.constant_speed,
            ))

    def update_physics(self):
        """Update game physics"""
        self.gate_angle += self.config.gate_rotation_speed
        if self.gate_angle > math.pi * 2:
            self.gate_angle -= math.pi * 2

        alive = [f for f in self.flags if f.alive and not f.eliminating]
        flag_r = max(self.config.flag_width, self.config.flag_height) / 2
        wall_r = self.config.arena_radius - flag_r
        speed = self.config.constant_speed

        # Move
        for f in alive:
            f.x += f.vx
            f.y += f.vy

        # Wall collision
        for f in alive:
            dx, dy = f.x - self.center_x, f.y - self.center_y
            dist = math.hypot(dx, dy)

            if dist > wall_r:
                angle = math.atan2(dy, dx)
                rel_angle = (angle - self.gate_angle + math.pi) % (2 * math.pi) - math.pi

                if abs(rel_angle) < self.config.exit_gap_angle / 2:
                    f.eliminating = True
                    f.eliminate_vx = (dx / dist) * 6
                    f.eliminate_vy = (dy / dist) * 6
                else:
                    nx, ny = dx / dist, dy / dist
                    f.x = self.center_x + nx * wall_r
                    f.y = self.center_y + ny * wall_r
                    dot = f.vx * nx + f.vy * ny
                    f.vx -= 2 * dot * nx
                    f.vy -= 2 * dot * ny
                    s = math.hypot(f.vx, f.vy)
                    if s > 0:
                        f.vx = f.vx / s * speed
                        f.vy = f.vy / s * speed

        # Flag collision
        for i, a in enumerate(alive):
            for b in alive[i+1:]:
                dx, dy = b.x - a.x, b.y - a.y
                dist = math.hypot(dx, dy)
                min_dist = flag_r * 1.6

                if 0 < dist < min_dist:
                    nx, ny = dx / dist, dy / dist
                    overlap = (min_dist - dist) / 2 + 0.5
                    a.x -= nx * overlap
                    a.y -= ny * overlap
                    b.x += nx * overlap
                    b.y += ny * overlap

                    rv = (a.vx - b.vx) * nx + (a.vy - b.vy) * ny
                    if rv > 0:
                        a.vx -= rv * nx
                        a.vy -= rv * ny
                        b.vx += rv * nx
                        b.vy += rv * ny

                        for f in (a, b):
                            s = math.hypot(f.vx, f.vy)
                            if s > 0:
                                f.vx = f.vx / s * speed
                                f.vy = f.vy / s * speed

        # Eliminating flags
        for f in self.flags:
            if f.eliminating and f.alive:
                f.x += f.eliminate_vx
                f.y += f.eliminate_vy
                f.opacity -= 0.06
                if f.opacity <= 0:
                    f.alive = False
                    f.opacity = 0
                    self.eliminated_flags.append(f)

        # Check winner
        remaining = [f for f in self.flags if f.alive and not f.eliminating]
        if len(remaining) == 1 and self.state == GameState.RUNNING:
            self._handle_win(remaining[0])
        elif len(remaining) == 0 and self.state == GameState.RUNNING and self.eliminated_flags:
            self._handle_win(self.eliminated_flags[-1])

    def _handle_win(self, winner: Flag):
        """Handle round win"""
        self.state = GameState.ROUND_END
        self.last_winner = winner
        self.audio.pause_sfx()

        self.win_counts[winner.code] = self.win_counts.get(winner.code, 0) + 1

        if self.win_counts[winner.code] >= self.config.wins_needed:
            self.current_champion = winner.code
            self.new_champion_flag = winner.code  # Trigger new champion notification
            self.win_counts[winner.code] = 0

        self._save_game()
        self.countdown = 5
        self.countdown_timer = pygame.time.get_ticks()

    # ==================== DRAWING ====================
    def draw_arena(self):
        """Draw arena with gap"""
        cx, cy = self.center_x, self.center_y
        r = self.config.arena_radius
        gap = self.config.exit_gap_angle
        thickness = self.config.border_width

        start = self.gate_angle + gap / 2
        end = self.gate_angle - gap / 2 + math.pi * 2

        segments = 100
        angle_range = end - start
        points = []

        for i in range(segments + 1):
            a = start + (i / segments) * angle_range
            points.append((cx + math.cos(a) * (r + thickness / 2),
                          cy + math.sin(a) * (r + thickness / 2)))

        for i in range(segments, -1, -1):
            a = start + (i / segments) * angle_range
            points.append((cx + math.cos(a) * (r - thickness / 2),
                          cy + math.sin(a) * (r - thickness / 2)))

        pygame.draw.polygon(self.screen, COLOR_ARENA_BORDER, points)

    def draw_flags(self):
        """Draw flags"""
        w, h = self.config.flag_width, self.config.flag_height
        for f in self.flags:
            if not f.alive and not f.eliminating:
                continue
            img = self.flag_cache.load_flag(f.code, w, h)
            x, y = int(f.x - w / 2), int(f.y - h / 2)
            if f.opacity < 1.0:
                img = img.copy()
                img.set_alpha(int(f.opacity * 255))
            self.screen.blit(img, (x, y))

    def draw_leaderboard(self):
        """Draw leaderboard - positioned at top right"""
        self.screen.blit(self.leaderboard_panel, (SCREEN_WIDTH - 468, 108))
        title = self.font_small.render("TOP CONTENDERS:", True, COLOR_GOLD)
        self.screen.blit(title, (SCREEN_WIDTH - 439, 137))

        sorted_wins = sorted(self.win_counts.items(), key=lambda x: x[1], reverse=True)[:3]
        colors = [COLOR_GOLD, COLOR_SILVER, COLOR_BRONZE]

        if not sorted_wins:
            txt = self.font_tiny.render("Waiting...", True, (136, 136, 136))
            self.screen.blit(txt, (SCREEN_WIDTH - 324, 240))
            return

        for i, (code, wins) in enumerate(sorted_wins):
            y = 199 + i * 67
            x = SCREEN_WIDTH - 439
            self.screen.blit(self.font_small.render(f"{i+1}.", True, colors[i]), (x, y))
            img = self.flag_cache.load_flag(code, 67, 48)
            self.screen.blit(img, (x + 55, y))
            country = next((c for c in COUNTRIES if c["code"] == code), None)
            name = (country["name"] if country else code)[:10]
            self.screen.blit(self.font_tiny.render(name, True, COLOR_WHITE), (x + 134, y + 5))
            self.screen.blit(self.font_small.render(str(wins), True, COLOR_GREEN), (x + 343, y))

    def draw_champion(self):
        """Draw champion banner - positioned at top left"""
        if not self.current_champion:
            # Still draw the BAN text even without champion
            self._draw_ban_text()
            return

        pygame.draw.rect(self.screen, COLOR_GOLD, (36, 132, 336, 108), border_radius=24)

        # Crown symbol (text-based)
        crown = self.font_small.render("CHAMPION", True, (139, 69, 19))
        self.screen.blit(crown, (53, 139))

        # Flag
        img = self.flag_cache.load_flag(self.current_champion, 86, 58)
        self.screen.blit(img, (53, 178))

        # Name
        country = next((c for c in COUNTRIES if c["code"] == self.current_champion), None)
        name = (country["name"] if country else self.current_champion)[:10]
        self.screen.blit(self.font_small.render(name, True, COLOR_BLACK), (149, 182))

        # BAN text below champion box
        self._draw_ban_text()

    def _draw_ban_text(self):
        """Draw 67 = BAN text below champion UI"""
        # Position below champion box (y=240) but above arena (y~384)
        ban_text = self.font_large.render("67 = BAN", True, COLOR_WHITE)
        # Add black outline for visibility
        for dx, dy in [(-3, -3), (-3, 3), (3, -3), (3, 3), (-3, 0), (3, 0), (0, -3), (0, 3)]:
            outline = self.font_large.render("67 = BAN", True, COLOR_BLACK)
            self.screen.blit(outline, (36 + dx, 260 + dy))
        self.screen.blit(ban_text, (36, 260))

    def draw_wins_info(self):
        """Draw wins needed - centered at top"""
        text = f"{self.config.wins_needed} wins needed"
        surf = self.font_small.render(text, True, COLOR_WHITE)
        w = surf.get_width() + 58
        x = self.center_x - w // 2
        pill = pygame.Surface((w, 62), pygame.SRCALPHA)
        pygame.draw.rect(pill, (30, 60, 90, 230), pill.get_rect(), border_radius=31)
        self.screen.blit(pill, (x, 29))
        self.screen.blit(surf, (x + 29, 38))

    def draw_eliminated(self):
        """Draw eliminated flags - max 3 rows, oldest removed when full"""
        section_bottom = 1872
        max_rows = 3

        w, h = 77, 53
        gap_x, gap_y = 5, 5
        cols = (SCREEN_WIDTH - 24) // (w + gap_x)
        start_x = (SCREEN_WIDTH - cols * (w + gap_x) + gap_x) // 2

        # Calculate max flags to display (3 rows worth)
        max_flags = cols * max_rows

        # Get the flags to display (most recent ones if we exceed max)
        if len(self.eliminated_flags) > max_flags:
            display_flags = self.eliminated_flags[-max_flags:]
        else:
            display_flags = self.eliminated_flags

        total_flags = len(display_flags)
        total_rows = (total_flags + cols - 1) // cols if total_flags > 0 else 0

        # Calculate section height based on actual rows
        actual_height = total_rows * (h + gap_y)
        section_top = section_bottom - actual_height

        # Store for CTA positioning
        self.eliminated_section_top = section_top if total_flags > 0 else section_bottom

        if total_flags == 0:
            return

        for i, f in enumerate(display_flags):
            row, col = i // cols, i % cols
            x = start_x + col * (w + gap_x)
            y = section_bottom - (row + 1) * (h + gap_y)

            self.screen.blit(self.flag_shadow, (x + 5, y + 5))
            img = self.flag_cache.load_flag(f.code, w, h)
            self.screen.blit(img, (x, y))

    def draw_cta(self):
        """Draw call-to-action - positioned above eliminated flags"""
        # Position CTA above the eliminated flags section
        cta_height = 132  # Height of CTA text block
        base_y = getattr(self, 'eliminated_section_top', 1764)
        y = base_y - cta_height

        t1_text = "Comment the name of your country"
        t2_text = "to make it win!"

        for dx, dy in [(-2, -2), (-2, 2), (2, -2), (2, 2), (-2, 0), (2, 0), (0, -2), (0, 2)]:
            t1 = self.font_medium.render(t1_text, True, COLOR_WHITE)
            t2 = self.font_medium.render(t2_text, True, COLOR_WHITE)
            self.screen.blit(t1, (self.center_x - t1.get_width() // 2 + dx, y + dy))
            self.screen.blit(t2, (self.center_x - t2.get_width() // 2 + dx, y + 62 + dy))

        t1 = self.font_medium.render(t1_text, True, COLOR_RED)
        t2 = self.font_medium.render(t2_text, True, COLOR_GREEN)
        self.screen.blit(t1, (self.center_x - t1.get_width() // 2, y))
        self.screen.blit(t2, (self.center_x - t2.get_width() // 2, y + 62))

    def draw_round_overlay(self):
        """Draw round end overlay"""
        if self.state != GameState.ROUND_END or not self.last_winner:
            return

        self.screen.blit(self.overlay_dark, (0, 0))

        # Check if new champion
        is_new_champion = self.new_champion_flag == self.last_winner.code

        if is_new_champion:
            # New Champion celebration
            txt = self.font_large.render("NEW CHAMPION!", True, COLOR_GOLD)
            self.screen.blit(txt, (self.center_x - txt.get_width() // 2, 432))

            # Crown symbols
            crown_txt = self.font_large.render("* * *", True, COLOR_GOLD)
            self.screen.blit(crown_txt, (self.center_x - crown_txt.get_width() // 2, 528))
        else:
            txt = self.font_large.render("WINNER!", True, COLOR_GOLD)
            self.screen.blit(txt, (self.center_x - txt.get_width() // 2, 480))

        # Flag
        img = self.flag_cache.load_flag(self.last_winner.code, 360, 240)
        rect = img.get_rect(center=(self.center_x, 792))
        pygame.draw.rect(self.screen, COLOR_GOLD, rect.inflate(19, 19), border_radius=19)
        self.screen.blit(img, rect)

        # Name
        name = self.font_medium.render(self.last_winner.name, True, COLOR_WHITE)
        self.screen.blit(name, (self.center_x - name.get_width() // 2, 960))

        # Wins info
        if is_new_champion:
            w_txt = self.font_small.render(f"Achieved {self.config.wins_needed} wins!", True, COLOR_GREEN)
        else:
            wins = self.win_counts.get(self.last_winner.code, 0)
            w_txt = self.font_small.render(f"{wins}/{self.config.wins_needed} wins", True, COLOR_GREEN)
        self.screen.blit(w_txt, (self.center_x - w_txt.get_width() // 2, 1044))

        # Countdown
        cd = self.font_large.render(str(self.countdown), True, COLOR_GOLD)
        self.screen.blit(cd, (self.center_x - cd.get_width() // 2, 1200))

        nxt = self.font_tiny.render("Next round", True, (136, 136, 136))
        self.screen.blit(nxt, (self.center_x - nxt.get_width() // 2, 1320))

    def draw_settings(self):
        """Draw settings panel - compact layout"""
        if self.state != GameState.SETTINGS:
            return

        self.screen.blit(self.overlay_settings, (0, 0))
        pygame.draw.rect(self.screen, (26, 42, 74), (20, 10, SCREEN_WIDTH - 40, 780), border_radius=16)

        title = self.font_medium.render("Settings", True, COLOR_GOLD)
        self.screen.blit(title, (self.center_x - title.get_width() // 2, 18))

        # Sliders - compact spacing
        sliders = [
            ("Arena Size", self.config.arena_radius, 200, 800, "arena_radius"),
            ("Flag Count", self.config.flag_count, 10, 120, "flag_count"),
            ("Flag Speed", self.config.constant_speed, 1.0, 36, "constant_speed"),
            ("Gate Rotation", self.config.gate_rotation_speed, 0.001, 0.1, "gate_rotation_speed"),
            ("Exit Gap", self.config.exit_gap_angle, 0.1, 2.0, "exit_gap_angle"),
            ("Flag Size", self.config.flag_width, 24, 192, "flag_width"),
            ("Border", self.config.border_width, 2, 24, "border_width"),
            ("Wins Needed", self.config.wins_needed, 1, 20, "wins_needed"),
            ("SFX Vol", self.audio.sfx_volume, 0, 1, "sfx_volume"),
            ("BGM Vol", self.audio.bgm_volume, 0, 1, "bgm_volume"),
        ]

        y = 48
        self.slider_rects = {}
        for label, value, min_v, max_v, attr in sliders:
            # Label & value on same line
            lbl = self.font_tiny.render(label, True, COLOR_WHITE)
            self.screen.blit(lbl, (35, y))

            if attr in ["constant_speed", "exit_gap_angle"]:
                val_str = f"{value:.2f}"
            elif attr == "gate_rotation_speed":
                val_str = f"{value:.3f}"
            elif attr in ["sfx_volume", "bgm_volume"]:
                val_str = f"{int(value * 100)}%"
            else:
                val_str = str(int(value))

            val = self.font_tiny.render(val_str, True, COLOR_GREEN)
            self.screen.blit(val, (SCREEN_WIDTH - 50 - val.get_width(), y))

            # Slider track - thinner
            rect = pygame.Rect(35, y + 16, SCREEN_WIDTH - 70, 10)
            pygame.draw.rect(self.screen, (42, 58, 90), rect, border_radius=3)

            # Thumb
            ratio = (value - min_v) / (max_v - min_v) if max_v != min_v else 0
            thumb_x = rect.x + ratio * rect.width
            pygame.draw.circle(self.screen, COLOR_GOLD, (int(thumb_x), rect.centery), 6)

            self.slider_rects[attr] = (rect, min_v, max_v)
            y += 38

        # Audio section - compact
        y += 5

        # SFX/BGM info on one line
        sfx_name = os.path.basename(self.audio.sfx_path)[:15] if self.audio.sfx_path else "None"
        bgm_count = len(self.audio.bgm_files)
        audio_txt = self.font_tiny.render(f"SFX: {sfx_name} | BGM: {bgm_count} files", True, (180, 180, 180))
        self.screen.blit(audio_txt, (35, y))
        y += 18

        # Refresh button - smaller
        refresh_btn = pygame.Rect(35, y, SCREEN_WIDTH - 70, 24)
        pygame.draw.rect(self.screen, (60, 100, 140), refresh_btn, border_radius=5)
        ref_txt = self.font_tiny.render("Refresh Audio (/audio folder)", True, COLOR_WHITE)
        self.screen.blit(ref_txt, (refresh_btn.centerx - ref_txt.get_width() // 2, refresh_btn.y + 5))
        self.refresh_btn_rect = refresh_btn
        y += 30

        # Enable toggles - smaller, on one line
        sfx_toggle = pygame.Rect(35, y, 20, 20)
        pygame.draw.rect(self.screen, COLOR_GREEN if self.audio.sfx_enabled else (80, 80, 80), sfx_toggle, border_radius=3)
        if self.audio.sfx_enabled:
            pygame.draw.line(self.screen, COLOR_WHITE, (sfx_toggle.x + 4, sfx_toggle.centery),
                           (sfx_toggle.x + 8, sfx_toggle.bottom - 4), 2)
            pygame.draw.line(self.screen, COLOR_WHITE, (sfx_toggle.x + 8, sfx_toggle.bottom - 4),
                           (sfx_toggle.right - 4, sfx_toggle.y + 4), 2)
        self.screen.blit(self.font_tiny.render("SFX", True, COLOR_WHITE), (60, y + 3))
        self.sfx_toggle_rect = sfx_toggle

        bgm_toggle = pygame.Rect(110, y, 20, 20)
        pygame.draw.rect(self.screen, COLOR_GREEN if self.audio.bgm_enabled else (80, 80, 80), bgm_toggle, border_radius=3)
        if self.audio.bgm_enabled:
            pygame.draw.line(self.screen, COLOR_WHITE, (bgm_toggle.x + 4, bgm_toggle.centery),
                           (bgm_toggle.x + 8, bgm_toggle.bottom - 4), 2)
            pygame.draw.line(self.screen, COLOR_WHITE, (bgm_toggle.x + 8, bgm_toggle.bottom - 4),
                           (bgm_toggle.right - 4, bgm_toggle.y + 4), 2)
        self.screen.blit(self.font_tiny.render("BGM", True, COLOR_WHITE), (135, y + 3))
        self.bgm_toggle_rect = bgm_toggle
        y += 28

        # Buttons - two columns, smaller
        btn_w = (SCREEN_WIDTH - 90) // 2

        reset_btn = pygame.Rect(35, y, btn_w, 28)
        pygame.draw.rect(self.screen, (239, 68, 68), reset_btn, border_radius=6)
        rst = self.font_tiny.render("Reset", True, COLOR_WHITE)
        self.screen.blit(rst, (reset_btn.centerx - rst.get_width() // 2, reset_btn.y + 7))
        self.reset_btn_rect = reset_btn

        apply_btn = pygame.Rect(45 + btn_w, y, btn_w, 28)
        pygame.draw.rect(self.screen, COLOR_GREEN, apply_btn, border_radius=6)
        apl = self.font_tiny.render("Apply", True, COLOR_BLACK)
        self.screen.blit(apl, (apply_btn.centerx - apl.get_width() // 2, apply_btn.y + 7))
        self.apply_btn_rect = apply_btn
        y += 34

        clear_btn = pygame.Rect(35, y, SCREEN_WIDTH - 70, 26)
        pygame.draw.rect(self.screen, (180, 50, 50), clear_btn, border_radius=6)
        clr = self.font_tiny.render("Clear All Data", True, COLOR_WHITE)
        self.screen.blit(clr, (clear_btn.centerx - clr.get_width() // 2, clear_btn.y + 6))
        self.clear_btn_rect = clear_btn
        y += 34

        # ===== YouTube Streaming Section =====
        stream_header = self.font_tiny.render("YouTube Live:", True, COLOR_GOLD)
        self.screen.blit(stream_header, (35, y))
        y += 18

        # Stream key input display
        key_display = self.streamer.stream_key[:18] + "..." if self.streamer.stream_key and len(self.streamer.stream_key) > 18 else (self.streamer.stream_key or "No key set")
        key_btn = pygame.Rect(35, y, SCREEN_WIDTH - 70, 24)
        pygame.draw.rect(self.screen, (50, 70, 100), key_btn, border_radius=5)
        key_txt = self.font_tiny.render(f"Key: {key_display}", True, COLOR_WHITE if self.streamer.stream_key else (150, 150, 150))
        self.screen.blit(key_txt, (key_btn.x + 8, key_btn.y + 5))
        self.stream_key_btn_rect = key_btn
        y += 28

        # Start/Stop streaming button
        if self.streamer.streaming:
            stream_btn = pygame.Rect(35, y, SCREEN_WIDTH - 70, 26)
            pygame.draw.rect(self.screen, COLOR_RED, stream_btn, border_radius=5)
            stream_txt = self.font_tiny.render("Stop Streaming", True, COLOR_WHITE)
        else:
            stream_btn = pygame.Rect(35, y, SCREEN_WIDTH - 70, 26)
            pygame.draw.rect(self.screen, COLOR_GREEN if self.streamer.stream_key else (80, 80, 80), stream_btn, border_radius=5)
            stream_txt = self.font_tiny.render("Start Streaming", True, COLOR_WHITE if self.streamer.stream_key else (150, 150, 150))
        self.screen.blit(stream_txt, (stream_btn.centerx - stream_txt.get_width() // 2, stream_btn.y + 6))
        self.stream_btn_rect = stream_btn

        # Status
        y += 30
        if self.streamer.streaming:
            status = self.font_tiny.render("LIVE", True, COLOR_RED)
            self.screen.blit(status, (35, y))

        # Hint at bottom
        hint = self.font_tiny.render("Press ESC to close", True, (100, 100, 100))
        self.screen.blit(hint, (self.center_x - hint.get_width() // 2, 772))

    def handle_settings_event(self, event):
        """Handle settings input"""
        if event.type == pygame.MOUSEBUTTONDOWN:
            mx, my = event.pos

            # Check sliders
            for attr, (rect, min_v, max_v) in self.slider_rects.items():
                if rect.collidepoint(mx, my) or (abs(my - rect.centery) < 12 and rect.x - 5 <= mx <= rect.right + 5):
                    self.dragging_slider = (attr, rect, min_v, max_v)
                    self._update_slider_value(mx)

            # Refresh button
            if hasattr(self, 'refresh_btn_rect') and self.refresh_btn_rect.collidepoint(mx, my):
                self.audio.scan_audio_folder()

            # Toggles
            if hasattr(self, 'sfx_toggle_rect') and self.sfx_toggle_rect.collidepoint(mx, my):
                self.audio.toggle_sfx(not self.audio.sfx_enabled)

            if hasattr(self, 'bgm_toggle_rect') and self.bgm_toggle_rect.collidepoint(mx, my):
                self.audio.toggle_bgm(not self.audio.bgm_enabled)

            # Buttons
            if hasattr(self, 'reset_btn_rect') and self.reset_btn_rect.collidepoint(mx, my):
                self._reset_settings()

            if hasattr(self, 'apply_btn_rect') and self.apply_btn_rect.collidepoint(mx, my):
                self._apply_settings()

            if hasattr(self, 'clear_btn_rect') and self.clear_btn_rect.collidepoint(mx, my):
                self._clear_all()

            # Streaming controls
            if hasattr(self, 'stream_key_btn_rect') and self.stream_key_btn_rect.collidepoint(mx, my):
                self._input_stream_key()

            if hasattr(self, 'stream_btn_rect') and self.stream_btn_rect.collidepoint(mx, my):
                if self.streamer.streaming:
                    self.streamer.stop()
                elif self.streamer.stream_key:
                    self.streamer.start()

        elif event.type == pygame.MOUSEBUTTONUP:
            self.dragging_slider = None

        elif event.type == pygame.MOUSEMOTION and self.dragging_slider:
            self._update_slider_value(event.pos[0])

    def _update_slider_value(self, mx):
        """Update slider value"""
        if not self.dragging_slider:
            return
        attr, rect, min_v, max_v = self.dragging_slider
        ratio = max(0, min(1, (mx - rect.x) / rect.width))
        value = min_v + ratio * (max_v - min_v)

        if attr == "sfx_volume":
            self.audio.set_sfx_volume(value)
        elif attr == "bgm_volume":
            self.audio.set_bgm_volume(value)
        elif attr in ["arena_radius", "flag_count", "flag_width", "border_width", "wins_needed"]:
            setattr(self.config, attr, int(value))
        else:
            setattr(self.config, attr, value)

    def _input_stream_key(self):
        """Simple text input for stream key"""
        # Create a simple input overlay
        self.stream_key_input_mode = True
        self.stream_key_buffer = self.streamer.stream_key or ""

    def _handle_stream_key_input(self, event):
        """Handle stream key text input"""
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RETURN:
                self.streamer.set_stream_key(self.stream_key_buffer)
                self.stream_key_input_mode = False
            elif event.key == pygame.K_ESCAPE:
                self.stream_key_input_mode = False
            elif event.key == pygame.K_BACKSPACE:
                self.stream_key_buffer = self.stream_key_buffer[:-1]
            elif event.key == pygame.K_v and (event.mod & pygame.KMOD_META or event.mod & pygame.KMOD_CTRL):
                # Paste from clipboard
                try:
                    import subprocess
                    result = subprocess.run(['pbpaste'], capture_output=True, text=True)
                    self.stream_key_buffer += result.stdout.strip()
                except:
                    pass
            elif len(event.unicode) == 1 and event.unicode.isprintable():
                self.stream_key_buffer += event.unicode

    def _draw_stream_key_input(self):
        """Draw stream key input overlay"""
        if not hasattr(self, 'stream_key_input_mode') or not self.stream_key_input_mode:
            return

        # Dark overlay
        overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.SRCALPHA)
        overlay.fill((0, 0, 0, 200))
        self.screen.blit(overlay, (0, 0))

        # Input box
        box_rect = pygame.Rect(30, 350, SCREEN_WIDTH - 60, 100)
        pygame.draw.rect(self.screen, (40, 60, 100), box_rect, border_radius=12)

        # Title
        title = self.font_medium.render("Enter YouTube Stream Key", True, COLOR_GOLD)
        self.screen.blit(title, (box_rect.centerx - title.get_width() // 2, box_rect.y + 15))

        # Input field
        input_rect = pygame.Rect(box_rect.x + 15, box_rect.y + 50, box_rect.width - 30, 30)
        pygame.draw.rect(self.screen, (30, 45, 70), input_rect, border_radius=6)

        # Text (masked)
        display_text = "*" * len(self.stream_key_buffer) if self.stream_key_buffer else "Paste or type key..."
        txt = self.font_small.render(display_text[:35], True, COLOR_WHITE if self.stream_key_buffer else (120, 120, 120))
        self.screen.blit(txt, (input_rect.x + 10, input_rect.y + 7))

        # Hint
        hint = self.font_tiny.render("Enter to save, ESC to cancel, Cmd+V to paste", True, (150, 150, 150))
        self.screen.blit(hint, (box_rect.centerx - hint.get_width() // 2, box_rect.bottom + 10))

    def _reset_settings(self):
        """Reset to defaults"""
        self.config = Config()
        self.config.flag_height = int(self.config.flag_width * 0.67)
        self.audio.sfx_volume = 0.5
        self.audio.bgm_volume = 0.3
        self.audio.set_sfx_volume(0.5)
        self.audio.set_bgm_volume(0.3)

    def _apply_settings(self):
        """Apply and close settings"""
        self.config.flag_height = int(self.config.flag_width * 0.67)
        self._save_game()
        self.state = GameState.IDLE
        self.spawn_flags()
        self.state = GameState.RUNNING
        self.audio.resume_sfx()

    def _clear_all(self):
        """Clear all data"""
        self.win_counts.clear()
        self.current_champion = None
        self.config = Config()
        self.config.flag_height = int(self.config.flag_width * 0.67)
        self.audio.sfx_path = None
        self.audio.sfx_sound = None
        self.audio.bgm_files.clear()
        self.audio.stop_sfx()
        pygame.mixer.music.stop()
        self._save_game()
        self.state = GameState.IDLE
        self.spawn_flags()
        self.state = GameState.RUNNING

    def run(self):
        """Main game loop"""
        self.stream_key_input_mode = False
        self.stream_key_buffer = ""
        stream_frame_counter = 0.0

        # Headless mode: auto-start streaming and game
        if HEADLESS_MODE:
            print("=" * 50)
            print("HEADLESS MODE - Background Streaming")
            print("=" * 50)
            if self.streamer.stream_key:
                print(f"Starting stream at {self.streamer.output_width}x{self.streamer.output_height}...")
                self.streamer.start()
                self.spawn_flags()
                self.state = GameState.RUNNING
                print("Game running. Press Ctrl+C to stop.")
            else:
                print("ERROR: No stream key set!")
                print("Set stream key in stream_config.json or run normal mode first.")
                return
        else:
            pygame.time.set_timer(pygame.USEREVENT, 500)

        running = True
        try:
            while running:
                # Event handling (skip most in headless mode)
                for event in pygame.event.get():
                    if event.type == pygame.QUIT:
                        running = False

                    elif event.type == pygame.USEREVENT and not HEADLESS_MODE:
                        pygame.time.set_timer(pygame.USEREVENT, 0)
                        self.spawn_flags()
                        self.state = GameState.RUNNING
                        self.audio.play_sfx()
                        self.audio.play_bgm()

                    elif event.type == pygame.USEREVENT + 1 and not HEADLESS_MODE:  # BGM ended
                        self.audio.next_bgm()

                    # Stream key input mode
                    elif not HEADLESS_MODE and self.stream_key_input_mode:
                        self._handle_stream_key_input(event)

                    elif not HEADLESS_MODE and event.type == pygame.KEYDOWN:
                        if event.key == pygame.K_ESCAPE:
                            if self.state == GameState.SETTINGS:
                                self.state = GameState.RUNNING
                                self.audio.resume_sfx()
                            elif self.state == GameState.RUNNING:
                                self.state = GameState.SETTINGS
                                self.audio.pause_sfx()

                    elif not HEADLESS_MODE and self.state == GameState.SETTINGS:
                        self.handle_settings_event(event)

                # Update
                if self.state == GameState.RUNNING:
                    self.update_physics()
                elif self.state == GameState.ROUND_END:
                    now = pygame.time.get_ticks()
                    if now - self.countdown_timer >= 1000:
                        self.countdown -= 1
                        self.countdown_timer = now
                        if self.countdown <= 0:
                            self.spawn_flags()
                            self.state = GameState.RUNNING
                            if not HEADLESS_MODE:
                                self.audio.resume_sfx()

                # Draw
                self.screen.blit(self.bg_surface, (0, 0))

                if self.state != GameState.SETTINGS:
                    self.draw_champion()
                    self.draw_wins_info()
                    self.draw_leaderboard()
                    self.draw_arena()
                    self.draw_flags()
                    self.draw_eliminated()
                    self.draw_cta()
                    self.draw_round_overlay()

                    # LIVE indicator
                    if self.streamer.streaming:
                        live_bg = pygame.Surface((120, 53), pygame.SRCALPHA)
                        pygame.draw.rect(live_bg, (255, 0, 0, 200), live_bg.get_rect(), border_radius=10)
                        self.screen.blit(live_bg, (SCREEN_WIDTH - 144, SCREEN_HEIGHT - 72))
                        live_txt = self.font_small.render("LIVE", True, COLOR_WHITE)
                        self.screen.blit(live_txt, (SCREEN_WIDTH - 132, SCREEN_HEIGHT - 65))

                if not HEADLESS_MODE:
                    self.draw_settings()
                    self._draw_stream_key_input()
                    pygame.display.flip()

                # Send frames at 24fps (60/24 = 2.5 frames interval)
                if self.streamer.streaming:
                    stream_frame_counter += 1.0
                    if stream_frame_counter >= 2.5:
                        self.streamer.send_frame(self.screen)
                        stream_frame_counter -= 2.5

                self.clock.tick(FPS)

        except KeyboardInterrupt:
            print("\nStopping...")

        # Cleanup
        self.streamer.stop()
        self._save_game()
        pygame.quit()


if __name__ == "__main__":
    game = Game()
    game.run()
