Welcome! Share code as fast as possible.

import sys
import json
import requests
from PyQt6.QtWidgets import QApplication, QMainWindow, QLabel, QWidget
from PyQt6.QtCore import Qt, QTimer, pyqtSignal, QRect, QPoint, QRectF
from PyQt6.QtGui import QColor, QPainter, QPen, QScreen, QFont, QFontDatabase, QPainterPath
import win32gui
import win32api
import win32con
import urllib3
import time
from enum import Enum, auto
from typing import Dict, List, Optional
from dataclasses import dataclass

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

class LogLevel(Enum):
    ERROR = auto()
    WARNING = auto()
    INFO = auto()
    DEBUG = auto()

class SpellIcon(QLabel):
    def __init__(self, spell_name, parent=None, debug_mode=False):
        super().__init__(parent)
        self.spell_name = spell_name
        self.timer_value = 0
        self.timer_active = False
        self.summoner_name = ""
        self.debug_mode = debug_mode
        self.parent_widget = parent
        self.overlay = parent.parent()  # Reference to DebugOverlay for logging
        
        # Load custom font
        font_id = QFontDatabase.addApplicationFont("Beaufort-Bold.ttf")
        
        # Get icon size from parent overlay
        self.icon_size = self.overlay.icon_size
        self.setFixedSize(self.icon_size, self.icon_size)
        
        # Scale font size relative to icon size
        font_size = int(self.icon_size * 0.32)  # 32% of icon size
        
        if font_id != -1:
            self.timer_font = QFont("Beaufort", font_size, QFont.Weight.Bold)
        else:
            self.overlay.log("Failed to load Beaufort font, using fallback", LogLevel.WARNING)
            self.timer_font = QFont("Arial", font_size, QFont.Weight.Bold)
            
        self.update_style()

    def update_style(self):
        if self.debug_mode:
            self.setStyleSheet("""
                background-color: rgba(255, 0, 0, 180);
                border: 1px solid yellow;
                color: white;
                font-size: 16px;
                font-weight: bold;
            """)
            self.setText(self.get_spell_abbreviation(self.spell_name))
        else:
            self.setStyleSheet("""      
                background-color: transparent;
                border: none;
                color: white;
                font-size: 16px;
                font-weight: bold;
            """)
            self.setText("")  # Clear the spell abbreviation

    def get_spell_abbreviation(self, spell_name):
        abbreviations = {
            'SummonerFlash': 'F',
            'SummonerDot': 'I',      # Ignite
            'SummonerExhaust': 'E',
            'SummonerBarrier': 'B',
            'SummonerHeal': 'H',
            'SummonerGhost': 'G',
            'SummonerBoost': 'C',    # Cleanse
            'SummonerTeleport': 'T',
            'S12_SummonerTeleportUpgrade': 'T',  # Unleashed Teleport
            'SummonerSmite': 'S',
            'SummonerMana': 'M',     # Clarity
            'SummonerSnowball': 'M'  # Mark (ARAM)
        }
        return abbreviations.get(spell_name, '?')

    def get_summoner_spell_cooldown(self, spell_name, game_time=0, haste=0):
        """Get cooldown from Data Dragon, with special handling for Teleport upgrade"""
        try:
            cooldown = self.parent().data_dragon.summoner_spells.get(spell_name, {}).get('cooldown', [300])[0]
            
            if spell_name == 'SummonerTeleport' and game_time >= 600:
                cooldown = 330  # Unleashed Teleport cooldown (5.5 minutes)
                
            if haste > 0:
                cooldown = cooldown * (100 / (100 + haste))
                
            return cooldown
            
        except Exception as e:
            self.overlay.log(f"Error getting cooldown for {spell_name}: {e}", LogLevel.ERROR)
            return 300  # Fallback cooldown

    def start_timer(self, duration):
        self.overlay.log(
            f"Starting timer for {self.spell_name} ({self.summoner_name}) with duration {duration}s",
            LogLevel.INFO
        )
        self.timer_value = float(duration)
        self.timer_active = True
        self.update()
        
        # Ensure timer is running when we start a new countdown
        if not self.overlay.spell_timer.isActive():
            self.overlay.spell_timer.start(16)
        
    def reset_timer(self):
        self.overlay.log(
            f"Manually resetting timer for {self.spell_name} ({self.summoner_name})",
            LogLevel.INFO
        )
        self.timer_value = 0
        self.timer_active = False
        if self.debug_mode:
            self.setText(self.get_spell_abbreviation(self.spell_name))
        else:
            self.setText("")
        self.update()
        
    def start_timer(self, duration):
        self.overlay.log(
            f"Starting timer for {self.spell_name} ({self.summoner_name}) with duration {duration}s",
            LogLevel.INFO
        )
        self.timer_value = float(duration)
        self.timer_active = True
        self.update()
        
        # Ensure timer is running when we start a new countdown
        if not self.overlay.spell_timer.isActive():
            self.overlay.spell_timer.start(16)

    def update_timer(self):
        if self.timer_active and self.timer_value > 0:
            self.timer_value -= 0.016  # 16ms worth of time
            if self.timer_value <= 0:
                self.timer_value = 0
                self.timer_active = False
                if self.debug_mode:
                    self.setText(self.get_spell_abbreviation(self.spell_name))
                else:
                    self.setText("")
                self.overlay.log(
                    f"Timer completed for {self.spell_name} ({self.summoner_name})",
                    LogLevel.INFO
                )
            elif int(self.timer_value) % 30 == 0:  # Log every 30 seconds
                self.overlay.log(
                    f"Timer {self.spell_name} for {self.summoner_name}: {int(self.timer_value)}s remaining",
                    LogLevel.DEBUG
                )
            self.update()

    def paintEvent(self, event):
        super().paintEvent(event)
        painter = QPainter(self)
        painter.setRenderHint(QPainter.RenderHint.Antialiasing)
        
        if self.timer_active:
            # Scale margins based on icon size
            margin = int(self.icon_size * 0.05)  # 5% margin
            rect = self.rect().adjusted(margin, margin, -margin, -margin)
            
            # Scale shadow offsets based on icon size
            shadow_offset = max(1, int(self.icon_size * 0.05))  # At least 1px, up to 5% of icon size
            shadow_offsets = [
                (-shadow_offset, -shadow_offset),
                (shadow_offset, -shadow_offset),
                (-shadow_offset, shadow_offset),
                (shadow_offset, shadow_offset)
            ]
            
            # Calculate the progress (1.0 to 0.0)
            base_cd = self.overlay.data_dragon.get_spell_cooldown(self.spell_name)  # Fixed data_dragon access
            progress = self.timer_value / base_cd
            
            # Draw square progress indicator
            rect = self.rect().adjusted(2, 2, -2, -2)  # Create margin
            
            # Draw progress overlay (black with 33% opacity)
            if progress > 0:
                painter.setBrush(QColor(40, 50, 70, 200))  # Dark blue-grey with 50% opacity
                span_angle = int(progress * 360 * 16)  # Removed negative sign to reverse direction
                
                # Create a larger rect for the arc (2x the size)
                size_multiplier = 2.0
                center = rect.center()
                larger_size = rect.width() * size_multiplier
                larger_rect = QRectF(
                    center.x() - larger_size/2,
                    center.y() - larger_size/2,
                    larger_size,
                    larger_size
                )
                
                # Create path with larger arc
                path = QPainterPath()
                path.moveTo(float(center.x()), float(center.y()))
                path.arcTo(larger_rect, 90, span_angle/16)  # Start at top (90 degrees)
                path.lineTo(float(center.x()), float(center.y()))
                
                # Clip to original square shape
                painter.setClipRect(rect)
                painter.drawPath(path)
                painter.setClipRect(self.rect())  # Reset clip
            
            # Draw timer text with shadow
            painter.setFont(self.timer_font)
            minutes = int(self.timer_value) // 60
            seconds = int(self.timer_value) % 60
            time_text = f"{minutes}:{seconds:02d}"
            
            # Draw black shadow
            painter.setPen(QPen(Qt.GlobalColor.black, 2))
            for offset_x, offset_y in shadow_offsets:
                painter.drawText(self.rect().adjusted(offset_x, offset_y, offset_x, offset_y), 
                               Qt.AlignmentFlag.AlignCenter, time_text)
            
            # Draw white text
            painter.setPen(QPen(Qt.GlobalColor.white, 1))
            painter.drawText(self.rect(), Qt.AlignmentFlag.AlignCenter, time_text)

@dataclass
class SpellUsage:
    spell_name: str
    cast_count: int = 0
    total_time: float = 0.0
    
    @property
    def average_time(self) -> float:
        return self.total_time / self.cast_count if self.cast_count > 0 else 0.0

@dataclass
class Enemy:
    name: str
    team: str
    spell1: SpellIcon
    spell2: SpellIcon
    haste: int = 0

class DebugOverlay(QMainWindow):
    def __init__(self):
        super().__init__()
        self.debug_mode = False
        self.log_level = LogLevel.ERROR
        self.data_dragon = DataDragon()
        
        # Initialize log history
        self.log_history = []
        
        # Keep existing data structures
        self.spell_icons = {}
        self.summoner_haste = {}
        self.spell_usage: Dict[str, SpellUsage] = {}  # Add spell usage tracking
        
        # New data structure
        self.enemies: Dict[str, Enemy] = {}
        self.game_active = False
        self.game_time = 0
        
        # Window flags for overlay behavior
        self.setWindowFlags(
            Qt.WindowType.FramelessWindowHint |  # No window frame
            Qt.WindowType.WindowStaysOnTopHint | # Always on top
            Qt.WindowType.Tool |                 # Task bar icon hidden
            Qt.WindowType.WindowTransparentForInput  # Add this back to prevent screen flashing
        )
        
        # Window attributes for proper overlay behavior
        self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)  # Transparent background
        self.setAttribute(Qt.WidgetAttribute.WA_ShowWithoutActivating) # Don't steal focus
        self.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents)  # Keep game cursor
        
        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)
        
        screen = QApplication.primaryScreen()
        self.screen_geometry = screen.geometry()
        self.screen_width = self.screen_geometry.width()    
        self.screen_height = self.screen_geometry.height()
        
        # Use relative positions and sizes
        self.left_column_x = int(self.screen_width * 0.4)  # 40% from the left
        self.right_column_x = int(self.screen_width * 0.7)  # 60% from the left
        
        self.first_row_y = int(self.screen_height * 0.25)  # Centered vertically
        self.player_height = int(self.screen_height * 0.1)  # 10% of screen height
        self.spell_spacing = int(self.player_height * 0.5)  # 50% of player height
        
        self.spell_icons = {}   
        self.game_active = False
        self.last_game_check = 0
        self.game_time = 0
        
        self.spell_usage: Dict[str, SpellUsage] = {}  # Track spell usage statistics
        
        self.setup_ui()
        
        # Single timer for all game updates (every 10 seconds)
        self.game_update_timer = QTimer()
        self.game_update_timer.timeout.connect(self.update_game_state_and_data)
        self.game_update_timer.start(10000)  # Check every 10 seconds
        
        # Spell timer runs at 60 FPS (16.67ms) when timers are active
        self.spell_timer = QTimer()
        self.spell_timer.timeout.connect(self.update_spell_timers)
        self.spell_timer.start(16)  # ~60 FPS
        
        # Keep tab and mouse timers at high frequency for responsiveness
        self.tab_timer = QTimer()
        self.tab_timer.timeout.connect(self.check_tab)
        self.tab_timer.start(16)
        
        self.mouse_timer = QTimer()
        self.mouse_timer.timeout.connect(self.check_mouse_click)
        self.mouse_timer.start(16)
        
        # Track last click states
        self.last_left_click = False
        self.last_right_click = False

    def setup_ui(self):
        # Get League window size instead of screen size
        self.screen_width, self.screen_height = self.get_league_window_size()
        self.resize(self.screen_width, self.screen_height)
        
        # Base calculations on game window height for consistent scaling
        scoreboard_center_x = self.screen_width // 2
        
        # Icon size - 3.5% of window height
        self.icon_size = int(self.screen_height * 0.024)

        # Spacing calculations
        self.horizontal_spacing = int(self.icon_size * .35)  # 120% of icon size
        self.spell_spacing = int(self.icon_size * 1.06)  # 110% of icon size
        self.player_height = int(self.icon_size * 2.98)  # 240% of icon size
        
        # Column positions - adjust these offsets to move columns left/right
        self.left_column_x = scoreboard_center_x - self.horizontal_spacing -35 # Subtract to move left column more left
        self.right_column_x = scoreboard_center_x + self.horizontal_spacing   # Add to move right column more right
        
        # Vertical positioning
        scoreboard_top = int(self.screen_height * 0.298)  # 40% from top
        self.first_row_y = scoreboard_top

    def check_mouse_click(self):
        """Handle mouse clicks without affecting the game window"""
        if not self.game_active or not self.isVisible():
            return
            
        left_click = win32api.GetAsyncKeyState(win32con.VK_LBUTTON) & 0x8000
        right_click = win32api.GetAsyncKeyState(win32con.VK_RBUTTON) & 0x8000
        
        cursor_pos = QPoint(win32api.GetCursorPos()[0], win32api.GetCursorPos()[1])
        
        if (left_click and not self.last_left_click) or (right_click and not self.last_right_click):
            for summoner_name, summoner_spells in self.spell_icons.items():
                enemy = self.enemies.get(summoner_name)  # Get Enemy object if it exists
                
                for spell_icon in summoner_spells:
                    icon_pos = spell_icon.mapToGlobal(QPoint(0, 0))
                    icon_rect = QRect(icon_pos.x(), icon_pos.y(), 
                                    spell_icon.width(), spell_icon.height())
                    
                    if icon_rect.contains(cursor_pos):
                        if right_click and not self.last_right_click:
                            spell_icon.reset_timer()
                            self.log(f"Reset timer for {spell_icon.spell_name} for {summoner_name}", LogLevel.INFO)
                        elif left_click and not self.last_left_click and not spell_icon.timer_active:
                            # Get base cooldown and apply haste
                            base_cd = self.data_dragon.get_spell_cooldown(spell_icon.spell_name)
                            haste = enemy.haste if enemy else 0  # Use Enemy dataclass for haste
                            
                            if haste > 0:
                                cd = base_cd * (100 / (100 + haste))
                                cd = round(cd)
                                self.log(f"Applying {haste} haste to {base_cd}s cooldown = {cd}s", LogLevel.INFO)
                            else:
                                cd = base_cd
                            
                            spell_icon.start_timer(cd)
                            self.track_spell_cast(spell_icon.spell_name, cd)  # Track the spell cast
                            self.log(f"Started {spell_icon.spell_name} timer for {cd}s for {summoner_name}", LogLevel.INFO)
        
        self.last_left_click = left_click
        self.last_right_click = right_click

    def check_game_state(self):
        """Check if a game is in progress and initialize if needed"""
        try:
            response = requests.get('https://127.0.0.1:2999/liveclientdata/gamestats', 
                                  verify=False, 
                                  timeout=1.0)
            
            if response.status_code == 200:
                if not self.game_active:
                    self.start_new_game()
            else:
                self.end_game("Game API returned non-200 status")
                
        except requests.exceptions.RequestException as e:
            if self.game_active:
                if isinstance(e, requests.exceptions.ConnectTimeout):
                    self.end_game("Game ended - client disconnected")
                else:
                    self.end_game(f"Connection error: {str(e)}")

    def start_new_game(self):
        """Initialize a new game"""
        try:
            self.game_active = True
            self.game_time = 0
            
            # Initialize game data and start timers
            self.log("Initializing game data...", LogLevel.INFO)
            self.initialize_game_data()
            
            enemy_count = len(self.enemies)
            timer_count = sum(1 for spells in self.spell_icons.values() for _ in spells)
            
            self.log(f"Successfully created {timer_count} timers for {enemy_count} enemies", LogLevel.INFO)
            
            self.game_data_timer.start(10000)  # Update every 10 seconds
            self.log("Game monitoring started", LogLevel.INFO)
            
            if self.debug_mode:
                self.log("\nTimer Status:", LogLevel.DEBUG)
                self.log(f"- Game data timer: {self.game_data_timer.isActive()}", LogLevel.DEBUG)
                self.log(f"- Spell timer: {self.spell_timer.isActive()}", LogLevel.DEBUG)
                
        except Exception as e:
            self.log(f"Error starting new game: {e}", LogLevel.ERROR)

    def end_game(self, reason="Game ended"):
        """Clean up when game ends"""
        if self.game_active:
            self.log(f"Game End Detected: {reason}", LogLevel.INFO)
            self.game_active = False
            
            # Stop all timers
            timer_count = 0
            for summoner_name, spells in self.spell_icons.items():
                for spell in spells:
                    if spell.timer_active:
                        timer_count += 1
                    spell.hide()
                    spell.deleteLater()
            
            self.log(f"Cleaned up {timer_count} active timers", LogLevel.INFO)
            
            # Stop timers
            if self.spell_timer.isActive():
                self.spell_timer.stop()
            if self.game_data_timer.isActive():
                self.game_data_timer.stop()
                
            # Clear all game state
            enemy_count = len(self.enemies)
            self.enemies.clear()
            self.spell_icons.clear()
            self.summoner_haste.clear()
            self.game_time = 0
            
            self.log(f"Cleared data for {enemy_count} enemies", LogLevel.INFO)
            self.log("Waiting for new game...", LogLevel.INFO)
            
            # Export final game stats if in debug mode
            if self.debug_mode:
                self.log_game_stats()

    def initialize_game_data(self):
        """Initialize game data when a new game is detected"""
        try:
            response = requests.get('https://127.0.0.1:2999/liveclientdata/allgamedata', verify=False)
            if response.status_code == 200:
                data = response.json()
                
                # Get active player's team - fix team detection
                active_player_name = data.get('activePlayer', {}).get('summonerName', '')
                if not active_player_name:
                    self.log("Could not get active player name!", LogLevel.ERROR)
                    return
                    
                # Find active player in allPlayers to get team
                for player in data['allPlayers']:
                    if player['summonerName'] == active_player_name:
                        our_team = player['team']
                        enemy_team = 'CHAOS' if our_team == 'ORDER' else 'ORDER'
                        x_position = self.right_column_x if our_team == 'ORDER' else self.left_column_x
                        
                        self.log(f"Creating icons for enemy team: {enemy_team} at x={x_position}", LogLevel.INFO)
                        
                        enemy_players = [p for p in data['allPlayers'] if p['team'] == enemy_team]
                        for i, player in enumerate(enemy_players):
                            self.create_spell_icons(player, i, x_position)
                        
                        self.log(f"Initialization complete - tracking {len(self.enemies)} enemies", LogLevel.INFO)
                        self.log_game_state()  # Log initial state in debug mode
                        return
                        
                self.log("Could not find active player in player list!", LogLevel.ERROR)
                
        except Exception as e:
            self.log(f"Error initializing game data: {e}", LogLevel.ERROR)

    def create_spell_icons(self, player, index, x_position):
        """Helper function to create spell buttons for a player"""
        try:
            summoner_name = player['summonerName']
            
            spell1_raw = player['summonerSpells']['summonerSpellOne']['rawDisplayName']
            spell2_raw = player['summonerSpells']['summonerSpellTwo']['rawDisplayName']
            
            spell1_id = spell1_raw.replace('GeneratedTip_SummonerSpell_', '').replace('_DisplayName', '')
            spell2_id = spell2_raw.replace('GeneratedTip_SummonerSpell_', '').replace('_DisplayName', '')
            
            base_y = self.first_row_y + (index * self.player_height)
            
            spell1 = SpellIcon(spell1_id, self.central_widget, self.debug_mode)
            spell2 = SpellIcon(spell2_id, self.central_widget, self.debug_mode)
            
            spell1.summoner_name = summoner_name
            spell2.summoner_name = summoner_name
            
            spell1.move(x_position, base_y)
            spell2.move(x_position, base_y + self.spell_spacing)
            
            # Create Enemy object with the new data structure
            enemy = Enemy(
                name=summoner_name,
                team=player['team'],
                spell1=spell1,
                spell2=spell2,
                haste=self.calculate_haste(player)
            )
            self.enemies[summoner_name] = enemy
            
            # Keep old data structure working during transition
            self.spell_icons[summoner_name] = [spell1, spell2]
            self.summoner_haste[summoner_name] = enemy.haste
            
            self.log(f"Created spell buttons for {summoner_name}", LogLevel.INFO)
            
        except Exception as e:
            self.log(f"Error creating spell buttons for player: {e}", LogLevel.ERROR)

    def debug_haste_calculation(self, player) -> None:
        """Debug helper for haste calculations"""
        if not self.debug_mode:
            return
            
        summoner_name = player['summonerName']
        cosmic_insight = self.check_for_cosmic_insight(player)
        ionian_boots = self.check_for_ionian_boots(player)
        total_haste = (18 if cosmic_insight else 0) + (10 if ionian_boots else 0)
        
        self.log(f"Haste calculation for {summoner_name}:", LogLevel.DEBUG)
        self.log(f"  Cosmic Insight: {cosmic_insight} (+18)", LogLevel.DEBUG)
        self.log(f"  Ionian Boots: {ionian_boots} (+10)", LogLevel.DEBUG)
        self.log(f"  Total Haste: {total_haste}", LogLevel.DEBUG)

    def calculate_haste(self, player) -> int:
        """Calculate total summoner spell haste for a player"""
        self.debug_haste_calculation(player)  # Add debug info
        has_cosmic_insight = self.check_for_cosmic_insight(player)
        has_ionian_boots = self.check_for_ionian_boots(player)
        return (18 if has_cosmic_insight else 0) + (10 if has_ionian_boots else 0)

    def track_spell_cast(self, spell_name: str, cooldown: float):
        """Track spell usage statistics"""
        if spell_name not in self.spell_usage:
            self.spell_usage[spell_name] = SpellUsage(spell_name)
        
        usage = self.spell_usage[spell_name]
        usage.cast_count += 1
        usage.total_time += cooldown
        
        if self.debug_mode:
            avg_time = usage.average_time
            self.log(f"Spell usage - {spell_name}: {usage.cast_count} casts, avg CD: {avg_time:.1f}s", LogLevel.DEBUG)

    def update_game_data(self):
        """Update game data periodically"""
        if not self.game_active:
            return  
            
        try:
            # Update game time
            time_response = requests.get('https://127.0.0.1:2999/liveclientdata/gamestats', verify=False)
            if time_response.status_code == 200:
                stats = time_response.json()
                old_game_time = self.game_time
                self.game_time = stats.get('gameTime', 0)
                
                # Check for teleport upgrade only when crossing the 10-minute mark
                if old_game_time < 600 and self.game_time >= 600:
                    print("10 minute mark reached - upgrading teleports")
                    for spells in self.spell_icons.values():
                        for spell in spells:
                            if spell.spell_name == 'SummonerTeleport':
                                spell.spell_name = 'S12_SummonerTeleportUpgrade'
                                spell.setText(spell.get_spell_abbreviation('S12_SummonerTeleportUpgrade'))
            
        except Exception as e:
            print(f"Error updating game data: {e}")

    def update_spell_timers(self):
        """Only update if there are active timers"""
        has_active_timers = False
        for summoner_name, spells in self.spell_icons.items():
            for spell in spells:
                if spell.timer_active:
                    has_active_timers = True
                    spell.update_timer()
        
        # Stop the timer if nothing is active
        if not has_active_timers:
            self.spell_timer.stop()

    def check_tab(self):
        if not self.game_active:
            return
            
        foreground_window = win32gui.GetForegroundWindow()
        window_title = win32gui.GetWindowText(foreground_window)
        
        is_league_focused = "League of Legends" in window_title
        tab_pressed = win32api.GetAsyncKeyState(0x09) & 0x8000  # Tab key
        alt_pressed = win32api.GetAsyncKeyState(0x12) & 0x8000  # Alt key
        
        if is_league_focused and tab_pressed and not alt_pressed:  # Only show if Alt is not pressed
            self.show()
        else:
            self.hide()
        
        # Commented out window focus logging to reduce spam
        # if self.debug_mode and tab_pressed:
        #     self.log(f"Active window: {window_title}", LogLevel.DEBUG)
        #     self.log(f"League focused: {is_league_focused}", LogLevel.DEBUG)
        
        # Commented out window focus logging to reduce spam
        # if self.debug_mode and tab_pressed:
        #     self.log(f"Active window: {window_title}", LogLevel.DEBUG)
        #     self.log(f"League focused: {is_league_focused}", LogLevel.DEBUG)

    def log(self, message: str, level: LogLevel):
        """Log a message with the specified level"""
        timestamp = time.strftime('%H:%M:%S')
        
        # Format based on log level
        if level == LogLevel.ERROR:
            prefix = "ERROR"
        elif level == LogLevel.WARNING:
            prefix = "WARN"
        elif level == LogLevel.INFO:
            prefix = "INFO"
        elif level == LogLevel.DEBUG:
            prefix = "DEBUG"
        
        # Format the log message
        log_message = f"[{timestamp}] {prefix}: {message}"
        
        # Always print INFO and ERROR to terminal
        if level in [LogLevel.INFO, LogLevel.ERROR]:
            print(log_message)
        
        # Print DEBUG and WARNING only in debug mode
        elif self.debug_mode:
            print(log_message)
        
        # Keep last 1000 messages in history
        self.log_history.append(log_message)
        if len(self.log_history) > 1000:
            self.log_history.pop(0)

    def print_debug(self, message):
        """Temporary method until all references are updated"""
        self.log(message, LogLevel.DEBUG)

    def toggle_debug_mode(self):
        """Toggle debug mode on/off"""
        self.debug_mode = not self.debug_mode
        # Update all existing spell icons
        for spells in self.spell_icons.values():
            for spell in spells:
                spell.debug_mode = self.debug_mode
                spell.update_style()
        self.print_debug(f"Debug mode {'enabled' if self.debug_mode else 'disabled'}")

    def update_game_state_and_data(self):
        """Combined method for all game updates every 10 seconds"""
        try:
            response = requests.get('https://127.0.0.1:2999/liveclientdata/allgamedata', verify=False)
            was_active = self.game_active
            self.game_active = response.status_code == 200
            
            if not self.game_active:
                if was_active:
                    self.log("Game ended - clearing overlay", LogLevel.INFO)
                    self.enemies.clear()
                    self.spell_icons.clear()
                    self.summoner_haste.clear()
                return
            elif not was_active:
                self.log("Game detected - initializing overlay", LogLevel.INFO)
                self.initialize_game_data()
                return
                
            if response.status_code == 200:
                data = response.json()
                
                # Update game time and check teleport upgrade
                old_game_time = self.game_time
                self.game_time = data.get('gameStats', {}).get('gameTime', 0)
                
                if old_game_time < 600 and self.game_time >= 600:
                    self.log("10 minute mark reached - upgrading teleports", LogLevel.INFO)
                    self.upgrade_teleports()
                
                # Update haste values using Enemy dataclass
                for player in data['allPlayers']:
                    summoner_name = player['summonerName']
                    if summoner_name not in self.enemies:
                        continue
                        
                    new_haste = self.calculate_haste(player)
                    enemy = self.enemies[summoner_name]
                    old_haste = enemy.haste
                    
                    if new_haste != old_haste:
                        self.log(f"Haste changed for {summoner_name}: {old_haste} -> {new_haste}", LogLevel.INFO)
                        # Update both data structures
                        enemy.haste = new_haste
                        self.summoner_haste[summoner_name] = new_haste
                        self.update_active_timers(summoner_name, old_haste, new_haste)
                    
        except requests.exceptions.ConnectionError:
            # Only log if we're transitioning from active to inactive
            if self.game_active:
                self.log("Waiting for game to start...", LogLevel.INFO)
                self.game_active = False
                self.enemies.clear()
                self.spell_icons.clear()
                self.summoner_haste.clear()
        except Exception as e:
            self.log(f"Unexpected error: {e}", LogLevel.ERROR)
            if not isinstance(e, requests.exceptions.ConnectionError):
                self.log(f"Unexpected error type: {type(e)}", LogLevel.ERROR)

    def check_for_cosmic_insight(self, player):
        """Check if a player has the Cosmic Insight rune"""
        runes = player.get('perks', {}).get('styles', [])
        for rune_style in runes:
            selections = rune_style.get('selections', [])
            for selection in selections:
                if selection.get('perk') == 8347:  # Cosmic Insight ID
                    return True
        return False

    def check_for_ionian_boots(self, player):
        """Check if a player has Ionian Boots of Lucidity"""
        for item in player.get('items', []):
            if item.get('itemID') == 3158:  # Ionian Boots item ID
                return True
        return False

    def update_active_timers(self, summoner_name, old_haste, new_haste):
        """Update any active timers when haste changes"""
        if summoner_name not in self.enemies:
            return
            
        enemy = self.enemies[summoner_name]
        for spell in [enemy.spell1, enemy.spell2]:
            if spell.timer_active:
                # Get base cooldown
                base_cd = self.data_dragon.get_spell_cooldown(spell.spell_name)
                
                # Calculate old and new cooldowns
                old_cd = base_cd * (100 / (100 + old_haste))
                new_cd = base_cd * (100 / (100 + new_haste))
                
                # Adjust current timer proportionally
                remaining_pct = spell.timer_value / old_cd
                spell.timer_value = round(new_cd * remaining_pct)
                
                self.log(f"Updated {spell.spell_name} timer for {enemy.name} with new haste {new_haste}", LogLevel.INFO)

    def upgrade_teleports(self):
        """Helper method to upgrade teleport spells at 10 minutes"""
        for enemy in self.enemies.values():
            for spell in [enemy.spell1, enemy.spell2]:
                if spell.spell_name == 'SummonerTeleport':
                    spell.spell_name = 'S12_SummonerTeleportUpgrade'
                    if spell.debug_mode:
                        spell.setText(spell.get_spell_abbreviation('S12_SummonerTeleportUpgrade'))
                    self.log(f"Upgraded teleport for {enemy.name}", LogLevel.INFO)

    def get_enemy(self, summoner_name: str) -> Optional[Enemy]:
        """Safely get an enemy by summoner name"""
        enemy = self.enemies.get(summoner_name)
        if enemy is None:
            self.log(f"Warning: No enemy found for {summoner_name}", LogLevel.WARNING)
        return enemy

    def get_active_spells(self) -> List[SpellIcon]:
        """Get all currently active spell timers"""
        active_spells = []
        for enemy in self.enemies.values():
            for spell in [enemy.spell1, enemy.spell2]:
                if spell.timer_active:
                    active_spells.append(spell)
        return active_spells

    def log_game_state(self):
        """Debug helper to log current game state"""
        if not self.debug_mode:
            return
            
        self.log("Current Game State:", LogLevel.DEBUG)
        self.log(f"Game Active: {self.game_active}", LogLevel.DEBUG)
        self.log(f"Game Time: {self.game_time}", LogLevel.DEBUG)
        
        for enemy in self.enemies.values():
            self.log(f"Enemy: {enemy.name} (Haste: {enemy.haste})", LogLevel.DEBUG)
            for spell in [enemy.spell1, enemy.spell2]:
                if spell.timer_active:
                    self.log(f"  {spell.spell_name}: {spell.timer_value}s remaining", LogLevel.DEBUG)

    def export_debug_info(self) -> dict:
        """Export current state for debugging"""
        debug_info = {
            'game_state': {
                'active': self.game_active,
                'game_time': self.game_time
            },
            'enemies': [
                {
                    'name': enemy.name,
                    'team': enemy.team,
                    'haste': enemy.haste,
                    'spells': [
                        {
                            'name': spell.spell_name,
                            'active': spell.timer_active,
                            'remaining': spell.timer_value if spell.timer_active else 0
                        }
                        for spell in [enemy.spell1, enemy.spell2]
                    ]
                }
                for enemy in self.enemies.values()
            ],
            'spell_usage': [
                {
                    'spell': name,
                    'casts': usage.cast_count,
                    'avg_cooldown': usage.average_time
                }
                for name, usage in self.spell_usage.items()
            ]
        }
        
        if self.debug_mode:
            self.log("Debug info exported", LogLevel.DEBUG)
            self.log(f"Active enemies: {len(self.enemies)}", LogLevel.DEBUG)
            self.log(f"Tracked spells: {len(self.spell_usage)}", LogLevel.DEBUG)
        
        return debug_info

    def get_game_stats(self) -> dict:
        """Get current game statistics"""
        stats = {
            'active_timers': 0,
            'total_spells_tracked': 0,
            'spells_by_type': {},
            'haste_stats': {
                'min': float('inf'),
                'max': 0,
                'avg': 0
            }
        }
        
        total_haste = 0
        for enemy in self.enemies.values():
            # Track haste stats
            stats['haste_stats']['min'] = min(stats['haste_stats']['min'], enemy.haste)
            stats['haste_stats']['max'] = max(stats['haste_stats']['max'], enemy.haste)
            total_haste += enemy.haste
            
            # Track spell stats
            for spell in [enemy.spell1, enemy.spell2]:
                stats['total_spells_tracked'] += 1
                if spell.timer_active:
                    stats['active_timers'] += 1
                
                # Track spell types
                if spell.spell_name not in stats['spells_by_type']:
                    stats['spells_by_type'][spell.spell_name] = 0
                stats['spells_by_type'][spell.spell_name] += 1
        
        if self.enemies:
            stats['haste_stats']['avg'] = total_haste / len(self.enemies)
            if stats['haste_stats']['min'] == float('inf'):
                stats['haste_stats']['min'] = 0
        
        return stats

    def log_game_stats(self):
        """Log current game statistics"""
        if not self.debug_mode:
            return
            
        stats = self.get_game_stats()
        self.log("Current Game Statistics:", LogLevel.DEBUG)
        self.log(f"Active Timers: {stats['active_timers']}", LogLevel.DEBUG)
        self.log(f"Total Spells Tracked: {stats['total_spells_tracked']}", LogLevel.DEBUG)
        
        self.log("\nSpells by Type:", LogLevel.DEBUG)
        for spell_name, count in stats['spells_by_type'].items():
            self.log(f"  {spell_name}: {count}", LogLevel.DEBUG)
        
        self.log("\nHaste Statistics:", LogLevel.DEBUG)
        self.log(f"  Min: {stats['haste_stats']['min']}", LogLevel.DEBUG)
        self.log(f"  Max: {stats['haste_stats']['max']}", LogLevel.DEBUG)
        self.log(f"  Avg: {stats['haste_stats']['avg']:.1f}", LogLevel.DEBUG)

    def check_game_state(self) -> bool:
        """Check if a game is in progress and handle state changes"""
        try:
            response = requests.get('https://127.0.0.1:2999/liveclientdata/gamestats', 
                                  verify=False, 
                                  timeout=1.0)
            
            # Game ended or not active
            if response.status_code != 200:
                if self.game_active:
                    self.end_game("Game client closed - cleaning up timers")
                return False
                
            # New game detected
            if not self.game_active:
                self.log("League game detected - initializing overlay", LogLevel.INFO)
                self.start_new_game()
                
            return True
                
        except requests.exceptions.RequestException as e:
            if self.game_active:
                if isinstance(e, requests.exceptions.ConnectTimeout):
                    self.end_game("Game ended - waiting for new game")
                else:
                    self.end_game(f"Connection error: {str(e)}")
            return False

    def get_league_window_size(self):
        """Get the size of the League of Legends game window"""
        try:
            # Find League of Legends game window specifically
            hwnd = win32gui.FindWindow(None, "League of Legends (TM) Client")
            
            if hwnd != 0:
                # Get window rect
                rect = win32gui.GetWindowRect(hwnd)
                width = rect[2] - rect[0]
                height = rect[3] - rect[1]
                self.log(f"Found League game window: {width}x{height}", LogLevel.INFO)
                return width, height
            else:
                self.log("League game window not found, using screen resolution", LogLevel.WARNING)
                screen = QApplication.primaryScreen()
                size = screen.size()
                return size.width(), size.height()
            
        except Exception as e:
            self.log(f"Error getting League window size: {e}", LogLevel.ERROR)
            # Fallback to screen size
            screen = QApplication.primaryScreen()
            size = screen.size()
            return size.width(), size.height()

class DataDragon:
    def __init__(self):
        self.version = None
        self.summoner_spells = {}
        self.initialize()
        
    def initialize(self):
        """Get the current version and summoner spell data"""
        try:
            # Get current version
            version_url = 'https://ddragon.leagueoflegends.com/realms/na.json'
            version_response = requests.get(version_url)
            if version_response.status_code == 200:
                version_data = version_response.json()
                self.version = version_data['n']['summoner']
                print(f"Retrieved Data Dragon version: {self.version}")
                
                # Get summoner spell data
                spells_url = f'https://ddragon.leagueoflegends.com/cdn/{self.version}/data/en_US/summoner.json'
                spells_response = requests.get(spells_url)
                if spells_response.status_code == 200:
                    spell_data = spells_response.json()
                    
                    # Process each summoner spell
                    for spell_id, spell_info in spell_data['data'].items():
                        if 'CLASSIC' in spell_info.get('modes', []):
                            self.summoner_spells[spell_id] = {
                                'name': spell_info['name'],
                                'cooldown': spell_info['cooldown'][0],
                                'description': spell_info['description']
                            }
                    print(f"Loaded {len(self.summoner_spells)} summoner spells")
                    
        except Exception as e:
            print(f"Error initializing Data Dragon: {e}")
            
    def get_spell_cooldown(self, spell_id):
        """Get the base cooldown for a summoner spell"""
        if spell_id in self.summoner_spells:
            return self.summoner_spells[spell_id]['cooldown']
        return 300  # Default fallback cooldown

def main():
    app = QApplication(sys.argv)
    
    if hasattr(Qt.ApplicationAttribute, 'AA_EnableHighDpiScaling'):
        QApplication.setAttribute(Qt.ApplicationAttribute.AA_EnableHighDpiScaling, True)
    if hasattr(Qt.ApplicationAttribute, 'AA_UseHighDpiPixmaps'):
        QApplication.setAttribute(Qt.ApplicationAttribute.AA_UseHighDpiPixmaps, True)
    
    overlay = DebugOverlay()
    
    # Check command line arguments for debug mode
    if len(sys.argv) > 1 and sys.argv[1] == '--debug':
        overlay.toggle_debug_mode()
    
    sys.exit(app.exec())

if __name__ == "__main__":
    main()