#!/usr/bin/env python3

MAIN_VERSION = "2.4.2"
MAIN_BUILD_DATE = "2025-10-24"
MAIN_DESCRIPTION = "Enhanced install_date collection with 3-step fallback (InstallDate → InstallLocation → UninstallString)"

try:
    from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout
    from PyQt5.QtCore import Qt, QTimer
    from PyQt5.QtGui import QFont
    PYQT5_AVAILABLE = True
except ImportError:
    PYQT5_AVAILABLE = False
    print("PyQt5 not available, using tkinter fallback")

import sys
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
import tkinter.scrolledtext as scrolledtext
import webbrowser
import socket
import platform
import psutil
import math
import logging
import datetime
import subprocess
import re
import os
import threading
import sqlite3
import atexit
import signal
import time


def enable_network_adapters():
    import logging
    import subprocess
    import time
    
    logger = logging.getLogger(__name__)
    logger.info("=== Starting network adapter activation process ===")
    
    try:
        status_script = '''
        $adapters = Get-NetAdapter | Select-Object Name, Status
        foreach ($adapter in $adapters) {
            Write-Host "ADAPTER: $($adapter.Name) - STATUS: $($adapter.Status)"
        }
        '''
        
        status_result = subprocess.run([
            'powershell', '-Command', status_script
        ], capture_output=True, text=True, timeout=30, encoding='cp949',
        creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0)
        
        logger.info("Current network adapter status:")
        if status_result.stdout:
            for line in status_result.stdout.split('\n'):
                if line.strip() and line.startswith('ADAPTER:'):
                    logger.info(f"  {line.strip()}")
        else:
            logger.warning("No output from adapter status check")
        
        powershell_script = '''
        $adapters = Get-NetAdapter | Where-Object { $_.Status -eq "Disabled" }
        $enabledCount = 0
        
        Write-Host "FOUND_DISABLED: $($adapters.Count) adapters"
        
        foreach ($adapter in $adapters) {
            try {
                Write-Host "ENABLING: $($adapter.Name)"
                Enable-NetAdapter -Name $adapter.Name -Confirm:$false
                Write-Host "ENABLED: $($adapter.Name)"
                $enabledCount++
            } catch {
                Write-Host "FAILED: $($adapter.Name) - $($_.Exception.Message)"
            }
        }
        
        Write-Host "TOTAL_ENABLED: $enabledCount"
        '''
        
        result = subprocess.run([
            'powershell', '-Command', powershell_script
        ], capture_output=True, text=True, timeout=30, encoding='cp949',
        creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0)
        
        logger.info(f"PowerShell adapter enable result: {result.stdout}")
        
        if result.stderr:
            logger.warning(f"PowerShell adapter enable warning: {result.stderr}")
        
        enabled_adapters = []
        total_enabled = 0
        
        if result.stdout:
            for line in result.stdout.split('\n'):
                line = line.strip()
                if line.startswith('ENABLED:'):
                    adapter_name = line.replace('ENABLED:', '').strip()
                    enabled_adapters.append(adapter_name)
                elif line.startswith('TOTAL_ENABLED:'):
                    try:
                        total_enabled = int(line.replace('TOTAL_ENABLED:', '').strip())
                    except:
                        pass
        else:
            logger.warning("No PowerShell output to parse")
        
        if total_enabled > 0:
            logger.info(f"Successfully enabled {total_enabled} network adapters: {enabled_adapters}")
            import time
            time.sleep(5)
            return True
        else:
            logger.info("No disabled network adapters found or all adapters were already enabled")
            return True
            
    except subprocess.TimeoutExpired:
        logger.error("Network adapter enable operation timed out")
        return False
    except Exception as e:
        logger.error(f"Network adapter enable error: {e}")
        return False

def get_cache_dir():
    try:
        if '__file__' in globals():
            script_dir = os.path.dirname(os.path.abspath(__file__))
        else:
            script_dir = os.getcwd()
        
        cache_dir = os.path.join(script_dir, 'cache')
        
        if not os.path.exists(cache_dir):
            try:
                os.makedirs(cache_dir)
                print(f"Cache directory created: {cache_dir}")
            except OSError as e:
                print(f"Failed to create cache directory: {e}")
                return script_dir
        
        return cache_dir
        
    except Exception as e:
        print(f"Error in get_cache_dir: {e}")
        fallback_cache = os.path.join(os.getcwd(), 'cache')
        if not os.path.exists(fallback_cache):
            try:
                os.makedirs(fallback_cache)
            except:
                pass
        return fallback_cache

def get_cache_file_path(filename):
    return os.path.join(get_cache_dir(), filename)

def save_data(key, value):
    import json
    
    data_file = get_cache_file_path('app_data.json')
    data = {}
    
    if os.path.exists(data_file):
        try:
            with open(data_file, 'r', encoding='utf-8') as f:
                data = json.load(f)
        except (json.JSONDecodeError, FileNotFoundError):
            data = {}
    
    data[key] = value
    
    try:
        with open(data_file, 'w', encoding='utf-8') as f:
            json.dump(data, f, indent=2, ensure_ascii=False)
    except OSError as e:
        print(f"Failed to save data: {e}")

def load_data(key, default=None):
    import json
    
    data_file = get_cache_file_path('app_data.json')
    
    if os.path.exists(data_file):
        try:
            with open(data_file, 'r', encoding='utf-8') as f:
                data = json.load(f)
                return data.get(key, default)
        except (json.JSONDecodeError, FileNotFoundError):
            return default
    
    return default

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)

class StatusWindow:
    def __init__(self):
        self.status_window = None
        self.status_label = None
        self.is_visible = False
        self.is_dragging = False
        self.drag_start_x = 0
        self.drag_start_y = 0
        self.settings_window = None
        self.edit_mode = False
        self.click_through_enabled = False
        self.last_mouse_pos = (0, 0)
        self.user_has_moved = load_data('user_has_moved', False)
        self.radial_menu = None

    def get_dpi_aware_default_position(self, window_width, window_height):
        try:
            import ctypes
            from ctypes import wintypes

            user32 = ctypes.windll.user32
            user32.SetProcessDPIAware()

            screen_width = user32.GetSystemMetrics(0)
            screen_height = user32.GetSystemMetrics(1)

            try:
                shcore = ctypes.windll.shcore
                MONITOR_DEFAULTTOPRIMARY = 1
                MDT_EFFECTIVE_DPI = 0

                monitor = user32.MonitorFromPoint(wintypes.POINT(0, 0), MONITOR_DEFAULTTOPRIMARY)
                dpi_x = ctypes.c_uint()
                dpi_y = ctypes.c_uint()
                shcore.GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, ctypes.byref(dpi_x), ctypes.byref(dpi_y))

                dpi_scale = dpi_x.value / 96.0
                logger.info(f"DPI Scale detected: {dpi_scale} ({dpi_x.value} DPI)")
            except:
                dpi_scale = 1.0
                logger.warning("Could not detect DPI scale, using 1.0")

            taskbar_height = 5
            right_margin = -190

            x = screen_width - window_width - right_margin
            y = screen_height - window_height - taskbar_height

            logger.info(f"Screen: {screen_width}x{screen_height}, DPI Scale: {dpi_scale}, Position: ({x}, {y})")

            return int(x), int(y)

        except Exception as e:
            logger.error(f"Error calculating DPI-aware position: {e}")
            return 1722, 990
        
    def create_status_window(self):
        if hasattr(self, 'qt_widget') and self.qt_widget:
            return

        logger.info("Creating PyQt5 status window...")

        try:
            from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QDesktopWidget
            from PyQt5.QtCore import Qt, QTimer
            from PyQt5.QtGui import QFont
            import sys
        except ImportError:
            logger.error("PyQt5 not installed. Please install it with: pip install PyQt5")
            return

        if not QApplication.instance():
            self.qt_app = QApplication(sys.argv)
        else:
            self.qt_app = QApplication.instance()

        position = load_data('status_position', None)
        window_width = 350
        window_height = 80

        if position:
            x, y = position
        else:
            x, y = self.get_dpi_aware_default_position(window_width, window_height)

        if self.edit_mode:
            self.create_edit_mode_window(x, y, window_width, window_height)
        else:
            self.create_pyqt_normal_window(x, y, window_width, window_height)
        
        self.is_visible = True
        self.update_status()
        logger.info(f"Status window created at ({x}, {y}), edit_mode={self.edit_mode}")

    def create_pyqt_normal_window(self, x, y, width, height):
        try:
            from PyQt5.QtWidgets import QWidget, QLabel, QVBoxLayout
            from PyQt5.QtCore import Qt, QTimer
            from PyQt5.QtGui import QFont

            self.qt_widget = QWidget()

            hotkey_type = load_data('hotkey_type', 'mouse_wheel')

            window_flags = (
                Qt.WindowStaysOnTopHint |
                Qt.FramelessWindowHint |
                Qt.Tool
            )

            if hotkey_type != 'click_status':
                window_flags |= Qt.WindowTransparentForInput

            self.qt_widget.setWindowFlags(window_flags)

            self.qt_widget.move(x, y)

            self.qt_widget.setAttribute(Qt.WA_TranslucentBackground)

            layout = QVBoxLayout(self.qt_widget)
            layout.setContentsMargins(8, 8, 8, 8)

            self.qt_label = QLabel("인터넷 상태 확인 중...", self.qt_widget)
            self.qt_label.setStyleSheet("""
                QLabel {
                    background-color: rgba(44, 62, 80, 220);
                    color: white;
                    padding: 8px 16px;
                    border-radius: 5px;
                    border: 1px solid rgba(255, 255, 255, 100);
                }
            """)
            self.qt_label.setFont(QFont("Arial", 9))
            self.qt_label.setAlignment(Qt.AlignCenter)

            layout.addWidget(self.qt_label)

            self.qt_widget.adjustSize()

            if hotkey_type == 'click_status':
                self.qt_widget.mousePressEvent = self.qt_widget_clicked
                logger.info("Status window click event handler registered")

            self.qt_timer = QTimer()
            self.qt_timer.timeout.connect(self.update_pyqt_status)
            self.qt_timer.start(5000)

            self.qt_widget.show()

            saved_opacity = load_data('status_opacity', 0.85)
            self.qt_widget.setWindowOpacity(saved_opacity)
            logger.info(f"PyQt5 창 투명도 {saved_opacity:.2f} 적용됨")

            self.qt_widget.adjustSize()
            self.saved_qt_size = (self.qt_widget.width(), self.qt_widget.height())
            logger.info(f"PyQt5 window size saved: {self.saved_qt_size}")

            self.status_window = None
            self.status_label = self.qt_label

            logger.info(f"PyQt5 normal window created successfully, click_status enabled: {hotkey_type == 'click_status'}")

        except Exception as e:
            logger.error(f"PyQt window creation failed: {e}")
            import traceback
            traceback.print_exc()
    
    def create_pyqt_perfect_window(self, x, y, width, height):
        try:
            from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout
            from PyQt5.QtCore import Qt, QTimer
            from PyQt5.QtGui import QFont
            import sys
            
            if not QApplication.instance():
                self.qt_app = QApplication(sys.argv)
            else:
                self.qt_app = QApplication.instance()
            
            self.qt_widget = QWidget()
            
            self.qt_widget.setWindowFlags(
                Qt.FramelessWindowHint |
                Qt.WindowStaysOnTopHint |
                Qt.Tool |
                Qt.WindowTransparentForInput
            )
            
            self.qt_widget.setAttribute(Qt.WA_TranslucentBackground)
            
            self.qt_widget.resize(width, height)
            self.qt_widget.move(x, y)
            
            self.initial_x = x
            self.initial_width = width
            
            layout = QVBoxLayout(self.qt_widget)
            layout.setContentsMargins(8, 8, 8, 8)
            
            self.qt_label = QLabel("인터넷 상태 확인 중...", self.qt_widget)
            self.qt_label.setStyleSheet("""
                QLabel {
                    background-color: rgba(44, 62, 80, 220);
                    color: white;
                    padding: 8px;
                    border: 1px solid rgba(255, 255, 255, 100);
                    border-radius: 5px;
                    font-size: 9pt;
                }
            """)
            self.qt_label.setFont(QFont("Arial", 9))
            self.qt_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
            self.qt_label.setWordWrap(False)
            
            layout.addWidget(self.qt_label)
            
            self.qt_timer = QTimer()
            self.qt_timer.timeout.connect(self.update_pyqt_perfect_status)
            self.qt_timer.start(5000)
            
            self.qt_widget.show()

            saved_opacity = load_data('network_status_opacity', 0.9)
            self.qt_widget.setWindowOpacity(saved_opacity)
            logger.info(f"PyQt5 창 투명도 {saved_opacity:.2f} 적용됨")

            self.update_pyqt_perfect_status()
            
            self.setup_pyqt_click_listener(x, y, width, height)
            
            self.status_window = None
            self.status_label = self.qt_label
            
            logger.info("Perfect PyQt5 click-through window created successfully")
            logger.info("Window should be visible but completely click-through")
            
        except ImportError:
            logger.error("PyQt5 not available, falling back to simple window")
            self.create_simple_window(x, y, width, height)
        except Exception as e:
            logger.error(f"Perfect PyQt window creation failed: {e}")
            import traceback
            traceback.print_exc()
            self.create_simple_window(x, y, width, height)
    
    def update_pyqt_perfect_status(self):
        try:
            if hasattr(self, 'qt_label') and self.qt_label:
                is_connected = self.check_internet_connection()
                
                if is_connected:
                    network_info = self.get_network_info()
                    status_text = f"🟢 온라인 - {network_info}"
                else:
                    status_text = "🔴 오프라인"
                
                self.qt_label.setText(status_text)

                self.current_status_text = status_text

                self.current_status_text = status_text

                self.adjust_window_size()
                
        except Exception as e:
            logger.error(f"Perfect PyQt status update error: {e}")
    
    def adjust_window_size(self):
        try:
            if hasattr(self, 'qt_label') and hasattr(self, 'qt_widget'):
                from PyQt5.QtCore import Qt
                
                font_metrics = self.qt_label.fontMetrics()
                try:
                    text_width = font_metrics.horizontalAdvance(self.qt_label.text())
                except AttributeError:
                    text_width = font_metrics.width(self.qt_label.text())
                text_height = font_metrics.height()
                
                widget_width = text_width + 16
                widget_height = max(text_height + 16, 80)
                
                self.qt_widget.resize(widget_width, widget_height)
                
                if hasattr(self, 'initial_x') and not self.edit_mode and not getattr(self, 'is_dragging', False) and not getattr(self, 'user_has_moved', False):
                    screen_width = self.qt_widget.screen().geometry().width()
                    new_x = screen_width - widget_width - 2
                    self.qt_widget.move(new_x, self.qt_widget.y())
                
        except Exception as e:
            logger.error(f"Window size adjustment error: {e}")
    
    def setup_pyqt_click_listener(self, x, y, width, height):
        try:
            hotkey_type = load_data('hotkey_type', 'mouse_wheel')
            if hotkey_type != 'click_status':
                logger.info(f"PyQt click listener not needed for hotkey type: {hotkey_type}")
                return
            
            if hasattr(self, 'pyqt_mouse_listener'):
                try:
                    self.pyqt_mouse_listener.stop()
                    delattr(self, 'pyqt_mouse_listener')
                    logger.info("Existing PyQt mouse listener cleaned up")
                except:
                    pass
            
            try:
                from pynput import mouse as pynput_mouse
                
                def on_click(mouse_x, mouse_y, button, pressed):
                    if button == pynput_mouse.Button.left and pressed:
                        if hasattr(self, 'qt_widget') and self.qt_widget:
                            try:
                                from PyQt5.QtCore import QPoint
                                global_pos = self.qt_widget.mapToGlobal(QPoint(0, 0))
                                widget_x = global_pos.x()
                                widget_y = global_pos.y()
                            except:
                                widget_x = self.qt_widget.x()
                                widget_y = self.qt_widget.y()

                            widget_width = self.qt_widget.width()
                            widget_height = self.qt_widget.height()

                            if (widget_x <= mouse_x <= widget_x + widget_width and
                                widget_y <= mouse_y <= widget_y + widget_height):
                                try:
                                    import time
                                    current_time = time.time()
                                    if hasattr(self, '_last_pyqt_click_time') and current_time - self._last_pyqt_click_time < 0.2:
                                        logger.info("Ignoring duplicate PyQt click within 200ms")
                                        return
                                    self._last_pyqt_click_time = current_time

                                    center_x = mouse_x
                                    center_y = mouse_y
                                    
                                    if hasattr(self, 'radial_menu') and self.radial_menu and hasattr(self.radial_menu, 'settings_window'):
                                        settings_window = self.radial_menu.settings_window
                                        if hasattr(settings_window, 'settings_root') and settings_window.settings_root and settings_window.settings_root.winfo_exists():
                                            logger.info("Settings window is open - ignoring status click")
                                            return
                                    
                                    if hasattr(self, '_settings_button_clicked_time'):
                                        import time
                                        if time.time() - self._settings_button_clicked_time < 1.0:
                                            logger.info("Settings button recently clicked - ignoring status click")
                                            return
                                    
                                    if hasattr(self, 'edit_mode') and self.edit_mode:
                                        logger.info("Edit mode is active - ignoring status click")
                                        return
                                    
                                    if hasattr(self, 'radial_menu') and self.radial_menu and hasattr(self.radial_menu, 'overlay') and self.radial_menu.overlay:
                                        logger.info("Radial menu already open - ignoring status click")
                                        return
                                    
                                    if hasattr(self, '_radial_menu_active') and self._radial_menu_active:
                                        logger.info("Radial menu active flag is set - ignoring status click")
                                        return
                                    
                                    current_hotkey = load_data('hotkey_type', 'mouse_wheel')
                                    if current_hotkey != 'click_status':
                                        logger.info(f"Hotkey changed from click_status to {current_hotkey} - ignoring PyQt status click")
                                        return
                                    
                                    if hasattr(self, 'radial_menu') and self.radial_menu:
                                        logger.info(f"PyQt status window clicked - opening radial menu at center ({center_x}, {center_y})")
                                        if hasattr(self.radial_menu, 'overlay') and self.radial_menu.overlay:
                                            logger.info("Menu already open - will close and reopen at new position")
                                        try:
                                            self.radial_menu.create_menu(center_x, center_y)
                                            logger.info("PyQt radial menu create_menu called successfully")
                                            if hasattr(self.radial_menu, 'overlay') and self.radial_menu.overlay:
                                                logger.info("Menu successfully created and displayed")
                                            else:
                                                logger.warning("Menu create_menu called but overlay not found")
                                        except Exception as menu_error:
                                            logger.error(f"Error in create_menu: {menu_error}")
                                            import traceback
                                            traceback.print_exc()
                                    else:
                                        logger.error(f"Radial menu not available - hasattr: {hasattr(self, 'radial_menu')}, radial_menu: {getattr(self, 'radial_menu', None)}")
                                except Exception as e:
                                    logger.error(f"Error handling PyQt status click: {e}")
                
                self.pyqt_mouse_listener = pynput_mouse.Listener(on_click=on_click)
                self.pyqt_mouse_listener.start()
                
                logger.info("PyQt click listener setup completed")
                
            except ImportError:
                logger.warning("pynput not available for PyQt click detection")
                
        except Exception as e:
            logger.error(f"PyQt click listener setup error: {e}")
    
    def create_autohotkey_style_window(self, x, y, width, height):
        try:
            import ctypes
            from ctypes import wintypes
            
            self.status_window = tk.Toplevel()
            self.status_window.overrideredirect(True)
            self.status_window.attributes('-topmost', True)
            saved_opacity = load_data('status_opacity', 0.85)
            self.status_window.attributes('-alpha', saved_opacity)
            self.status_window.configure(bg='#2c3e50')
            
            self.status_window.geometry(f"{width}x{height}+{x}+{y}")
            
            self.status_label = tk.Label(
                self.status_window,
                text="인터넷 상태 확인 중...",
                bg='#2c3e50',
                fg='white',
                font=('Arial', 9),
                justify='left',
                anchor='w'
            )
            self.status_label.pack(fill=tk.BOTH, expand=True, padx=8, pady=8)
            
            self.status_window.after(200, lambda: self.apply_autohotkey_style())
            self.status_window.after(1000, lambda: self.verify_and_reapply_style())
            
            logger.info("AutoHotkey style window created")
            
        except Exception as e:
            logger.error(f"AutoHotkey style window creation failed: {e}")
            self.create_simple_window(x, y, width, height)
    
    def apply_autohotkey_style(self):
        if platform.system() != "Windows":
            return
            
        try:
            import ctypes
            
            hwnd = self.status_window.winfo_id()
            
            GWL_EXSTYLE = -20
            WS_EX_LAYERED = 0x00080000
            WS_EX_TRANSPARENT = 0x00000020
            WS_EX_NOACTIVATE = 0x08000000
            WS_EX_TOOLWINDOW = 0x00000080
            
            current_style = ctypes.windll.user32.GetWindowLongW(hwnd, GWL_EXSTYLE)
            
            new_style = (current_style | 
                        WS_EX_LAYERED | 
                        WS_EX_TRANSPARENT | 
                        WS_EX_NOACTIVATE | 
                        WS_EX_TOOLWINDOW)
            
            result = ctypes.windll.user32.SetWindowLongW(hwnd, GWL_EXSTYLE, new_style)
            
            if result:
                LWA_ALPHA = 0x00000002
                ctypes.windll.user32.SetLayeredWindowAttributes(
                    hwnd, 0, int(255 * 0.9), LWA_ALPHA
                )
                
                HWND_TOPMOST = -1
                SWP_NOMOVE = 0x0002
                SWP_NOSIZE = 0x0001
                SWP_NOACTIVATE = 0x0010
                
                ctypes.windll.user32.SetWindowPos(
                    hwnd, HWND_TOPMOST, 0, 0, 0, 0,
                    SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE
                )
                
                logger.info("Simple AutoHotkey style applied successfully")
                logger.info("Click-through should now work properly")
            else:
                logger.error("Failed to apply window styles")
                
        except Exception as e:
            logger.error(f"AutoHotkey style application failed: {e}")
            import traceback
            traceback.print_exc()
    
    def verify_and_reapply_style(self):
        if not self.status_window or not self.status_window.winfo_exists():
            return
            
        try:
            import ctypes
            hwnd = self.status_window.winfo_id()
            
            GWL_EXSTYLE = -20
            current_style = ctypes.windll.user32.GetWindowLongW(hwnd, GWL_EXSTYLE)
            
            WS_EX_TRANSPARENT = 0x00000020
            WS_EX_TOOLWINDOW = 0x00000080
            
            has_transparent = bool(current_style & WS_EX_TRANSPARENT)
            has_toolwindow = bool(current_style & WS_EX_TOOLWINDOW)
            
            if not (has_transparent and has_toolwindow):
                logger.warning("Style verification failed, reapplying...")
                self.apply_autohotkey_style()
            else:
                logger.info("Style verification passed - click-through is active")
                
        except Exception as e:
            logger.error(f"Style verification error: {e}")
    
    
    def create_edit_mode_window(self, x, y, width, height):
        self.status_window = tk.Toplevel()
        self.status_window.overrideredirect(True)
        self.status_window.attributes('-topmost', True)
        self.status_window.attributes('-alpha', 0.9)

        self.status_window.configure(bg='#e74c3c')

        edit_text = "🔴 편집모드 - 드래그하여 이동"

        if hasattr(self, 'saved_qt_size'):
            dynamic_width, dynamic_height = self.saved_qt_size
            logger.info(f"Using saved PyQt window size: {dynamic_width}x{dynamic_height}")
        else:
            dynamic_width = 150
            dynamic_height = 35
            logger.info(f"Using default size: {dynamic_width}x{dynamic_height}")

        final_x = x
        final_y = y

        self.status_window.geometry(f"{dynamic_width}x{dynamic_height}+{final_x}+{final_y}")
        logger.info(f"Edit mode window created at saved position: size={dynamic_width}x{dynamic_height}, pos=({final_x},{final_y})")

        self.status_label = tk.Label(
            self.status_window,
            text=edit_text,
            bg='#e74c3c',
            fg='white',
            font=('Arial', 9, 'bold'),
            justify='left',
            anchor='w',
            cursor='hand2'
        )
        self.status_label.pack(fill=tk.BOTH, expand=True, padx=8, pady=8)
        
        self.enable_drag_events()
        
        logger.info("Edit mode window created with drag functionality")
    
    def create_click_through_window(self, x, y, width, height):
        self.status_window = tk.Toplevel()
        self.status_window.overrideredirect(True)
        self.status_window.attributes('-topmost', True)
        saved_opacity = load_data('status_opacity', 0.85)
        self.status_window.attributes('-alpha', saved_opacity)
        
        self.status_window.geometry(f"{width}x{height}+{x}+{y}")
        
        if platform.system() == "Windows":
            try:
                transparent_color = '#000001'
                self.status_window.configure(bg=transparent_color)
                self.status_window.attributes('-transparentcolor', transparent_color)
                
                visible_frame = tk.Frame(
                    self.status_window,
                    bg='#2c3e50',
                    relief='raised',
                    bd=1
                )
                visible_frame.place(x=0, y=0, width=width, height=height)
                
                self.status_label = tk.Label(
                    visible_frame,
                    text="인터넷 상태 확인 중...",
                    bg='#2c3e50',
                    fg='white',
                    font=('Arial', 9),
                    justify='left',
                    anchor='w'
                )
                self.status_label.pack(fill=tk.BOTH, expand=True, padx=8, pady=8)
                
                self.status_window.after(200, self.apply_windows_click_through)
                
                logger.info("Windows click-through window created with transparent color method")
                
            except Exception as e:
                logger.error(f"Transparent color method failed: {e}")
                self.create_win32_click_through_window(x, y, width, height)
        else:
            self.status_window.configure(bg='#2c3e50')
            self.status_label = tk.Label(
                self.status_window,
                text="인터넷 상태 확인 중...",
                bg='#2c3e50',
                fg='white',
                font=('Arial', 9),
                justify='left',
                anchor='w'
            )
            self.status_label.pack(fill=tk.BOTH, expand=True, padx=8, pady=8)
    
    def create_win32_click_through_window(self, x, y, width, height):
        try:
            if self.status_window:
                self.status_window.destroy()
                
            self.status_window = tk.Toplevel()
            self.status_window.overrideredirect(True)
            self.status_window.attributes('-topmost', True)
            saved_opacity = load_data('status_opacity', 0.85)
            self.status_window.attributes('-alpha', saved_opacity)
            self.status_window.configure(bg='#2c3e50')
            
            self.status_window.geometry(f"{width}x{height}+{x}+{y}")
            
            self.status_label = tk.Label(
                self.status_window,
                text="인터넷 상태 확인 중...",
                bg='#2c3e50',
                fg='white',
                font=('Arial', 9),
                justify='left',
                anchor='w'
            )
            self.status_label.pack(fill=tk.BOTH, expand=True, padx=8, pady=8)
            
            self.status_window.after(200, self.apply_win32_click_through)
            
            logger.info("Win32 API click-through window created")
            
        except Exception as e:
            logger.error(f"Win32 click-through window creation failed: {e}")
    
    def apply_win32_click_through(self):
        if platform.system() != "Windows":
            return
            
        try:
            import ctypes
            hwnd = self.status_window.winfo_id()
            
            GWL_EXSTYLE = -20
            WS_EX_LAYERED = 0x00080000
            WS_EX_TRANSPARENT = 0x00000020
            WS_EX_NOACTIVATE = 0x08000000
            
            style = ctypes.windll.user32.GetWindowLongW(hwnd, GWL_EXSTYLE)
            new_style = style | WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_NOACTIVATE
            
            result = ctypes.windll.user32.SetWindowLongW(hwnd, GWL_EXSTYLE, new_style)
            
            if result:
                HWND_BOTTOM = 1
                SWP_NOSIZE = 0x0001
                SWP_NOMOVE = 0x0002
                SWP_NOACTIVATE = 0x0010
                
                ctypes.windll.user32.SetWindowPos(
                    hwnd, HWND_BOTTOM, 0, 0, 0, 0,
                    SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE
                )
                
                self.click_through_enabled = True
                logger.info("Win32 enhanced click-through applied successfully")
            else:
                logger.error("Failed to apply Win32 click-through")
                
        except Exception as e:
            logger.error(f"Win32 click-through error: {e}")
            logger.info("Consider using PyQt for better click-through support")
    
    def create_pyqt_click_through_window(self, x, y, width, height):
        try:
            from PyQt5.QtWidgets import QApplication, QWidget, QLabel
            from PyQt5.QtCore import Qt, QTimer
            from PyQt5.QtGui import QFont
            import sys
            
            if not QApplication.instance():
                self.qt_app = QApplication(sys.argv)
            else:
                self.qt_app = QApplication.instance()
            
            self.qt_widget = QWidget()

            hotkey_type = load_data('hotkey_type', 'mouse_wheel')

            window_flags = (
                Qt.WindowStaysOnTopHint |
                Qt.FramelessWindowHint |
                Qt.Tool
            )

            if hotkey_type != 'click_status':
                window_flags |= Qt.WindowTransparentForInput

            self.qt_widget.setWindowFlags(window_flags)
            
            self.qt_widget.setGeometry(x, y, width, height)
            self.qt_widget.setStyleSheet("""
                QWidget {
                    background-color: rgba(44, 62, 80, 220);
                    border: 1px solid rgba(255, 255, 255, 100);
                    border-radius: 5px;
                }
            """)
            
            self.qt_label = QLabel("인터넷 상태 확인 중...", self.qt_widget)
            self.qt_label.setStyleSheet("""
                QLabel {
                    color: white;
                    background-color: transparent;
                    padding: 8px;
                }
            """)
            self.qt_label.setFont(QFont("Arial", 9))
            self.qt_label.setGeometry(0, 0, width, height)
            self.qt_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
            
            self.qt_timer = QTimer()
            self.qt_timer.timeout.connect(self.update_pyqt_status)
            self.qt_timer.start(5000)

            if hotkey_type == 'click_status':
                self.qt_widget.mousePressEvent = self.qt_widget_clicked

            self.qt_widget.show()

            saved_opacity = load_data('status_opacity', 0.85)
            self.qt_widget.setWindowOpacity(saved_opacity)
            logger.info(f"PyQt5 클릭통과 창 투명도 {saved_opacity:.2f} 적용됨")

            self.status_window = None
            self.status_label = self.qt_label
            
            logger.info("PyQt click-through window created successfully")
            
        except ImportError:
            logger.error("PyQt5 not available, falling back to tkinter")
            self.create_win32_click_through_window(x, y, width, height)
        except Exception as e:
            logger.error(f"PyQt window creation failed: {e}")
            self.create_win32_click_through_window(x, y, width, height)
    
    def qt_widget_clicked(self, event):
        try:
            if self.edit_mode:
                return

            import time
            current_time = time.time()
            if hasattr(self, '_last_click_time') and current_time - self._last_click_time < 0.3:
                logger.info("Ignoring duplicate PyQt click within 300ms")
                return
            self._last_click_time = current_time

            if hasattr(self, 'radial_menu') and self.radial_menu and hasattr(self.radial_menu, 'settings_window'):
                settings_window = self.radial_menu.settings_window
                if hasattr(settings_window, 'settings_root') and settings_window.settings_root and settings_window.settings_root.winfo_exists():
                    logger.info("Settings window is open - ignoring PyQt click")
                    return

            if hasattr(self, 'radial_menu') and self.radial_menu and hasattr(self.radial_menu, 'overlay') and self.radial_menu.overlay:
                logger.info("Radial menu already open - ignoring PyQt click")
                return

            logger.info("PyQt status window clicked - opening radial menu")

            try:
                try:
                    from pynput import mouse as pynput_mouse
                    controller = pynput_mouse.Controller()
                    x, y = controller.position
                except ImportError:
                    import mouse
                    x, y = mouse.get_position()

                if hasattr(self, 'radial_menu') and self.radial_menu:
                    self.radial_menu.create_menu(x, y)
                else:
                    logger.error("Radial menu not initialized")

            except Exception as e:
                logger.error(f"Error getting mouse position or creating menu: {e}")

        except Exception as e:
            logger.error(f"Error in PyQt widget click handler: {e}")

    def update_pyqt_status(self):
        try:
            if hasattr(self, 'qt_label') and self.qt_label:
                is_connected = self.check_internet_connection()

                if is_connected:
                    network_info = self.get_network_info()
                    status_text = f"🟢 온라인 - {network_info}"
                else:
                    status_text = "🔴 오프라인"

                self.qt_label.setText(status_text)

                self.current_status_text = status_text

                if hasattr(self, 'qt_widget') and self.qt_widget:
                    self.qt_widget.adjustSize()
                    self.saved_qt_size = (self.qt_widget.width(), self.qt_widget.height())
                    logger.debug(f"Updated PyQt5 window size: {self.saved_qt_size}")

        except Exception as e:
            logger.error(f"PyQt status update error: {e}")
    
    def create_pysimplegui_window(self, x, y, width, height):
        try:
            import PySimpleGUI as sg
            import threading
            
            sg.theme('DarkBlue3')
            
            layout = [
                [sg.Text('인터넷 상태 확인 중...', 
                        key='-STATUS-',
                        background_color='#2c3e50',
                        text_color='white',
                        font=('Arial', 9),
                        size=(25, 3),
                        justification='left')]
            ]
            
            self.sg_window = sg.Window(
                'Status',
                layout,
                location=(x, y),
                size=(width, height),
                no_titlebar=True,
                keep_on_top=True,
                alpha_channel=0.85,
                grab_anywhere=False,
                background_color='#2c3e50',
                margins=(8, 8),
                element_padding=(0, 0),
                finalize=True,
                transparent_color='#000001'
            )
            
            if platform.system() == "Windows":
                try:
                    import ctypes
                    hwnd = self.sg_window.TKroot.winfo_id()
                    
                    GWL_EXSTYLE = -20
                    WS_EX_LAYERED = 0x00080000
                    WS_EX_TRANSPARENT = 0x00000020
                    
                    style = ctypes.windll.user32.GetWindowLongW(hwnd, GWL_EXSTYLE)
                    new_style = style | WS_EX_LAYERED | WS_EX_TRANSPARENT
                    
                    ctypes.windll.user32.SetWindowLongW(hwnd, GWL_EXSTYLE, new_style)
                    logger.info("PySimpleGUI click-through applied")
                    
                except Exception as e:
                    logger.error(f"PySimpleGUI click-through failed: {e}")
            
            self.sg_running = True
            self.sg_thread = threading.Thread(target=self.run_pysimplegui_loop, daemon=True)
            self.sg_thread.start()
            
            self.status_window = None
            self.status_label = None
            
            logger.info("PySimpleGUI window created successfully")
            
        except ImportError:
            logger.error("PySimpleGUI not available, falling back")
            self.create_win32_click_through_window(x, y, width, height)
        except Exception as e:
            logger.error(f"PySimpleGUI window creation failed: {e}")
            self.create_win32_click_through_window(x, y, width, height)
    
    def run_pysimplegui_loop(self):
        try:
            while self.sg_running and hasattr(self, 'sg_window'):
                event, values = self.sg_window.read(timeout=5000)
                
                if event in (None, sg.WIN_CLOSED):
                    break
                
                if self.sg_running:
                    self.update_pysimplegui_status()
                    
        except Exception as e:
            logger.error(f"PySimpleGUI loop error: {e}")
        finally:
            if hasattr(self, 'sg_window'):
                try:
                    self.sg_window.close()
                except:
                    pass
    
    def update_pysimplegui_status(self):
        try:
            if hasattr(self, 'sg_window') and self.sg_window:
                is_connected = self.check_internet_connection()
                
                if is_connected:
                    network_info = self.get_network_info()
                    status_text = f"🟢 온라인 - {network_info}"
                else:
                    status_text = "🔴 오프라인"
                
                self.sg_window['-STATUS-'].update(status_text)
                
        except Exception as e:
            logger.error(f"PySimpleGUI status update error: {e}")
    
    def create_qt_transparent_window(self, x, y, width, height):
        try:
            from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout
            from PyQt5.QtCore import Qt, QTimer
            from PyQt5.QtGui import QFont
            import sys
            
            if not QApplication.instance():
                self.qt_app = QApplication(sys.argv)
            else:
                self.qt_app = QApplication.instance()
            
            self.qt_widget = QWidget()
            
            self.qt_widget.setAttribute(Qt.WA_TransparentForMouseEvents, True)
            
            self.qt_widget.setWindowFlags(
                Qt.WindowStaysOnTopHint | 
                Qt.FramelessWindowHint | 
                Qt.Tool
            )
            
            self.qt_widget.setGeometry(x, y, width, height)
            self.qt_widget.setStyleSheet("""
                QWidget {
                    background-color: rgba(44, 62, 80, 200);
                    border: 1px solid rgba(255, 255, 255, 80);
                    border-radius: 8px;
                }
            """)
            
            layout = QVBoxLayout()
            layout.setContentsMargins(8, 8, 8, 8)
            
            self.qt_label = QLabel("인터넷 상태 확인 중...")
            self.qt_label.setStyleSheet("""
                QLabel {
                    color: white;
                    background-color: transparent;
                    font-size: 9pt;
                }
            """)
            self.qt_label.setFont(QFont("Arial", 9))
            self.qt_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
            self.qt_label.setWordWrap(False)
            
            layout.addWidget(self.qt_label)
            self.qt_widget.setLayout(layout)
            
            self.qt_timer = QTimer()
            self.qt_timer.timeout.connect(self.update_pyqt_status)
            self.qt_timer.start(5000)
            
            self.qt_widget.show()

            saved_opacity = load_data('status_opacity', 0.85)
            self.qt_widget.setWindowOpacity(saved_opacity)
            logger.info(f"PyQt5 투명 창 투명도 {saved_opacity:.2f} 적용됨")

            self.status_window = None
            self.status_label = self.qt_label
            
            logger.info("Qt WA_TransparentForMouseEvents window created successfully")
            
        except ImportError:
            logger.error("PyQt5 not available for Qt transparent method")
            self.create_win32_click_through_window(x, y, width, height)
        except Exception as e:
            logger.error(f"Qt transparent window creation failed: {e}")
            self.create_win32_click_through_window(x, y, width, height)
    
    def create_simple_window(self, x, y, width, height):
        try:
            self.status_window = tk.Toplevel()
            self.status_window.overrideredirect(True)
            self.status_window.attributes('-topmost', True)
            self.status_window.geometry(f'{width}x{height}+{x}+{y}')
            self.status_window.configure(bg='#2b2b2b')
            
            saved_opacity = load_data('status_opacity', 0.9)
            self.status_window.attributes('-alpha', saved_opacity)
            
            main_frame = tk.Frame(self.status_window, bg='#2b2b2b')
            main_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
            
            self.status_label = tk.Label(
                main_frame,
                text="🟢 온라인",
                font=('맑은 고딕', 10, 'bold'),
                fg='#4CAF50',
                bg='#2b2b2b'
            )
            self.status_label.pack(anchor='w')
            
            self.network_label = tk.Label(
                main_frame,
                text="네트워크: 확인 중...",
                font=('맑은 고딕', 9),
                fg='#ffffff',
                bg='#2b2b2b'
            )
            self.network_label.pack(anchor='w')
            
            self.ip_label = tk.Label(
                main_frame,
                text="IP: 확인 중...",
                font=('맑은 고딕', 9),
                fg='#ffffff',
                bg='#2b2b2b'
            )
            self.ip_label.pack(anchor='w')
            
            logger.info("Simple fixed window created")
            
        except Exception as e:
            logger.error(f"Simple window creation failed: {e}")
    
    def create_auto_hide_window(self, x, y, width, height):
        try:
            self.status_window = tk.Toplevel()
            self.status_window.overrideredirect(True)
            self.status_window.attributes('-topmost', True)
            saved_opacity = load_data('status_opacity', 0.9)
            self.status_window.attributes('-alpha', saved_opacity)
            self.status_window.configure(bg='#2c3e50')
            
            self.status_window.geometry(f"{width}x{height}+{x}+{y}")
            
            self.status_label = tk.Label(
                self.status_window,
                text="인터넷 상태 확인 중...",
                bg='#2c3e50',
                fg='white',
                font=('Arial', 9),
                justify='left',
                anchor='w'
            )
            self.status_label.pack(fill=tk.BOTH, expand=True, padx=8, pady=8)
            
            self.status_label.bind('<Button-1>', self.on_status_click)
            self.status_window.bind('<Button-1>', self.on_status_click)
            
            self.mouse_check_active = True
            self.check_mouse_position()
            
            logger.info("Auto-hide window created")
            
        except Exception as e:
            logger.error(f"Auto-hide window creation failed: {e}")
    
    def check_mouse_position(self):
        if not self.mouse_check_active or not self.status_window:
            return
            
        try:
            import mouse
            mouse_x, mouse_y = mouse.get_position()
            
            win_x = self.status_window.winfo_x()
            win_y = self.status_window.winfo_y()
            win_width = self.status_window.winfo_width()
            win_height = self.status_window.winfo_height()
            
            margin = 50
            near_window = (
                mouse_x >= win_x - margin and mouse_x <= win_x + win_width + margin and
                mouse_y >= win_y - margin and mouse_y <= win_y + win_height + margin
            )
            
            if near_window:
                if self.status_window.winfo_viewable():
                    self.status_window.withdraw()
                    logger.debug("Window hidden due to nearby mouse")
            else:
                if not self.status_window.winfo_viewable():
                    self.status_window.deiconify()
                    logger.debug("Window shown, mouse moved away")
            
            self.status_window.after(100, self.check_mouse_position)
            
        except Exception as e:
            logger.error(f"Mouse position check error: {e}")
            if self.status_window:
                self.status_window.after(1000, self.check_mouse_position)
    
    def create_minimal_dot_window(self, x, y):
        try:
            self.status_window = tk.Toplevel()
            self.status_window.overrideredirect(True)
            self.status_window.attributes('-topmost', True)
            saved_opacity = load_data('status_opacity', 0.7)
            self.status_window.attributes('-alpha', saved_opacity)
            self.status_window.configure(bg='#2c3e50')
            
            self.dot_size = 20
            self.expanded_size = (200, 80)
            self.is_expanded = False
            
            self.status_window.geometry(f"{self.dot_size}x{self.dot_size}+{x}+{y}")
            
            self.dot_canvas = tk.Canvas(
                self.status_window,
                width=self.dot_size,
                height=self.dot_size,
                bg='#2c3e50',
                highlightthickness=0
            )
            self.dot_canvas.pack()
            
            self.dot_canvas.create_oval(2, 2, 18, 18, fill='#27ae60', outline='white', width=1)
            
            self.status_window.bind('<Enter>', self.expand_dot)
            self.status_window.bind('<Leave>', self.shrink_dot)
            self.dot_canvas.bind('<Enter>', self.expand_dot)
            self.dot_canvas.bind('<Leave>', self.shrink_dot)
            
            logger.info("Minimal dot window created")
            
        except Exception as e:
            logger.error(f"Minimal dot window creation failed: {e}")
    
    def expand_dot(self, event):
        if self.is_expanded:
            return
            
        try:
            self.is_expanded = True
            
            width, height = self.expanded_size
            x = self.status_window.winfo_x()
            y = self.status_window.winfo_y() - (height - self.dot_size) // 2
            
            self.status_window.geometry(f"{width}x{height}+{x}+{y}")
            
            self.dot_canvas.destroy()
            
            self.status_label = tk.Label(
                self.status_window,
                text="인터넷 상태 확인 중...",
                bg='#2c3e50',
                fg='white',
                font=('Arial', 9),
                justify='left',
                anchor='w'
            )
            self.status_label.pack(fill=tk.BOTH, expand=True, padx=8, pady=8)
            
            self.update_minimal_status()
            
        except Exception as e:
            logger.error(f"Dot expansion error: {e}")
    
    def shrink_dot(self, event):
        if not self.is_expanded:
            return
            
        try:
            self.is_expanded = False
            
            x = self.status_window.winfo_x()
            y = self.status_window.winfo_y() + (self.expanded_size[1] - self.dot_size) // 2
            
            self.status_window.geometry(f"{self.dot_size}x{self.dot_size}+{x}+{y}")
            
            if hasattr(self, 'status_label') and self.status_label:
                self.status_label.destroy()
            
            self.dot_canvas = tk.Canvas(
                self.status_window,
                width=self.dot_size,
                height=self.dot_size,
                bg='#2c3e50',
                highlightthickness=0
            )
            self.dot_canvas.pack()
            
            color = '#27ae60'
            try:
                if self.check_internet_connection():
                    color = '#27ae60'
                else:
                    color = '#e74c3c'
            except:
                color = '#f39c12'
            
            self.dot_canvas.create_oval(2, 2, 18, 18, fill=color, outline='white', width=1)
            self.dot_canvas.bind('<Enter>', self.expand_dot)
            
        except Exception as e:
            logger.error(f"Dot shrinking error: {e}")
    
    def update_minimal_status(self):
        if self.is_expanded and hasattr(self, 'status_label') and self.status_label:
            try:
                is_connected = self.check_internet_connection()
                
                if is_connected:
                    network_info = self.get_network_info()
                    status_text = f"🟢 온라인 - {network_info}"
                else:
                    status_text = "🔴 오프라인"
                
                self.status_label.config(text=status_text)
                
            except Exception as e:
                logger.error(f"Minimal status update error: {e}")
    
    def create_sliding_panel_window(self):
        try:
            screen_width = tk._default_root.winfo_screenwidth()
            screen_height = tk._default_root.winfo_screenheight()
            
            panel_width = 250
            panel_height = 100
            
            self.status_window = tk.Toplevel()
            self.status_window.overrideredirect(True)
            self.status_window.attributes('-topmost', True)
            saved_opacity = load_data('status_opacity', 0.95)
            self.status_window.attributes('-alpha', saved_opacity)
            self.status_window.configure(bg='#34495e')
            
            self.hidden_x = screen_width
            self.visible_x = screen_width - panel_width
            self.panel_y = screen_height - panel_height - 100
            
            self.status_window.geometry(f"{panel_width}x{panel_height}+{self.hidden_x}+{self.panel_y}")
            
            self.status_label = tk.Label(
                self.status_window,
                text="← 마우스를 가져다 대세요",
                bg='#34495e',
                fg='white',
                font=('Arial', 10),
                justify='center'
            )
            self.status_label.pack(expand=True, fill=tk.BOTH, padx=10, pady=10)
            
            self.create_trigger_area()
            
            self.is_panel_visible = False
            
            logger.info("Sliding panel window created")
            
        except Exception as e:
            logger.error(f"Sliding panel creation failed: {e}")
    
    def create_trigger_area(self):
        try:
            screen_width = tk._default_root.winfo_screenwidth()
            screen_height = tk._default_root.winfo_screenheight()
            
            self.trigger_window = tk.Toplevel()
            self.trigger_window.overrideredirect(True)
            self.trigger_window.attributes('-topmost', True)
            self.trigger_window.attributes('-alpha', 0.01)
            self.trigger_window.configure(bg='black')
            
            trigger_width = 5
            trigger_height = 200
            trigger_x = screen_width - trigger_width
            trigger_y = screen_height - trigger_height - 100
            
            self.trigger_window.geometry(f"{trigger_width}x{trigger_height}+{trigger_x}+{trigger_y}")
            
            self.trigger_window.bind('<Enter>', self.show_panel)
            self.status_window.bind('<Leave>', self.hide_panel)
            
        except Exception as e:
            logger.error(f"Trigger area creation failed: {e}")
    
    def show_panel(self, event):
        if self.is_panel_visible:
            return
            
        try:
            self.is_panel_visible = True
            self.slide_panel_in()
            
            is_connected = self.check_internet_connection()
            if is_connected:
                network_info = self.get_network_info()
                status_text = f"🟢 온라인\n{network_info}"
            else:
                status_text = "🔴 오프라인"
            
            self.status_label.config(text=status_text)
            
        except Exception as e:
            logger.error(f"Panel show error: {e}")
    
    def hide_panel(self, event):
        if not self.is_panel_visible:
            return
            
        try:
            mouse_x = event.x_root
            mouse_y = event.y_root
            
            panel_x = self.status_window.winfo_x()
            panel_y = self.status_window.winfo_y()
            panel_width = self.status_window.winfo_width()
            panel_height = self.status_window.winfo_height()
            
            if (mouse_x < panel_x or mouse_x > panel_x + panel_width or
                mouse_y < panel_y or mouse_y > panel_y + panel_height):
                
                self.is_panel_visible = False
                self.slide_panel_out()
                
        except Exception as e:
            logger.error(f"Panel hide error: {e}")
    
    def slide_panel_in(self):
        try:
            current_x = self.status_window.winfo_x()
            if current_x > self.visible_x:
                new_x = max(self.visible_x, current_x - 20)
                self.status_window.geometry(f"+{new_x}+{self.panel_y}")
                self.status_window.after(20, self.slide_panel_in)
                
        except Exception as e:
            logger.error(f"Slide in error: {e}")
    
    def slide_panel_out(self):
        try:
            current_x = self.status_window.winfo_x()
            if current_x < self.hidden_x:
                new_x = min(self.hidden_x, current_x + 20)
                self.status_window.geometry(f"+{new_x}+{self.panel_y}")
                if new_x < self.hidden_x:
                    self.status_window.after(20, self.slide_panel_out)
                    
        except Exception as e:
            logger.error(f"Slide out error: {e}")
    
    def create_corner_popup_window(self):
        try:
            screen_width = tk._default_root.winfo_screenwidth()
            screen_height = tk._default_root.winfo_screenheight()
            
            self.corner_button = tk.Toplevel()
            self.corner_button.overrideredirect(True)
            self.corner_button.attributes('-topmost', True)
            self.corner_button.attributes('-alpha', 0.8)
            self.corner_button.configure(bg='#3498db')
            
            button_size = 30
            button_x = screen_width - button_size - 10
            button_y = screen_height - button_size - 100
            
            self.corner_button.geometry(f"{button_size}x{button_size}+{button_x}+{button_y}")
            
            button_label = tk.Label(
                self.corner_button,
                text="📶",
                bg='#3498db',
                fg='white',
                font=('Arial', 12),
                cursor='hand2'
            )
            button_label.pack(expand=True)
            
            button_label.bind('<Button-1>', self.show_corner_popup)
            self.corner_button.bind('<Button-1>', self.show_corner_popup)
            
            self.popup_window = None
            self.status_window = self.corner_button
            
            logger.info("Corner popup window created")
            
        except Exception as e:
            logger.error(f"Corner popup creation failed: {e}")
    
    def show_corner_popup(self, event):
        try:
            if self.popup_window:
                self.popup_window.destroy()
            
            button_x = self.corner_button.winfo_x()
            button_y = self.corner_button.winfo_y()
            
            popup_width = 200
            popup_height = 80
            popup_x = button_x - popup_width + 30
            popup_y = button_y - popup_height - 10
            
            self.popup_window = tk.Toplevel()
            self.popup_window.overrideredirect(True)
            self.popup_window.attributes('-topmost', True)
            self.popup_window.attributes('-alpha', 0.95)
            self.popup_window.configure(bg='#2c3e50')
            
            self.popup_window.geometry(f"{popup_width}x{popup_height}+{popup_x}+{popup_y}")
            
            is_connected = self.check_internet_connection()
            if is_connected:
                network_info = self.get_network_info()
                status_text = f"🟢 온라인\n{network_info}"
            else:
                status_text = "🔴 오프라인"
            
            popup_label = tk.Label(
                self.popup_window,
                text=status_text,
                bg='#2c3e50',
                fg='white',
                font=('Arial', 9),
                justify='left',
                anchor='w'
            )
            popup_label.pack(fill=tk.BOTH, expand=True, padx=8, pady=8)
            
            self.popup_window.after(3000, self.hide_corner_popup)
            
        except Exception as e:
            logger.error(f"Corner popup show error: {e}")
    
    def hide_corner_popup(self):
        try:
            if self.popup_window:
                self.popup_window.destroy()
                self.popup_window = None
        except Exception as e:
            logger.error(f"Corner popup hide error: {e}")
    
    def apply_windows_click_through(self):
        if platform.system() != "Windows":
            return
            
        try:
            import ctypes
            hwnd = self.status_window.winfo_id()
            
            GWL_EXSTYLE = -20
            WS_EX_LAYERED = 0x00080000
            WS_EX_TRANSPARENT = 0x00000020
            
            style = ctypes.windll.user32.GetWindowLongW(hwnd, GWL_EXSTYLE)
            new_style = style | WS_EX_LAYERED | WS_EX_TRANSPARENT
            
            result = ctypes.windll.user32.SetWindowLongW(hwnd, GWL_EXSTYLE, new_style)
            
            if result:
                self.click_through_enabled = True
                logger.info("Windows click-through successfully applied")
            else:
                logger.error("Failed to apply Windows click-through")
                
        except Exception as e:
            logger.error(f"Windows click-through error: {e}")
    
    def set_click_through(self, enable=True):
        if not self.status_window:
            return
            
        if platform.system() == "Windows":
            try:
                import ctypes
                from ctypes import wintypes
                
                GWL_EXSTYLE = -20
                WS_EX_LAYERED = 0x00080000
                WS_EX_TRANSPARENT = 0x00000020
                WS_EX_NOACTIVATE = 0x08000000
                WS_EX_TOOLWINDOW = 0x00000080
                
                hwnd = self.status_window.winfo_id()
                
                if enable:
                    try:
                        style = ctypes.windll.user32.GetWindowLongW(hwnd, GWL_EXSTYLE)
                        
                        new_style = style | WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW
                        
                        result = ctypes.windll.user32.SetWindowLongW(hwnd, GWL_EXSTYLE, new_style)
                        
                        HWND_BOTTOM = 1
                        SWP_NOSIZE = 0x0001
                        SWP_NOMOVE = 0x0002
                        SWP_NOACTIVATE = 0x0010
                        
                        ctypes.windll.user32.SetWindowPos(
                            hwnd, HWND_BOTTOM, 0, 0, 0, 0,
                            SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE
                        )
                        
                        LWA_ALPHA = 0x00000002
                        ctypes.windll.user32.SetLayeredWindowAttributes(
                            hwnd, 0, int(255 * 0.8), LWA_ALPHA
                        )
                        
                        logger.info("Enhanced click-through enabled - 상태 UI 뒤의 버튼 클릭 가능")
                        
                    except Exception as e:
                        logger.error(f"Enhanced click-through failed, trying alternative: {e}")
                        self.hide_and_show_alternative()
                        return
                else:
                    style = ctypes.windll.user32.GetWindowLongW(hwnd, GWL_EXSTYLE)
                    new_style = (style | WS_EX_LAYERED) & ~WS_EX_TRANSPARENT & ~WS_EX_NOACTIVATE
                    
                    ctypes.windll.user32.SetWindowLongW(hwnd, GWL_EXSTYLE, new_style)
                    
                    HWND_TOPMOST = -1
                    SWP_NOSIZE = 0x0001
                    SWP_NOMOVE = 0x0002
                    
                    ctypes.windll.user32.SetWindowPos(
                        hwnd, HWND_TOPMOST, 0, 0, 0, 0,
                        SWP_NOSIZE | SWP_NOMOVE
                    )
                    
                    logger.info("Click-through disabled - 상태 UI 편집 모드")
                
            except Exception as e:
                logger.error(f"Failed to set click-through: {e}")
                import traceback
                traceback.print_exc()
                self.recreate_window_as_transparent()
        else:
            logger.info(f"Click-through not supported on {platform.system()}")
    
    def hide_and_show_alternative(self):
        try:
            if hasattr(self, 'qt_widget') and self.qt_widget:
                pos = self.qt_widget.pos()
                x = pos.x()
                y = pos.y()
            elif self.status_window:
                x = self.status_window.winfo_x()
                y = self.status_window.winfo_y()
            else:
                return
                
                self.status_window.withdraw()
                
                self.status_window.after(100, lambda: self.show_transparent_window(x, y))
                
        except Exception as e:
            logger.error(f"Hide and show alternative failed: {e}")
    
    def show_transparent_window(self, x, y):
        try:
            self.status_window.deiconify()
            self.status_window.geometry(f"+{x}+{y}")
            
            self.status_window.after(50, lambda: self.set_click_through(True))
            
        except Exception as e:
            logger.error(f"Show transparent window failed: {e}")
    
    def recreate_window_as_transparent(self):
        try:
            logger.info("Recreating window as fully transparent overlay")
            
            if hasattr(self, 'qt_widget') and self.qt_widget:
                pos = self.qt_widget.pos()
                x = pos.x()
                y = pos.y()
                text = self.qt_label.text() if hasattr(self, 'qt_label') and self.qt_label else "상태 확인 중..."
            elif self.status_window:
                x = self.status_window.winfo_x()
                y = self.status_window.winfo_y()
                text = self.status_label.cget('text') if self.status_label else "상태 확인 중..."
            else:
                x, y = 100, 100
                text = "상태 확인 중..."
                
                self.status_window.destroy()
                
                self.status_window = tk.Toplevel()
                self.status_window.overrideredirect(True)
                self.status_window.attributes('-topmost', False)
                saved_opacity = load_data('status_opacity', 0.7)
                self.status_window.attributes('-alpha', saved_opacity)
                
                self.status_window.geometry(f"200x80+{x}+{y}")
                self.status_window.configure(bg='#2c3e50')
                
                self.status_label = tk.Label(
                    self.status_window,
                    text=text,
                    bg='#2c3e50',
                    fg='white',
                    font=('Arial', 9),
                    justify='left',
                    anchor='w'
                )
                self.status_label.pack(fill=tk.BOTH, expand=True, padx=8, pady=8)
                
                self.status_window.lower()
                
                logger.info("Window recreated as background overlay")
                
        except Exception as e:
            logger.error(f"Recreate window failed: {e}")
    
    def apply_initial_click_through(self):
        try:
            if self.status_window and self.status_window.winfo_exists():
                self.set_click_through(not self.edit_mode)
                logger.info("Initial click-through setting applied")
            else:
                logger.warning("Status window not available for click-through setting")
        except Exception as e:
            logger.error(f"Failed to apply initial click-through: {e}")
    
    def apply_enhanced_click_through(self):
        if platform.system() != "Windows" or self.edit_mode:
            return
            
        try:
            if self.status_window and self.status_window.winfo_exists():
                import ctypes
                
                hwnd = self.status_window.winfo_id()
                
                HWND_BOTTOM = 1
                SWP_NOSIZE = 0x0001
                SWP_NOMOVE = 0x0002
                SWP_NOACTIVATE = 0x0010
                SWP_NOREDRAW = 0x0008
                
                ctypes.windll.user32.SetWindowPos(
                    hwnd, HWND_BOTTOM, 0, 0, 0, 0,
                    SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOREDRAW
                )
                
                WS_DISABLED = 0x08000000
                GWL_STYLE = -16
                
                style = ctypes.windll.user32.GetWindowLongW(hwnd, GWL_STYLE)
                ctypes.windll.user32.SetWindowLongW(hwnd, GWL_STYLE, style | WS_DISABLED)
                
                self.status_window.configure(cursor='')
                if self.status_label:
                    self.status_label.configure(cursor='')
                
                logger.info("Enhanced click-through applied for Windows 11")
                
        except Exception as e:
            logger.error(f"Enhanced click-through failed: {e}")
            try:
                self.status_window.withdraw()
                self.status_window.after(100, self.status_window.deiconify)
                logger.info("Used withdraw/deiconify as fallback")
            except:
                pass
    
    def on_status_click(self, event):
        try:
            if self.edit_mode:
                return
            
            import time
            current_time = time.time()
            if hasattr(self, '_last_click_time') and current_time - self._last_click_time < 0.3:
                logger.info("Ignoring duplicate click within 300ms")
                return
            self._last_click_time = current_time
            
            if hasattr(self, 'radial_menu') and self.radial_menu and hasattr(self.radial_menu, 'settings_window'):
                settings_window = self.radial_menu.settings_window
                if hasattr(settings_window, 'settings_root') and settings_window.settings_root and settings_window.settings_root.winfo_exists():
                    logger.info("Settings window is open - ignoring status click")
                    return
            
            if hasattr(self, '_settings_button_clicked_time'):
                import time
                if time.time() - self._settings_button_clicked_time < 1.0:
                    logger.info("Settings button recently clicked - ignoring status click")
                    return
            
            if self.edit_mode:
                logger.info("Edit mode is active - ignoring status click")
                return
            
            if hasattr(self, 'radial_menu') and self.radial_menu and hasattr(self.radial_menu, 'overlay') and self.radial_menu.overlay:
                logger.info("Radial menu already open - ignoring status click")
                return
            
            if hasattr(self, '_radial_menu_active') and self._radial_menu_active:
                logger.info("Radial menu active flag is set - ignoring status click")
                return
            
            hotkey_type = load_data('hotkey_type', 'mouse_wheel')
            if hotkey_type == 'click_status':
                logger.info("Status window clicked - opening radial menu")

                try:
                    try:
                        from pynput import mouse as pynput_mouse
                        controller = pynput_mouse.Controller()
                        x, y = controller.position
                    except ImportError:
                        import mouse
                        x, y = mouse.get_position()

                    if hasattr(self, 'radial_menu') and self.radial_menu:
                        self.radial_menu.create_menu(x, y)
                    else:
                        logger.warning("Radial menu not available")

                except Exception as e:
                    logger.error(f"Error opening radial menu from status click: {e}")
                    
        except Exception as e:
            logger.error(f"Status click event error: {e}")

    def enable_drag_events(self):
        if self.status_label:
            self.status_label.bind('<Button-1>', self.start_drag)
            self.status_label.bind('<B1-Motion>', self.do_drag)
            self.status_label.bind('<ButtonRelease-1>', self.stop_drag)
            self.status_label.bind('<Enter>', self.on_enter)
            self.status_label.bind('<Leave>', self.on_leave)
        
        if self.status_window:
            self.status_window.bind('<Enter>', self.on_enter)
            self.status_window.bind('<Leave>', self.on_leave)
    
    def disable_drag_events(self):
        if self.status_label:
            self.status_label.unbind('<Button-1>')
            self.status_label.unbind('<B1-Motion>')
            self.status_label.unbind('<ButtonRelease-1>')
            self.status_label.unbind('<Enter>')
            self.status_label.unbind('<Leave>')
        
        if self.status_window:
            self.status_window.unbind('<Enter>')
            self.status_window.unbind('<Leave>')
    
    def set_edit_mode(self, enabled):
        if self.edit_mode == enabled:
            logger.info(f"Edit mode already {'enabled' if enabled else 'disabled'}, skipping")
            return

        logger.info(f"Setting edit mode to {'enabled' if enabled else 'disabled'}")
        self.edit_mode = enabled

        self.cleanup_all_windows()

        if self.is_visible:
            try:
                self.create_status_window()
                logger.info(f"Status window recreated in {'edit' if enabled else 'normal'} mode")
            except Exception as e:
                logger.error(f"Error creating status window: {e}")
                self.edit_mode = False

        logger.info(f"Edit mode {'enabled' if enabled else 'disabled'} - window recreated")

    def cleanup_all_windows(self):
        try:
            x, y = None, None

            if hasattr(self, 'qt_widget') and self.qt_widget:
                try:
                    pos = self.qt_widget.pos()
                    x = pos.x()
                    y = pos.y()
                    logger.info(f"PyQt5 창 위치 저장: ({x}, {y})")
                    self.qt_widget.close()
                    self.qt_widget.deleteLater()
                    self.qt_widget = None
                except Exception as e:
                    logger.error(f"Error closing PyQt5 window: {e}")

            if hasattr(self, 'status_window') and self.status_window:
                try:
                    if self.status_window.winfo_exists():
                        if x is None:
                            x = self.status_window.winfo_x()
                            y = self.status_window.winfo_y()
                            logger.info(f"tkinter 창 위치 저장: ({x}, {y})")
                        self.status_window.destroy()
                except Exception as e:
                    logger.error(f"Error closing tkinter window: {e}")
                finally:
                    self.status_window = None

            if x is not None and y is not None:
                save_data('status_position', (x, y))
                logger.info(f"Window position saved: ({x}, {y})")

            self.status_label = None
            if hasattr(self, 'qt_label'):
                self.qt_label = None

        except Exception as e:
            logger.error(f"Error in cleanup_all_windows: {e}")
            self.status_window = None
            self.status_label = None
            if hasattr(self, 'qt_widget'):
                self.qt_widget = None
            if hasattr(self, 'qt_label'):
                self.qt_label = None
    
    def on_enter(self, event):
        if self.edit_mode and self.status_window:
            self.status_window.configure(cursor='hand2')
        if self.edit_mode and self.status_label:
            self.status_label.configure(cursor='hand2')
    
    def on_leave(self, event):
        if self.edit_mode and not self.is_dragging:
            if self.status_window:
                self.status_window.configure(cursor='')
            if self.status_label:
                self.status_label.configure(cursor='hand2')
    
    def start_drag(self, event):
        if not self.status_window:
            logger.warning("Cannot process event: window not available")
            return
        
        if not self.edit_mode:
            self.on_status_click(event)
            return
        
        try:
            self.is_dragging = True
            
            self.offset_x = event.x
            self.offset_y = event.y
            
            self.drag_start_x = self.status_window.winfo_x()
            self.drag_start_y = self.status_window.winfo_y()
            
            logger.info(f"Drag started - offset: ({self.offset_x}, {self.offset_y}), window: ({self.drag_start_x}, {self.drag_start_y})")
            
            self.status_window.configure(cursor='fleur')
            self.status_label.configure(cursor='fleur')
            
            self.status_window.lift()
            
        except Exception as e:
            logger.error(f"Start drag error: {e}")
            self.is_dragging = False
    
    def do_drag(self, event):
        if not self.is_dragging or not self.status_window:
            return
            
        try:
            if not self.status_window.winfo_exists():
                logger.error("Status window does not exist during drag")
                self.is_dragging = False
                return
            
            new_x = event.x_root - self.offset_x
            new_y = event.y_root - self.offset_y
            
            try:
                import tkinter as tk
                root = self.status_window

                virtual_screen_width = root.winfo_vrootwidth()
                virtual_screen_height = root.winfo_vrootheight()

                virtual_x = root.winfo_vrootx()
                virtual_y = root.winfo_vrooty()

                logger.debug(f"Virtual screen: {virtual_screen_width}x{virtual_screen_height} at ({virtual_x}, {virtual_y})")

            except:
                virtual_screen_width = self.status_window.winfo_screenwidth()
                virtual_screen_height = self.status_window.winfo_screenheight()
                virtual_x = 0
                virtual_y = 0

            window_width = self.status_window.winfo_width()
            window_height = self.status_window.winfo_height()

            min_x = virtual_x - window_width + 50
            max_x = virtual_x + virtual_screen_width - 10

            new_x = max(min_x, min(new_x, max_x))
            new_y = max(0, min(new_y, virtual_screen_height - window_height))
            
            self.status_window.geometry(f"+{new_x}+{new_y}")
            
            logger.debug(f"Dragging to ({new_x}, {new_y}) - window size: {window_width}x{window_height}")
            
        except Exception as e:
            logger.error(f"Drag error: {e}")
            import traceback
            traceback.print_exc()
            self.is_dragging = False
    
    def stop_drag(self, event):
        if not self.is_dragging:
            return
            
        self.is_dragging = False

        try:
            if self.status_window and self.edit_mode:
                x = self.status_window.winfo_x()
                y = self.status_window.winfo_y()
                save_data('status_position', (x, y))
                logger.info(f"Edit mode drag completed - position saved: ({x}, {y})")

                self.user_has_moved = True
                save_data('user_has_moved', True)
            else:
                logger.warning("드래그 종료: 편집 모드가 아니거나 창이 없음")
                return
            
            if self.settings_window and hasattr(self.settings_window, 'update_position_label'):
                self.settings_window.update_position_label()
                
        except Exception as e:
            logger.error(f"Stop drag error: {e}")
        
        try:
            if self.status_window:
                self.status_window.configure(cursor='hand2')
            if self.status_label:
                self.status_label.configure(cursor='hand2')
        except Exception as e:
            logger.error(f"Cursor restore error: {e}")
        
    def update_status(self):
        if not self.is_visible or not self.status_window:
            return
            
        try:
            if self.edit_mode:
                status_text = "편집 모드\n드래그하여 이동"
                if self.status_label and self.status_label.winfo_exists():
                    self.status_label.config(text=status_text)
            else:
                def check_network_status():
                    try:
                        is_connected = self.check_internet_connection()
                        
                        if is_connected:
                            network_info = self.get_network_info()
                            status_text = f"🟢 온라인 - {network_info}"
                        else:
                            status_text = "🔴 오프라인"
                        
                        def update_ui():
                            try:
                                if (self.is_visible and self.status_window and self.status_label and 
                                    self.status_label.winfo_exists()):
                                    self.status_label.config(text=status_text)
                                    self.current_status_text = status_text
                            except Exception as e:
                                logger.error(f"UI update error: {e}")
                        
                        try:
                            if self.status_window and hasattr(self.status_window, 'after'):
                                self.status_window.after(0, update_ui)
                        except RuntimeError as e:
                            logger.error(f"Main thread not in main loop: {e}")
                        except Exception as e:
                            logger.error(f"UI scheduling error: {e}")
                        
                    except Exception as e:
                        logger.error(f"Background network check error: {e}")
                        def update_error_ui():
                            try:
                                if (self.is_visible and self.status_window and self.status_label and 
                                    self.status_label.winfo_exists()):
                                    self.status_label.config(text="❓ 상태 확인 실패")
                            except Exception as e:
                                logger.error(f"Error UI update error: {e}")
                        
                        try:
                            if self.status_window and hasattr(self.status_window, 'after'):
                                self.status_window.after(0, update_error_ui)
                        except RuntimeError as e:
                            logger.error(f"Main thread not in main loop during error handling: {e}")
                        except Exception as e:
                            logger.error(f"Error UI scheduling error: {e}")
                
                threading.Thread(target=check_network_status, daemon=True).start()
                
        except Exception as e:
            logger.error(f"Status update error: {e}")
            if self.edit_mode:
                self.status_label.config(text="편집 모드\n❗ 오류 발생")
            else:
                self.status_label.config(text="❓ 상태 확인 실패")
        
        if self.is_visible:
            update_interval = 10000 if self.edit_mode else 5000
            self.status_window.after(update_interval, self.update_status)
    
    def check_internet_connection(self):
        dns_servers = [
            ("8.8.8.8", 53),
            ("1.1.1.1", 53),
            ("208.67.222.222", 53),
            ("8.8.4.4", 53),
        ]

        for server, port in dns_servers:
            try:
                socket.create_connection((server, port), timeout=2)
                return True
            except (OSError, socket.timeout):
                continue
            except Exception as e:
                logger.debug(f"Connection error to {server}:{port} - {e}")
                continue

        http_servers = [
            ("google.com", 80),
            ("cloudflare.com", 80),
            ("microsoft.com", 80)
        ]

        for server, port in http_servers:
            try:
                socket.create_connection((server, port), timeout=3)
                return True
            except (OSError, socket.timeout):
                continue
            except Exception as e:
                logger.debug(f"HTTP connection error to {server}:{port} - {e}")
                continue

        return False
    
    def get_network_info(self):
        try:
            ip = self.get_local_ip()
            
            ssid = self.get_wifi_ssid()
            
            if ssid:
                return f"WiFi ({ssid})"
            else:
                return f"유선 연결"
                
        except Exception as e:
            logger.error(f"Network info error: {e}")
            return "네트워크 정보 없음"
    
    def get_local_ip(self):
        try:
            with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
                s.connect(("8.8.8.8", 80))
                return s.getsockname()[0]
        except:
            return "IP 없음"
    
    def get_wifi_ssid(self):
        try:
            if platform.system() == "Windows":
                result = subprocess.run(
                    ["netsh", "wlan", "show", "interfaces"],
                    capture_output=True,
                    text=True,
                    encoding='cp949',
                    creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0
                )
                
                if result.returncode == 0:
                    for line in result.stdout.split('\n'):
                        if 'SSID' in line and 'BSSID' not in line:
                            ssid = line.split(':', 1)[1].strip()
                            if ssid:
                                return ssid
                                
            elif platform.system() == "Darwin":
                result = subprocess.run(
                    ["/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport", "-I"],
                    capture_output=True,
                    text=True
                )
                
                if result.returncode == 0:
                    ssid_match = re.search(r'SSID: (.+)', result.stdout)
                    if ssid_match:
                        return ssid_match.group(1).strip()
                        
            elif platform.system() == "Linux":
                result = subprocess.run(
                    ["iwgetid", "-r"],
                    capture_output=True,
                    text=True
                )
                
                if result.returncode == 0:
                    return result.stdout.strip()
                    
        except Exception as e:
            logger.error(f"WiFi SSID error: {e}")
            
        return None
    
    def destroy(self):
        logger.info("=== Starting StatusWindow.destroy() ===")
        logger.info("Destroying status window completely...")
        
        if hasattr(self, 'mouse_check_active'):
            self.mouse_check_active = False
        
        if hasattr(self, 'trigger_window') and self.trigger_window:
            try:
                self.trigger_window.destroy()
            except:
                pass
            self.trigger_window = None
        
        if hasattr(self, 'qt_widget') and self.qt_widget:
            try:
                if hasattr(self, 'qt_timer') and self.qt_timer:
                    self.qt_timer.stop()
                    self.qt_timer = None
                
                self.qt_widget.hide()
                self.qt_widget.close()
                
                self.qt_widget.deleteLater()
                
                if hasattr(self, 'pyqt_mouse_listener'):
                    try:
                        self.pyqt_mouse_listener.stop()
                        logger.info("PyQt mouse listener stopped during destroy")
                    except:
                        pass
                    self.pyqt_mouse_listener = None
                
                if hasattr(self, 'qt_app') and self.qt_app:
                    try:
                        self.qt_app.processEvents()
                        self.qt_app.quit()
                    except:
                        pass
                    self.qt_app = None
                    
                logger.info("PyQt widgets cleaned up successfully")
            except Exception as e:
                logger.error(f"Error cleaning up PyQt widgets: {e}")
            finally:
                self.qt_widget = None
                self.qt_app = None
        
        if hasattr(self, 'sg_window') and self.sg_window:
            try:
                self.sg_window.close()
                self.sg_running = False
            except:
                pass
            self.sg_window = None
        
        if self.status_window:
            try:
                self.status_window.destroy()
            except:
                pass
            self.status_window = None
        
        if hasattr(self, 'settings_window') and self.settings_window:
            try:
                self.settings_window.destroy()
            except:
                pass
            self.settings_window = None
        
        self.status_label = None
        self.is_visible = False
        
        logger.info("Status window destroyed completely")
        logger.info("=== StatusWindow.destroy() completed ===\n")
    
    def hide_status_window(self):
        if hasattr(self, 'mouse_check_active'):
            self.mouse_check_active = False
            
        if hasattr(self, 'trigger_window') and self.trigger_window:
            self.trigger_window.destroy()
            self.trigger_window = None
            
        if hasattr(self, 'corner_button') and self.corner_button:
            self.corner_button.destroy()
            self.corner_button = None
            
        if hasattr(self, 'popup_window') and self.popup_window:
            self.popup_window.destroy()
            self.popup_window = None
            
        if self.status_window:
            self.status_window.destroy()
            self.status_window = None
            self.status_label = None
            
        if hasattr(self, 'qt_widget') and self.qt_widget:
            self.qt_widget.hide()
            if hasattr(self, 'qt_timer'):
                self.qt_timer.stop()
            if hasattr(self, 'pyqt_mouse_listener'):
                try:
                    self.pyqt_mouse_listener.stop()
                    logger.info("PyQt mouse listener stopped")
                except:
                    pass
                
        if hasattr(self, 'sg_window') and self.sg_window:
            self.sg_running = False
            try:
                self.sg_window.close()
            except:
                pass
            
        self.is_visible = False
        logger.info("Status window hidden")
    
    def show_status_window(self):
        if not self.is_visible:
            self.create_status_window()
        elif self.status_window and self.status_window.winfo_exists():
            self.status_window.deiconify()
            self.status_window.lift()
        elif hasattr(self, 'qt_widget') and self.qt_widget:
            self.qt_widget.show()
            if hasattr(self, 'qt_timer'):
                self.qt_timer.start(5000)
        elif hasattr(self, 'sg_window') and self.sg_window:
            try:
                self.sg_window.un_hide()
                self.sg_running = True
            except:
                self.create_status_window()
        logger.info("Status window shown")


class SettingsWindow:
    def __init__(self, status_window):
        self.settings_window = None
        self.status_window = status_window
        self.status_enabled = load_data('status_enabled', True)
        self.status_window.settings_window = self
        self.header_click_count = 0
        self.env_unlocked = False
        self.env_radios = []
        
    def show_settings(self):
        if self.settings_window:
            try:
                if self.settings_window.winfo_exists():
                    self.settings_window.lift()
                    self.refresh_environment_setting()
                    return
            except:
                self.settings_window = None
            
        logger.info("Opening settings window...")
        
        try:
            self.status_window.set_edit_mode(True)
        except Exception as e:
            logger.error(f"Error setting edit mode: {e}")
        
        self.settings_window = tk.Toplevel()
        self.settings_window.title("GeoMedical Helper - 설정")
        self.settings_window.geometry("950x650")
        self.settings_window.resizable(True, True)
        self.settings_window.configure(bg='#f0f0f0')
        
        self.canvas = tk.Canvas(self.settings_window)
        scrollbar = ttk.Scrollbar(self.settings_window, orient="vertical", command=self.canvas.yview)
        self.scrollable_frame = ttk.Frame(self.canvas)
        
        self.scrollable_frame.bind(
            "<Configure>",
            lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all"))
        )
        
        self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
        self.canvas.configure(yscrollcommand=scrollbar.set)
        
        self.canvas.pack(side="left", fill="both", expand=True)
        scrollbar.pack(side="right", fill="y")
        
        main_frame = tk.Frame(self.scrollable_frame, bg='#f0f0f0')
        main_frame.pack(fill=tk.BOTH, expand=True, padx=8, pady=8)

        def _on_mousewheel(event):
            self.canvas.yview_scroll(int(-1*(event.delta/120)), "units")

        def bind_mousewheel(event):
            self.canvas.bind_all("<MouseWheel>", _on_mousewheel)

        def unbind_mousewheel(event):
            self.canvas.unbind_all("<MouseWheel>")

        self.canvas.bind('<Enter>', bind_mousewheel)
        self.canvas.bind('<Leave>', unbind_mousewheel)

        # 헤더
        header_frame = tk.Frame(main_frame, bg='#2c3e50', height=50)
        header_frame.pack(fill=tk.X, pady=(0, 8))
        header_frame.pack_propagate(False)

        title_label = tk.Label(header_frame, text="⚙️ GeoMedical Helper 설정",
                              font=('Arial', 14, 'bold'), fg='white', bg='#2c3e50',
                              cursor='hand2')
        title_label.pack(pady=12)
        title_label.bind('<Button-1>', self.on_header_click)

        # 그리드 컨테이너 (2열 레이아웃)
        grid_container = tk.Frame(main_frame, bg='#f0f0f0')
        grid_container.pack(fill=tk.BOTH, expand=True)

        # 왼쪽 열
        left_column = tk.Frame(grid_container, bg='#f0f0f0')
        left_column.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 4))

        # 오른쪽 열
        right_column = tk.Frame(grid_container, bg='#f0f0f0')
        right_column.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(4, 0))

        # === 왼쪽 열: 상태 표시 ===
        status_container = self._create_section_frame(left_column, "🌐 상태 표시", '#3498db')
        status_container.master.master.pack(fill=tk.BOTH, expand=True, pady=(0, 8))

        self.status_var = tk.BooleanVar(value=self.status_enabled)
        status_check = tk.Checkbutton(
            status_container,
            text="인터넷 상태 표시 활성화",
            variable=self.status_var,
            command=self.toggle_status_display,
            font=('Arial', 9, 'bold'),
            bg='white',
            activebackground='white',
            anchor='w'
        )
        status_check.pack(fill=tk.X, pady=(0, 8))

        self.edit_mode_label = tk.Label(
            status_container,
            text="🔓 편집 모드: 드래그로 위치 조정 가능",
            font=('Arial', 8, 'bold'),
            fg='#e74c3c',
            bg='white',
            anchor='w'
        )
        self.edit_mode_label.pack(fill=tk.X, pady=(0, 8))

        # 투명도 조절
        opacity_frame = tk.Frame(status_container, bg='white')
        opacity_frame.pack(fill=tk.X, pady=(0, 8))

        tk.Label(opacity_frame, text="투명도:", font=('Arial', 9),
                bg='white', anchor='w').pack(side=tk.LEFT)

        self.network_opacity_var = tk.DoubleVar(value=load_data('network_status_opacity', 0.9))
        self.network_opacity_scale = tk.Scale(
            opacity_frame,
            from_=0.1,
            to=1.0,
            resolution=0.01,  # 0.01 단위로 세밀하게 조절 가능
            orient=tk.HORIZONTAL,
            variable=self.network_opacity_var,
            command=self.update_network_opacity,
            length=200,
            bg='white',
            highlightthickness=0,
            showvalue=False
        )
        self.network_opacity_scale.pack(side=tk.LEFT, padx=(5, 5))

        self.opacity_value_label = tk.Label(opacity_frame,
                                            text=f"{self.network_opacity_var.get():.2f}",
                                            font=('Arial', 9, 'bold'),
                                            bg='white',
                                            width=4)
        self.opacity_value_label.pack(side=tk.LEFT)

        # 위치 초기화 버튼
        reset_btn = tk.Button(
            status_container,
            text="↻ 위치 초기화",
            command=self.reset_status_position,
            bg='#ecf0f1',
            fg='#2c3e50',
            font=('Arial', 9),
            relief=tk.FLAT,
            cursor='hand2',
            padx=15,
            pady=5
        )
        reset_btn.pack(fill=tk.X, pady=(0, 8))

        self.position_label = tk.Label(
            status_container,
            text="",
            font=('Arial', 8),
            fg='#7f8c8d',
            bg='white',
            anchor='w'
        )
        self.position_label.pack(fill=tk.X)

        self.update_position_label()

        # === 왼쪽 열: 실행키 설정 ===
        hotkey_container = self._create_section_frame(left_column, "🔑 실행키 설정", '#9b59b6')
        hotkey_container.master.master.pack(fill=tk.BOTH, expand=True, pady=(0, 8))

        current_hotkey = load_data('hotkey_type', 'mouse_wheel')
        hotkey_names = {
            'mouse_wheel': '마우스휠 버튼 (권장)',
            'f1': 'F1 키',
            'f2': 'F2 키',
            'f3': 'F3 키',
            'f4': 'F4 키',
            'click_status': '상태창 클릭'
        }

        tk.Label(hotkey_container, text="실행키:", font=('Arial', 9),
                bg='white', anchor='w').pack(fill=tk.X, pady=(0, 5))

        self.hotkey_var = tk.StringVar(value=current_hotkey)
        self.hotkey_combo = ttk.Combobox(
            hotkey_container,
            textvariable=self.hotkey_var,
            values=list(hotkey_names.values()),
            state='readonly',
            font=('Arial', 9)
        )
        self.hotkey_combo.set(hotkey_names.get(current_hotkey, '마우스휠 버튼 (권장)'))
        self.hotkey_combo.pack(fill=tk.X, pady=(0, 10))
        self.hotkey_combo.bind('<<ComboboxSelected>>', self.on_hotkey_selected)

        self.current_key_label = tk.Label(
            hotkey_container,
            text=f"✓ 활성: {hotkey_names.get(current_hotkey, '마우스휠')}",
            font=('Arial', 9, 'bold'),
            fg='#27ae60',
            bg='white',
            anchor='w'
        )
        self.current_key_label.pack(fill=tk.X)

        # === 왼쪽 열: 사용 환경 ===
        env_container = self._create_section_frame(left_column, "🏢 사용 환경", '#27ae60')
        env_container.master.master.pack(fill=tk.BOTH, expand=True)

        saved_env = load_data('environment', 'office')
        env_display_map = {'office': '사무용', 'field': '현장용'}
        display_value = env_display_map.get(saved_env, '사무용')
        self.env_var = tk.StringVar(value=display_value)

        # 라디오 버튼 스타일링
        self.env_radios = []
        for text, value in [("🖥️ 사무용", "사무용"), ("🏗️ 현장용", "현장용")]:
            radio = tk.Radiobutton(
                env_container,
                text=text,
                variable=self.env_var,
                value=value,
                command=self.on_environment_changed,
                font=('Arial', 9),
                bg='white',
                activebackground='white',
                anchor='w',
                state=tk.DISABLED
            )
            radio.pack(fill=tk.X, pady=2)
            self.env_radios.append(radio)

        # === 오른쪽 열: 웹사이트 관리 ===
        website_container = self._create_section_frame(right_column, "🌍 웹사이트 관리", '#e67e22')
        website_container.master.master.pack(fill=tk.BOTH, expand=True, pady=(0, 8))

        # 웹사이트 리스트
        self.website_list_frame = tk.Frame(website_container, bg='white')
        self.website_list_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 10))
        self.update_website_list()

        # 구분선
        tk.Frame(website_container, height=1, bg='#ddd').pack(fill=tk.X, pady=(0, 10))

        # 추가 섹션
        tk.Label(website_container, text="새 웹사이트 추가 (최대 7개):",
                font=('Arial', 8), fg='#7f8c8d', bg='white',
                anchor='w').pack(fill=tk.X, pady=(0, 5))

        add_frame = tk.Frame(website_container, bg='white')
        add_frame.pack(fill=tk.X)

        tk.Label(add_frame, text="이름:", font=('Arial', 8),
                bg='white', width=5, anchor='w').grid(row=0, column=0, sticky=tk.W)
        self.website_text_var = tk.StringVar()
        self.website_text_entry = ttk.Entry(add_frame, textvariable=self.website_text_var, width=12)
        self.website_text_entry.grid(row=0, column=1, padx=(0, 5))

        tk.Label(add_frame, text="URL:", font=('Arial', 8),
                bg='white', width=4, anchor='w').grid(row=0, column=2, sticky=tk.W)
        self.website_url_var = tk.StringVar()
        self.website_url_entry = ttk.Entry(add_frame, textvariable=self.website_url_var, width=20)
        self.website_url_entry.grid(row=0, column=3, padx=(0, 5))

        self.add_website_btn = tk.Button(
            add_frame,
            text="✚ 추가",
            command=self.add_website,
            bg='#e67e22',
            fg='white',
            font=('Arial', 8, 'bold'),
            relief=tk.FLAT,
            cursor='hand2',
            padx=10
        )
        self.add_website_btn.grid(row=0, column=4)

        # === 오른쪽 열: 프로그램 관리 ===
        program_container = self._create_section_frame(right_column, "💻 프로그램 관리", '#f39c12')
        program_container.master.master.pack(fill=tk.BOTH, expand=True)

        # 프로그램 리스트
        self.program_list_frame = tk.Frame(program_container, bg='white')
        self.program_list_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 10))
        self.update_program_list()

        # 구분선
        tk.Frame(program_container, height=1, bg='#ddd').pack(fill=tk.X, pady=(0, 10))

        # 추가 섹션
        tk.Label(program_container, text="새 프로그램 추가 (최대 7개):",
                font=('Arial', 8), fg='#7f8c8d', bg='white',
                anchor='w').pack(fill=tk.X, pady=(0, 5))

        add_program_frame = tk.Frame(program_container, bg='white')
        add_program_frame.pack(fill=tk.X)

        tk.Label(add_program_frame, text="이름:", font=('Arial', 8),
                bg='white', width=5, anchor='w').grid(row=0, column=0, sticky=tk.W)
        self.program_text_var = tk.StringVar()
        self.program_text_entry = ttk.Entry(add_program_frame, textvariable=self.program_text_var, width=12)
        self.program_text_entry.grid(row=0, column=1, padx=(0, 5))

        tk.Label(add_program_frame, text="경로:", font=('Arial', 8),
                bg='white', width=4, anchor='w').grid(row=0, column=2, sticky=tk.W)
        self.program_path_var = tk.StringVar()
        self.program_path_entry = ttk.Entry(add_program_frame, textvariable=self.program_path_var, width=15)
        self.program_path_entry.grid(row=0, column=3, padx=(0, 3))

        self.browse_program_btn = tk.Button(
            add_program_frame,
            text="📁",
            command=self.browse_program_path,
            bg='#ecf0f1',
            font=('Arial', 8),
            relief=tk.FLAT,
            cursor='hand2',
            width=2
        )
        self.browse_program_btn.grid(row=0, column=4, padx=(0, 5))

        self.add_program_btn = tk.Button(
            add_program_frame,
            text="✚ 추가",
            command=self.add_program,
            bg='#f39c12',
            fg='white',
            font=('Arial', 8, 'bold'),
            relief=tk.FLAT,
            cursor='hand2',
            padx=10
        )
        self.add_program_btn.grid(row=0, column=5)

        self.settings_window.protocol("WM_DELETE_WINDOW", self.close_settings)
        
        if self.status_enabled:
            self.status_window.show_status_window()
        
        self.settings_window.after(100, self.adjust_window_height)
        
    def toggle_status_display(self):
        self.status_enabled = self.status_var.get()
        save_data('status_enabled', self.status_enabled)
        
        if self.status_enabled:
            self.status_window.show_status_window()
        else:
            self.status_window.hide_status_window()
            
        logger.info(f"Status display toggled: {self.status_enabled}")
    
    def on_environment_changed(self):
        if not self.env_unlocked:
            logger.warning("Environment change blocked - admin mode not activated")
            return

        environment_display = self.env_var.get()
        environment_map = {'사무용': 'office', '현장용': 'field'}
        environment = environment_map.get(environment_display, 'office')
        save_data('environment', environment)
        logger.info(f"Environment changed to: {environment} ({environment_display})")
    
    def refresh_environment_setting(self):
        try:
            saved_env = load_data('environment', 'office')
            env_display_map = {'office': '사무용', 'field': '현장용'}
            display_value = env_display_map.get(saved_env, '사무용')
            
            if hasattr(self, 'env_var') and self.env_var:
                self.env_var.set(display_value)
                logger.info(f"Environment setting refreshed: {saved_env} -> {display_value}")
                
            self.refresh_hotkey_setting()
                
        except Exception as e:
            logger.error(f"Error refreshing environment setting: {e}")
    
    def refresh_hotkey_setting(self):
        try:
            current_hotkey = load_data('hotkey_type', 'mouse_wheel')
            if hasattr(self, 'hotkey_var') and self.hotkey_var:
                self.hotkey_var.set(current_hotkey)
                
                hotkey_names = {
                    'mouse_wheel': '마우스휠 버튼',
                    'f1': 'F1 키',
                    'f2': 'F2 키', 
                    'f3': 'F3 키',
                    'f4': 'F4 키',
                    'click_status': '상태 창 클릭'
                }
                
                if hasattr(self, 'current_key_label'):
                    self.current_key_label.config(
                        text=f"✓ 현재 활성화된 실행키: {hotkey_names.get(current_hotkey, '마우스휠 버튼')}"
                    )
                
                logger.info(f"Hotkey setting refreshed: {current_hotkey}")
        except Exception as e:
            logger.error(f"Error refreshing hotkey setting: {e}")
    
    def on_hotkey_selected(self, event=None):
        selected_name = self.hotkey_combo.get()
        hotkey_names = {
            'mouse_wheel': '마우스휠 버튼 (권장)',
            'f1': 'F1 키',
            'f2': 'F2 키', 
            'f3': 'F3 키',
            'f4': 'F4 키',
            'click_status': '상태창 클릭'
        }
        
        hotkey_type = None
        for key, name in hotkey_names.items():
            if name == selected_name:
                hotkey_type = key
                break
        
        if hotkey_type:
            self.hotkey_var.set(hotkey_type)
            self.on_hotkey_changed()
    
    def on_hotkey_changed(self):
        hotkey_type = self.hotkey_var.get()
        save_data('hotkey_type', hotkey_type)
        
        hotkey_names = {
            'mouse_wheel': '마우스휠 버튼 (권장)',
            'f1': 'F1 키',
            'f2': 'F2 키', 
            'f3': 'F3 키',
            'f4': 'F4 키',
            'click_status': '상태창 클릭'
        }
        
        logger.info(f"Hotkey changed to: {hotkey_type} ({hotkey_names.get(hotkey_type, 'Unknown')})")
        
        if hasattr(self, 'current_key_label'):
            self.current_key_label.config(text=f"✓ 현재 활성화된 실행키: {hotkey_names.get(hotkey_type, '마우스휠 버튼')}")
        
        try:
            if hasattr(self.status_window, 'radial_menu') and self.status_window.radial_menu:
                radial_menu = self.status_window.radial_menu
                
                if hasattr(radial_menu, 'cleanup_listeners'):
                    radial_menu.cleanup_listeners()
                
                try:
                    import mouse
                    mouse.unhook_all()
                    logger.info("Additional global mouse hook cleanup")
                except:
                    pass
                    
                try:
                    import keyboard  
                    keyboard.unhook_all()
                    logger.info("Additional global keyboard hook cleanup")
                except:
                    pass
                
                setup_input_listener_for_instance(radial_menu, hotkey_type)
                
                messagebox.showinfo(
                    "실행키 변경 완료", 
                    f"실행키가 '{hotkey_names.get(hotkey_type, 'Unknown')}'으로 변경되었습니다.\n\n"
                    "변경사항이 즉시 적용되었습니다."
                )
            else:
                messagebox.showinfo(
                    "실행키 변경", 
                    f"실행키가 '{hotkey_names.get(hotkey_type, 'Unknown')}'으로 변경되었습니다.\n\n"
                    "프로그램을 재시작하면 새로운 설정이 적용됩니다."
                )
                
        except Exception as e:
            logger.error(f"Error applying hotkey change: {e}")
            messagebox.showwarning(
                "실행키 변경", 
                f"실행키가 변경되었으나 즉시 적용하는데 실패했습니다.\n"
                f"프로그램을 재시작하면 새로운 설정이 적용됩니다.\n\n"
                f"오류: {str(e)}"
            )
    
    def update_website_list(self):
        for widget in self.website_list_frame.winfo_children():
            widget.destroy()

        custom_websites = load_data('custom_websites', [])

        # 기본 웹사이트
        default_frame = tk.Frame(self.website_list_frame, bg='#ecf0f1', relief=tk.FLAT)
        default_frame.pack(fill=tk.X, pady=1)

        tk.Label(default_frame, text="🏠 지오메디컬 링크",
                font=('Arial', 8, 'bold'), bg='#ecf0f1', fg='#2c3e50',
                anchor='w').pack(side=tk.LEFT, padx=8, pady=4)

        tk.Label(default_frame, text="[기본]",
                font=('Arial', 7), bg='#ecf0f1', fg='#7f8c8d',
                anchor='e').pack(side=tk.RIGHT, padx=8)

        # 커스텀 웹사이트
        for i, website in enumerate(custom_websites):
            item_frame = tk.Frame(self.website_list_frame, bg='white', relief=tk.FLAT)
            item_frame.pack(fill=tk.X, pady=1)

            # 내용
            content_frame = tk.Frame(item_frame, bg='white')
            content_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=8, pady=4)

            tk.Label(content_frame, text=f"🌐 {website['text']}",
                    font=('Arial', 8), bg='white', fg='#2c3e50',
                    anchor='w').pack(side=tk.TOP, fill=tk.X)

            tk.Label(content_frame, text=website['url'],
                    font=('Arial', 7), bg='white', fg='#95a5a6',
                    anchor='w').pack(side=tk.TOP, fill=tk.X)

            # 삭제 버튼
            delete_btn = tk.Button(
                item_frame,
                text="🗑️",
                command=lambda idx=i: self.delete_website(idx),
                bg='#e74c3c',
                fg='white',
                font=('Arial', 8),
                relief=tk.FLAT,
                cursor='hand2',
                width=3
            )
            delete_btn.pack(side=tk.RIGHT, padx=4, pady=4)
        
        self.settings_window.update_idletasks()
        if hasattr(self, 'canvas'):
            self.canvas.configure(scrollregion=self.canvas.bbox("all"))
        
        self.adjust_window_height()
        
        if hasattr(self, 'add_website_btn') and self.add_website_btn is not None:
            try:
                if len(custom_websites) >= 7:
                    self.add_website_btn.config(state='disabled', text="등록 제한")
                else:
                    self.add_website_btn.config(state='normal', text="추가")
            except Exception as e:
                logger.error(f"Error updating add button state: {e}")
        
        logger.info("Website list updated")
    
    def add_website(self):
        text = self.website_text_var.get().strip()
        url = self.website_url_var.get().strip()
        
        if not text or not url:
            messagebox.showwarning("입력 오류", "표시 텍스트와 URL을 모두 입력해주세요.")
            return
        
        if not (url.startswith('http://') or url.startswith('https://')):
            url = 'https://' + url
        
        custom_websites = load_data('custom_websites', [])
        
        if len(custom_websites) >= 7:
            messagebox.showwarning("등록 제한", "최대 7개의 웹사이트만 등록할 수 있습니다.\n(기본 지오메디컬 링크 포함하여 총 8개)")
            return
        
        for website in custom_websites:
            if website['text'] == text or website['url'] == url:
                messagebox.showwarning("중복 오류", "이미 등록된 웹사이트입니다.")
                return
        
        import random
        colors = ['#3498db', '#2ecc71', '#e74c3c', '#f39c12', '#9b59b6', '#1abc9c', '#e67e22', '#34495e']
        color = random.choice(colors)
        
        new_website = {
            "text": text,
            "url": url,
            "color": color
        }
        custom_websites.append(new_website)
        
        save_data('custom_websites', custom_websites)
        
        self.website_text_var.set("")
        self.website_url_var.set("")
        
        self.update_website_list()
        
        messagebox.showinfo("추가 완료", f"'{text}' 웹사이트가 추가되었습니다.")
        logger.info(f"New website added: {text} -> {url}")
    
    def delete_website(self, index):
        custom_websites = load_data('custom_websites', [])
        
        if 0 <= index < len(custom_websites):
            deleted_website = custom_websites[index]
            
            if messagebox.askyesno("삭제 확인", 
                                 f"'{deleted_website['text']}' 웹사이트를 삭제하시겠습니까?"):
                custom_websites.pop(index)
                save_data('custom_websites', custom_websites)
                self.update_website_list()
                messagebox.showinfo("삭제 완료", f"'{deleted_website['text']}' 웹사이트가 삭제되었습니다.")
                logger.info(f"Website deleted: {deleted_website['text']}")

    def update_program_list(self):
        for widget in self.program_list_frame.winfo_children():
            widget.destroy()

        custom_programs = load_data('custom_programs', [])

        # 프로그램 없을 때
        if len(custom_programs) == 0:
            empty_label = tk.Label(
                self.program_list_frame,
                text="등록된 프로그램이 없습니다",
                font=('Arial', 8),
                fg='#95a5a6',
                bg='white'
            )
            empty_label.pack(pady=10)
            return

        # 프로그램 목록
        for i, program in enumerate(custom_programs):
            item_frame = tk.Frame(self.program_list_frame, bg='white', relief=tk.FLAT)
            item_frame.pack(fill=tk.X, pady=1)

            # 내용
            content_frame = tk.Frame(item_frame, bg='white')
            content_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=8, pady=4)

            tk.Label(content_frame, text=f"💾 {program['text']}",
                    font=('Arial', 8), bg='white', fg='#2c3e50',
                    anchor='w').pack(side=tk.TOP, fill=tk.X)

            # 경로 줄임 표시
            path = program['path']
            if len(path) > 40:
                path = path[:37] + "..."

            tk.Label(content_frame, text=path,
                    font=('Arial', 7), bg='white', fg='#95a5a6',
                    anchor='w').pack(side=tk.TOP, fill=tk.X)

            # 삭제 버튼
            delete_btn = tk.Button(
                item_frame,
                text="🗑️",
                command=lambda idx=i: self.delete_program(idx),
                bg='#e74c3c',
                fg='white',
                font=('Arial', 8),
                relief=tk.FLAT,
                cursor='hand2',
                width=3
            )
            delete_btn.pack(side=tk.RIGHT, padx=4, pady=4)

        self.settings_window.update_idletasks()
        if hasattr(self, 'canvas'):
            self.canvas.configure(scrollregion=self.canvas.bbox("all"))

        self.adjust_window_height()

    def browse_program_path(self):
        from tkinter import filedialog

        file_path = filedialog.askopenfilename(
            title="프로그램 파일 선택",
            filetypes=[
                ("모든 파일", "*.*"),
                ("실행 파일", "*.exe")
            ]
        )

        if file_path:
            self.program_path_var.set(file_path)

    def add_program(self):
        text = self.program_text_var.get().strip()
        path = self.program_path_var.get().strip()

        if not text or not path:
            messagebox.showwarning("입력 오류", "표시 텍스트와 파일 경로를 모두 입력해주세요.")
            return

        import os
        if not os.path.exists(path):
            messagebox.showwarning("경로 오류", "지정한 파일이 존재하지 않습니다.")
            return

        custom_programs = load_data('custom_programs', [])

        if len(custom_programs) >= 7:
            messagebox.showwarning("등록 제한", "최대 7개의 프로그램만 등록할 수 있습니다.")
            return

        for program in custom_programs:
            if program['text'] == text or program['path'] == path:
                messagebox.showwarning("중복 오류", "이미 등록된 프로그램입니다.")
                return

        import random
        colors = ['#3498db', '#2ecc71', '#e74c3c', '#f39c12', '#9b59b6', '#1abc9c', '#e67e22', '#34495e']
        color = random.choice(colors)

        new_program = {
            "text": text,
            "path": path,
            "color": color
        }
        custom_programs.append(new_program)

        save_data('custom_programs', custom_programs)

        self.program_text_var.set("")
        self.program_path_var.set("")

        self.update_program_list()

        messagebox.showinfo("추가 완료", f"'{text}' 프로그램이 추가되었습니다.")
        logger.info(f"New program added: {text} -> {path}")

    def delete_program(self, index):
        custom_programs = load_data('custom_programs', [])

        if 0 <= index < len(custom_programs):
            deleted_program = custom_programs[index]

            if messagebox.askyesno("삭제 확인",
                                 f"'{deleted_program['text']}' 프로그램을 삭제하시겠습니까?"):
                custom_programs.pop(index)
                save_data('custom_programs', custom_programs)
                self.update_program_list()
                messagebox.showinfo("삭제 완료", f"'{deleted_program['text']}' 프로그램이 삭제되었습니다.")
                logger.info(f"Program deleted: {deleted_program['text']}")

    def adjust_window_height(self):
        if not hasattr(self, 'canvas') or not self.settings_window:
            return
            
        try:
            self.settings_window.update_idletasks()
            content_height = self.scrollable_frame.winfo_reqheight()
            
            min_height = 600
            screen_height = self.settings_window.winfo_screenheight()
            max_height = min(1200, int(screen_height * 0.8))
            
            desired_height = min(max(content_height + 100, min_height), max_height)
            
            current_geometry = self.settings_window.geometry()
            current_width = int(current_geometry.split('x')[0])
            
            new_geometry = f"{current_width}x{desired_height}"
            self.settings_window.geometry(new_geometry)
            
            logger.info(f"Window height adjusted to: {desired_height} (content: {content_height})")
            
        except Exception as e:
            logger.error(f"Error adjusting window height: {e}")
    
    def reset_status_position(self):
        save_data('status_position', None)

        self.status_window.user_has_moved = False
        save_data('user_has_moved', False)

        window_width = 350
        window_height = 80
        new_x, new_y = self.status_window.get_dpi_aware_default_position(window_width, window_height)

        if hasattr(self.status_window, 'qt_widget') and self.status_window.qt_widget:
            self.status_window.qt_widget.move(new_x, new_y)
            logger.info(f"PyQt5 window moved to DPI-aware position: ({new_x}, {new_y})")
        elif hasattr(self.status_window, 'status_window') and self.status_window.status_window:
            self.status_window.status_window.geometry(f"+{new_x}+{new_y}")
            logger.info(f"Tkinter window moved to DPI-aware position: ({new_x}, {new_y})")

        self.update_position_label()

        logger.info("Status window position reset - DPI-aware alignment restored")

    def update_network_opacity(self, value):
        opacity = float(value)
        save_data('network_status_opacity', opacity)

        if hasattr(self, 'opacity_value_label'):
            self.opacity_value_label.config(text=f"{opacity:.2f}")

        if hasattr(self, 'status_window') and self.status_window and self.status_window.is_visible:
            if self.status_window.edit_mode:
                if hasattr(self.status_window, 'status_window') and self.status_window.status_window:
                    self.status_window.status_window.withdraw()

                self.status_window.set_edit_mode(False)

                if hasattr(self.status_window, 'qt_widget') and self.status_window.qt_widget:
                    self.status_window.qt_widget.setWindowOpacity(opacity)
                    self.status_window.qt_widget.raise_()
                    self.status_window.qt_widget.activateWindow()

                def restore_edit_mode():
                    if hasattr(self, 'status_window') and self.status_window:
                        self.status_window.set_edit_mode(True)

                self.settings_window.after(1000, restore_edit_mode)

            else:
                if hasattr(self.status_window, 'qt_widget') and self.status_window.qt_widget:
                    self.status_window.qt_widget.setWindowOpacity(opacity)
                    self.status_window.qt_widget.raise_()
                    self.status_window.qt_widget.activateWindow()

        logger.info(f"Network status UI opacity updated to: {opacity}")

    def move_status_window(self, dx, dy):
        if self.status_window.is_visible and self.status_window.status_window:
            try:
                current_x = self.status_window.status_window.winfo_x()
                current_y = self.status_window.status_window.winfo_y()
                
                new_x = current_x + dx
                new_y = current_y + dy
                
                screen_width = self.status_window.status_window.winfo_screenwidth()
                screen_height = self.status_window.status_window.winfo_screenheight()
                
                window_width = 200
                window_height = 80
                
                new_x = max(0, min(new_x, screen_width - window_width))
                new_y = max(0, min(new_y, screen_height - window_height))
                
                self.status_window.status_window.geometry(f"+{new_x}+{new_y}")
                
                save_data('status_position', (new_x, new_y))
                
                self.update_position_label()
                
                logger.info(f"Manually moved status window to ({new_x}, {new_y})")
                
            except Exception as e:
                logger.error(f"Manual move error: {e}")
    
    def update_position_label(self):
        if hasattr(self, 'position_label'):
            position = load_data('status_position', None)
            if position:
                x, y = position
                self.position_label.config(text=f"현재 위치: ({x}, {y})")
            else:
                self.position_label.config(text="현재 위치: 기본값")

    def test_click_through(self):
        try:
            if self.status_window.is_visible:
                self.status_window.set_click_through(True)
                
                messagebox.showinfo(
                    "클릭 통과 테스트",
                    "클릭 통과가 활성화되었습니다.\n\n"
                    "이제 상태 창 뒤에 있는 버튼을 클릭해보세요.\n"
                    "웹브라우저나 다른 프로그램을 열어서\n"
                    "상태 창 뒤의 버튼이 클릭되는지 확인하세요.\n\n"
                    "설정을 다시 열면 편집 모드로 돌아갑니다."
                )
            else:
                messagebox.showwarning("테스트 불가", "상태 창이 표시되어 있지 않습니다.")
                
        except Exception as e:
            logger.error(f"Click-through test error: {e}")
            messagebox.showerror("테스트 오류", f"클릭 통과 테스트 중 오류가 발생했습니다:\n{str(e)}")
    
    def recreate_status_window(self):
        try:
            if self.status_window.is_visible:
                current_enabled = self.status_enabled
                
                self.status_window.hide_status_window()
                
                self.settings_window.after(500, lambda: self.restore_status_window(current_enabled))
                
                messagebox.showinfo("창 재생성", "상태 창을 재생성합니다.\n잠시만 기다려주세요.")
            else:
                messagebox.showwarning("재생성 불가", "상태 창이 표시되어 있지 않습니다.")
                
        except Exception as e:
            logger.error(f"Recreate status window error: {e}")
            messagebox.showerror("재생성 오류", f"상태 창 재생성 중 오류가 발생했습니다:\n{str(e)}")
    
    def restore_status_window(self, enabled):
        if enabled:
            self.status_window.show_status_window()
            self.status_window.set_edit_mode(True)
    
    def on_method_changed(self, event):
        try:
            method = self.click_through_method.get()
            save_data('click_through_method', method)
            
            if self.status_window.is_visible and not self.status_window.edit_mode:
                result = messagebox.askyesno(
                    "방식 변경", 
                    f"클릭 통과 방식을 '{method}'로 변경합니다.\n"
                    "지금 즉시 적용하시겠습니까?\n\n"
                    "'아니오'를 선택하면 설정 창을 닫을 때 적용됩니다."
                )
                
                if result:
                    was_visible = self.status_window.is_visible
                    self.status_window.hide_status_window()
                    
                    if was_visible:
                        self.settings_window.after(500, lambda: self.status_window.show_status_window())
                
        except Exception as e:
            logger.error(f"Method change error: {e}")
    
    def try_pyqt_mode(self):
        try:
            import PyQt5
            from PyQt5.QtWidgets import QApplication, QWidget, QLabel
            from PyQt5.QtCore import Qt
            from PyQt5.QtGui import QFont
            
            messagebox.showinfo(
                "PyQt 모드",
                "PyQt5가 설치되어 있습니다.\n"
                "PyQt 기반 상태 창을 생성할 수 있습니다.\n\n"
                "콤보박스에서 'pyqt'를 선택하고 창을 재생성하세요."
            )
            
        except ImportError:
            result = messagebox.askyesno(
                "PyQt5 설치 필요",
                "PyQt5가 설치되어 있지 않습니다.\n\n"
                "PyQt5는 클릭 통과 기능이 가장 안정적으로 작동합니다.\n"
                "설치하시겠습니까?\n\n"
                "명령어: pip install PyQt5"
            )
            
            if result:
                try:
                    import subprocess
                    import sys
                    subprocess.run([sys.executable, '-m', 'pip', 'install', 'PyQt5'], check=True)
                    messagebox.showinfo("설치 완료", "PyQt5가 설치되었습니다.\n프로그램을 재시작해주세요.")
                except Exception as e:
                    messagebox.showerror("설치 실패", f"PyQt5 설치에 실패했습니다:\n{str(e)}")
        
        except Exception as e:
            logger.error(f"PyQt mode test error: {e}")
            messagebox.showerror("오류", f"PyQt 모드 테스트 중 오류가 발생했습니다:\n{str(e)}")
    
    def try_pysimplegui_mode(self):
        try:
            import PySimpleGUI as sg
            
            messagebox.showinfo(
                "PySimpleGUI 모드",
                "PySimpleGUI가 설치되어 있습니다.\n"
                "PySimpleGUI 기반 상태 창을 생성할 수 있습니다.\n\n"
                "콤보박스에서 'pysimplegui'를 선택하고 창을 재생성하세요."
            )
            
        except ImportError:
            result = messagebox.askyesno(
                "PySimpleGUI 설치 필요",
                "PySimpleGUI가 설치되어 있지 않습니다.\n\n"
                "PySimpleGUI는 간단하고 효과적인 GUI 라이브러리입니다.\n"
                "설치하시겠습니까?\n\n"
                "명령어: pip install PySimpleGUI"
            )
            
            if result:
                try:
                    import subprocess
                    import sys
                    subprocess.run([sys.executable, '-m', 'pip', 'install', 'PySimpleGUI'], check=True)
                    messagebox.showinfo("설치 완료", "PySimpleGUI가 설치되었습니다.\n프로그램을 재시작해주세요.")
                except Exception as e:
                    messagebox.showerror("설치 실패", f"PySimpleGUI 설치에 실패했습니다:\n{str(e)}")
        
        except Exception as e:
            logger.error(f"PySimpleGUI mode test error: {e}")
            messagebox.showerror("오류", f"PySimpleGUI 모드 테스트 중 오류가 발생했습니다:\n{str(e)}")
    
    def _create_section_frame(self, parent, title, color):
        """섹션 프레임 생성 헬퍼 메서드"""
        # 외부 컨테이너
        container = tk.Frame(parent, bg='#f0f0f0')

        # 헤더
        header = tk.Frame(container, bg=color, height=32)
        header.pack(fill=tk.X)
        header.pack_propagate(False)

        title_label = tk.Label(header, text=title, font=('Arial', 10, 'bold'),
                              fg='white', bg=color, anchor='w')
        title_label.pack(side=tk.LEFT, padx=10, fill=tk.BOTH, expand=True)

        # 콘텐츠 영역
        content = tk.Frame(container, bg='white', relief=tk.FLAT, bd=0)
        content.pack(fill=tk.BOTH, expand=True, padx=1, pady=(0, 1))

        # 내부 패딩
        inner = tk.Frame(content, bg='white')
        inner.pack(fill=tk.BOTH, expand=True, padx=12, pady=12)

        return inner

    def on_header_click(self, event):
        self.header_click_count += 1
        logger.debug(f"Header clicked: {self.header_click_count}/5")

        if self.header_click_count >= 5:
            self.toggle_env_admin_mode()
            self.header_click_count = 0

    def toggle_env_admin_mode(self):
        self.env_unlocked = not self.env_unlocked

        new_state = tk.NORMAL if self.env_unlocked else tk.DISABLED
        for radio in self.env_radios:
            radio.config(state=new_state)

        status_msg = "활성화" if self.env_unlocked else "비활성화"
        logger.info(f"Environment admin mode: {status_msg}")

    def close_settings(self):
        try:
            self.header_click_count = 0
            self.env_unlocked = False

            if self.status_window:
                self.status_window.set_edit_mode(False)

            if self.settings_window:
                if hasattr(self, 'add_website_btn'):
                    self.add_website_btn = None
                if hasattr(self, 'websites_label'):
                    self.websites_label = None
                if hasattr(self, 'website_list_frame'):
                    self.website_list_frame = None
                if hasattr(self, 'canvas'):
                    self.canvas = None
                if hasattr(self, 'scrollable_frame'):
                    self.scrollable_frame = None
                    
                self.settings_window.destroy()
                self.settings_window = None
                
        except Exception as e:
            logger.error(f"Error closing settings: {e}")
            self.settings_window = None
            
        logger.info("Settings window closed")


class RadialMenu:
    active_progress_windows = []

    # 버튼 색상 팔레트 (클래스 레벨 상수)
    BUTTON_COLORS = {
        'blue': '#3498db',
        'green': '#2ecc71',
        'orange': '#e67e22',
        'yellow': '#f39c12',
        'cyan': '#1abc9c',
        'purple': '#9b59b6',
        'red': '#e74c3c',
        'dark': '#34495e'
    }

    # 호버 효과 색상
    HOVER_OUTLINE_COLOR = '#FFD700'  # 금색 테두리
    HOVER_TEXT_COLOR = '#FFD700'  # 금색 텍스트
    DEFAULT_OUTLINE_COLOR = 'white'
    DEFAULT_TEXT_COLOR = 'white'

    def __init__(self):
        if RadialMenu.active_progress_windows:
            logger.info(f"Closing {len(RadialMenu.active_progress_windows)} active progress windows")
            for window in RadialMenu.active_progress_windows[:]:
                try:
                    window.destroy()
                except:
                    pass
            RadialMenu.active_progress_windows.clear()
        
        self.overlay = None
        self.background = None
        self.buttons = []
        self.mouse_x = 0
        self.mouse_y = 0
        self.status_window = StatusWindow()
        self.settings_window = SettingsWindow(self.status_window)
        self.active_listeners = []

        self._wifi_ssid_cache = None
        self._wifi_ssid_cache_time = 0
        
        self.asset_timer = threading.Timer(5.0, self.start_asset_monitoring)
        self.asset_timer.daemon = True
        self.asset_timer.start()
    
    def cleanup_listeners(self):
        logger.info("Cleaning up existing listeners...")

        if hasattr(self, 'asset_timer') and self.asset_timer:
            try:
                self.asset_timer.cancel()
                logger.info("Asset monitoring timer cancelled")
            except Exception as e:
                logger.error(f"Error cancelling asset timer: {e}")

        for listener in self.active_listeners:
            try:
                if hasattr(listener, 'stop'):
                    listener.stop()
                    logger.info(f"Stopped listener: {type(listener).__name__}")
            except Exception as e:
                logger.error(f"Error stopping listener: {e}")

        self.active_listeners.clear()
        
        if hasattr(self, 'status_window') and self.status_window:
            if hasattr(self.status_window, 'pyqt_mouse_listener'):
                try:
                    self.status_window.pyqt_mouse_listener.stop()
                    logger.info("Stopped PyQt mouse listener")
                    delattr(self.status_window, 'pyqt_mouse_listener')
                    logger.info("PyQt mouse listener reference removed")
                except Exception as e:
                    logger.error(f"Error stopping PyQt listener: {e}")
        
        try:
            import mouse
            mouse.unhook_all()
            logger.info("Cleared mouse hooks")
        except:
            pass
            
        try:
            import keyboard
            keyboard.unhook_all()
            logger.info("Cleared keyboard hooks")
        except:
            pass
        
    def create_menu(self, x, y):
        logger.info(f"create_menu called at position ({x}, {y})")
        
        if RadialMenu.active_progress_windows:
            logger.info(f"Closing {len(RadialMenu.active_progress_windows)} active progress windows before opening menu")
            for window in RadialMenu.active_progress_windows[:]:
                try:
                    window.destroy()
                except:
                    pass
            RadialMenu.active_progress_windows.clear()
        
        was_open = False
        if self.overlay:
            logger.info("Menu already open, closing and reopening at new position...")
            was_open = True
            self.close_menu()
            if was_open:
                pass
            
        self.mouse_x = x
        self.mouse_y = y

        self.overlay = tk.Toplevel()
        self.overlay.withdraw()
        self.overlay.overrideredirect(True)
        self.overlay.attributes('-topmost', True)

        if hasattr(self, 'status_window') and self.status_window:
            self.status_window._radial_menu_active = True
            logger.info("Radial menu active flag set - status clicks will be blocked")

        window_size = 350

        screen_width = self.overlay.winfo_screenwidth()
        screen_height = self.overlay.winfo_screenheight()

        # 듀얼 모니터 지원: 모든 모니터의 전체 범위 계산
        try:
            from screeninfo import get_monitors
            monitors = get_monitors()
            if monitors:
                min_x = min(m.x for m in monitors)
                min_y = min(m.y for m in monitors)
                max_x = max(m.x + m.width for m in monitors)
                max_y = max(m.y + m.height for m in monitors)

                virtual_x = min_x
                virtual_y = min_y
                virtual_width = max_x - min_x
                virtual_height = max_y - min_y

                logger.info(f"Detected {len(monitors)} monitor(s)")
                for i, m in enumerate(monitors):
                    logger.info(f"  Monitor {i+1}: ({m.x}, {m.y}) {m.width}x{m.height}")
            else:
                raise Exception("No monitors detected")
        except Exception as e:
            logger.warning(f"Failed to get multi-monitor info: {e}, using fallback")
            # 가상 루트 정보 가져오기 (fallback)
            virtual_x = self.overlay.winfo_vrootx()
            virtual_y = self.overlay.winfo_vrooty()
            virtual_width = self.overlay.winfo_vrootwidth()
            virtual_height = self.overlay.winfo_vrootheight()

            # 가상 루트 값이 유효하지 않은 경우 화면 크기 사용
            if virtual_width <= 0 or virtual_height <= 0:
                virtual_x = 0
                virtual_y = 0
                virtual_width = screen_width
                virtual_height = screen_height
                logger.warning(f"Invalid virtual root size, using screen size: {screen_width}x{screen_height}")

        logger.info(f"Screen info - Width: {screen_width}, Height: {screen_height}")
        logger.info(f"Virtual root - X: {virtual_x}, Y: {virtual_y}, W: {virtual_width}, H: {virtual_height}")
        logger.info(f"Mouse position - X: {x}, Y: {y}")

        window_x = int(x - window_size // 2)
        window_y = int(y - window_size // 2)

        logger.info(f"Calculated window position before clamping - X: {window_x}, Y: {window_y}")

        min_x = int(virtual_x)
        min_y = int(virtual_y)
        max_x = int(virtual_x + virtual_width - window_size)
        max_y = int(virtual_y + virtual_height - window_size)

        logger.info(f"Boundary limits - min_x: {min_x}, max_x: {max_x}, min_y: {min_y}, max_y: {max_y}")

        if window_x < min_x:
            logger.info(f"Clamping window_x from {window_x} to {min_x}")
            window_x = min_x
        elif window_x > max_x:
            logger.info(f"Clamping window_x from {window_x} to {max_x}")
            window_x = max_x

        if window_y < min_y:
            logger.info(f"Clamping window_y from {window_y} to {min_y}")
            window_y = min_y
        elif window_y > max_y:
            logger.info(f"Clamping window_y from {window_y} to {max_y}")
            window_y = max_y

        logger.info(f"Final window position - X: {window_x}, Y: {window_y}")

        geometry_str = f"{window_size}x{window_size}+{window_x}+{window_y}"
        logger.info(f"Setting geometry: {geometry_str}")
        self.overlay.geometry(geometry_str)

        self.overlay.attributes('-alpha', 0.9)
        self.overlay.configure(bg='#191919')
        self.overlay.wm_attributes('-transparentcolor', '#191919')

        self.overlay.update_idletasks()
        self.overlay.deiconify()

        actual_x = self.overlay.winfo_x()
        actual_y = self.overlay.winfo_y()
        logger.info(f"Actual window position after display - X: {actual_x}, Y: {actual_y}")
        
        self.background = tk.Toplevel()
        self.background.overrideredirect(True)
        self.background.attributes('-alpha', 0.01)

        self.background.geometry(f"{virtual_width}x{virtual_height}+{virtual_x}+{virtual_y}")

        self.background.bind('<Button-1>', lambda e: self.close_menu())

        self.background.attributes('-topmost', True)
        self.overlay.attributes('-topmost', True)

        self.background.lift()
        self.overlay.lift()
        
        self.canvas = tk.Canvas(self.overlay, highlightthickness=0, bg='#191919')
        self.canvas.pack(fill=tk.BOTH, expand=True)
        
        self.local_x = window_size // 2
        self.local_y = window_size // 2
        
        self.create_modern_circular_background()
        
        self.create_buttons()
        
        self.canvas.bind('<Button-1>', self.on_click)
        self.overlay.bind('<Escape>', lambda e: self.close_menu())
        
        self.overlay.focus_set()
        self.overlay.focus_force()
        
        self.overlay.update_idletasks()
        self.overlay.update()
        
        logger.info("Menu created successfully")
        
    def create_buttons(self):
        logger.info("Creating buttons...")
        
        radius = 110
        button_size = 80
        
        button_info = [
            {"text": "웹페이지", "color": "#3498db", "command": self.open_webpage},
            {"text": "탐색기", "color": "#2ecc71", "command": self.open_explorer},
            {"text": "점검", "color": "#e67e22", "command": self.perform_system_check},
            {"text": "자동조치", "color": "#f39c12", "command": self.open_auto_action},
            {"text": "프로그램", "color": "#1abc9c", "command": self.open_program},
            {"text": "설정", "color": "#9b59b6", "command": self.open_settings}
        ]
        
        center_size = 30
        center_circle = self.canvas.create_oval(
            self.local_x - center_size,
            self.local_y - center_size,
            self.local_x + center_size,
            self.local_y + center_size,
            fill='#34495e',
            outline='white',
            width=2,
            tags='center'
        )
        
        self.buttons = []
        for i, btn_info in enumerate(button_info):
            angle = (i * 60 - 90) * math.pi / 180
            
            btn_x = self.local_x + radius * math.cos(angle)
            btn_y = self.local_y + radius * math.sin(angle)
            
            x1 = btn_x - button_size/2
            y1 = btn_y - button_size/2
            x2 = btn_x + button_size/2
            y2 = btn_y + button_size/2
            
            btn = self.canvas.create_oval(
                x1, y1, x2, y2,
                fill=btn_info["color"],
                outline='white',
                width=2,
                tags=f"button_{i}"
            )
            
            text = self.canvas.create_text(
                btn_x, btn_y,
                text=btn_info["text"],
                fill='white',
                font=('Arial', 10, 'bold'),
                tags=f"button_{i}"
            )
            
            self.buttons.append({
                "id": btn,
                "text_id": text,
                "x": btn_x,
                "y": btn_y,
                "size": button_size,
                "command": btn_info["command"],
                "color": btn_info["color"],
                "tag": f"button_{i}"
            })

            # 헬퍼 메서드를 사용하여 이벤트 바인딩
            self._bind_button_events(i, btn_info["command"])
            
            logger.info(f"Button {i} ({btn_info['text']}) created at ({btn_x}, {btn_y}) with tag button_{i}")
        
        logger.info(f"Total {len(self.buttons)} buttons created")
        
        for i in range(len(button_info)):
            tag = f"button_{i}"
            bindings = self.canvas.tag_bind(tag)
            logger.debug(f"Button {i} bindings: {bindings}")
            if not bindings:
                logger.warning(f"Button {i} has no bindings!")
    
    def on_button_hover(self, tag, enter):
        logger.debug(f"Button hover: tag={tag}, enter={enter}")
        try:
            items = self.canvas.find_withtag(tag)

            if enter:
                for item in items:
                    item_type = self.canvas.type(item)
                    if item_type == 'oval':
                        current_fill = self.canvas.itemcget(item, 'fill')
                        self.canvas.itemconfig(item, fill=self._brighten_color(current_fill))
                        self.canvas.itemconfig(item, outline=self.HOVER_OUTLINE_COLOR, width=3)
                    elif item_type == 'text':
                        self.canvas.itemconfig(item, fill=self.HOVER_TEXT_COLOR)
                logger.debug(f"Button {tag} modern hover effect applied")
            else:
                for item in items:
                    item_type = self.canvas.type(item)
                    if item_type == 'oval':
                        original_color = self._get_original_button_color(tag)
                        self.canvas.itemconfig(item, fill=original_color)
                        self.canvas.itemconfig(item, outline=self.DEFAULT_OUTLINE_COLOR, width=2)
                    elif item_type == 'text':
                        self.canvas.itemconfig(item, fill=self.DEFAULT_TEXT_COLOR)
                logger.debug(f"Button {tag} hover effect removed")
        except Exception as e:
            logger.error(f"Error in on_button_hover: {e}")

    def _bind_button_events(self, button_index, command):
        """
        버튼 이벤트 바인딩을 위한 헬퍼 메서드

        Args:
            button_index: 버튼 인덱스 (0부터 시작)
            command: 클릭 시 실행할 명령
        """
        tag = f"button_{button_index}"

        # 마우스 호버 이벤트 바인딩
        self.canvas.tag_bind(tag, '<Enter>',
            lambda e, t=tag: self.on_button_hover(t, True))
        self.canvas.tag_bind(tag, '<Leave>',
            lambda e, t=tag: self.on_button_hover(t, False))

        # 클릭 이벤트 바인딩
        self.canvas.tag_bind(tag, '<Button-1>',
            lambda e, cmd=command: self.on_button_click(cmd))

    def _brighten_color(self, color):
        """
        색상을 밝게 변환 (동적 계산 방식)

        Args:
            color: 16진수 색상 코드 (예: '#3498db')

        Returns:
            밝아진 16진수 색상 코드
        """
        try:
            # 16진수 색상을 RGB로 변환
            color = color.lstrip('#')
            r, g, b = int(color[0:2], 16), int(color[2:4], 16), int(color[4:6], 16)

            # RGB를 HSV로 변환
            r_norm, g_norm, b_norm = r / 255.0, g / 255.0, b / 255.0
            max_c = max(r_norm, g_norm, b_norm)
            min_c = min(r_norm, g_norm, b_norm)
            diff = max_c - min_c

            # Hue 계산
            if diff == 0:
                h = 0
            elif max_c == r_norm:
                h = (60 * ((g_norm - b_norm) / diff) + 360) % 360
            elif max_c == g_norm:
                h = (60 * ((b_norm - r_norm) / diff) + 120) % 360
            else:
                h = (60 * ((r_norm - g_norm) / diff) + 240) % 360

            # Saturation 계산
            s = 0 if max_c == 0 else (diff / max_c)

            # Value (밝기)
            v = max_c

            # 밝기와 채도 조정 (더 밝고 생생하게)
            v = min(1.0, v + 0.2)  # 밝기 20% 증가
            s = max(0.3, s * 0.9)  # 채도 약간 감소하여 부드럽게

            # HSV를 RGB로 변환
            c = v * s
            x = c * (1 - abs(((h / 60) % 2) - 1))
            m = v - c

            if 0 <= h < 60:
                r_prime, g_prime, b_prime = c, x, 0
            elif 60 <= h < 120:
                r_prime, g_prime, b_prime = x, c, 0
            elif 120 <= h < 180:
                r_prime, g_prime, b_prime = 0, c, x
            elif 180 <= h < 240:
                r_prime, g_prime, b_prime = 0, x, c
            elif 240 <= h < 300:
                r_prime, g_prime, b_prime = x, 0, c
            else:
                r_prime, g_prime, b_prime = c, 0, x

            # RGB 값으로 변환
            r_final = int((r_prime + m) * 255)
            g_final = int((g_prime + m) * 255)
            b_final = int((b_prime + m) * 255)

            return f'#{r_final:02x}{g_final:02x}{b_final:02x}'
        except Exception as e:
            logger.error(f"Error brightening color {color}: {e}")
            return '#FFFFFF'  # 오류 시 기본값 반환
    
    def _get_original_button_color(self, tag):
        """
        버튼의 원래 색상을 가져옴 (개선된 버전)

        우선순위:
        1. buttons 리스트에서 color 속성 확인
        2. 캔버스 아이템에서 직접 색상 추출
        3. 기본 색상 맵 참조

        Args:
            tag: 버튼 태그 (예: 'button_0')

        Returns:
            16진수 색상 코드
        """
        # 1순위: buttons 리스트에서 색상 찾기
        if hasattr(self, 'buttons') and self.buttons:
            for button in self.buttons:
                if button.get("tag") == tag and "color" in button:
                    return button["color"]

        # 2순위: 캔버스 아이템에서 직접 색상 가져오기 (호버 전 상태 복원)
        try:
            items = self.canvas.find_withtag(tag)
            for item in items:
                if self.canvas.type(item) == 'oval':
                    # 캔버스 아이템의 현재 fill 색상 확인 (초기 생성 시 저장된 색상)
                    if hasattr(self, '_button_original_colors'):
                        return self._button_original_colors.get(tag, '#34495e')
        except Exception as e:
            logger.debug(f"Could not get color from canvas for {tag}: {e}")

        # 3순위: 기본 색상 맵 (폴백 - 클래스 상수 사용)
        main_button_colors = {
            'button_0': self.BUTTON_COLORS['blue'],
            'button_1': self.BUTTON_COLORS['green'],
            'button_2': self.BUTTON_COLORS['orange'],
            'button_3': self.BUTTON_COLORS['yellow'],
            'button_4': self.BUTTON_COLORS['cyan'],
            'button_5': self.BUTTON_COLORS['purple']
        }

        return main_button_colors.get(tag, self.BUTTON_COLORS['dark'])
    
    def create_modern_circular_background(self):
        center_x = self.local_x
        center_y = self.local_y
        
        blur_layers = [
            {'radius': 175, 'color': '#2C3E50', 'alpha': 0.15},
            {'radius': 165, 'color': '#34495E', 'alpha': 0.25},
            {'radius': 155, 'color': '#3F4F5F', 'alpha': 0.35},
        ]
        
        for layer in blur_layers:
            self.canvas.create_oval(
                center_x - layer['radius'], 
                center_y - layer['radius'],
                center_x + layer['radius'], 
                center_y + layer['radius'],
                fill=layer['color'],
                outline='',
                width=0,
                stipple='gray50'
            )
        
        gradient_layers = [
            {'radius': 140, 'color': '#4A5B6C'},
            {'radius': 135, 'color': '#5D6E7F'},
            {'radius': 130, 'color': '#708192'},
        ]
        
        for layer in gradient_layers:
            self.canvas.create_oval(
                center_x - layer['radius'], 
                center_y - layer['radius'],
                center_x + layer['radius'], 
                center_y + layer['radius'],
                fill=layer['color'],
                outline='',
                width=0
            )
        
        self.canvas.create_oval(
            center_x - 140, 
            center_y - 140,
            center_x + 140, 
            center_y + 140,
            fill='',
            outline='#85C1E9',
            width=1
        )
        
        logger.info("Modern circular background created")
    
    def create_modern_circular_background_submenu(self):
        logger.info("Creating simple circular background for check submenu")

        center_x, center_y = self.local_x, self.local_y
        radius = 160

        self.canvas.create_oval(
            center_x - radius, center_y - radius,
            center_x + radius, center_y + radius,
            fill='#1a1a1a', outline='', tags='background'
        )

        inner_radius = radius - 20
        self.canvas.create_oval(
            center_x - inner_radius, center_y - inner_radius,
            center_x + inner_radius, center_y + inner_radius,
            fill='#2a2a2a', outline='', tags='background'
        )

        core_radius = radius - 40
        self.canvas.create_oval(
            center_x - core_radius, center_y - core_radius,
            center_x + core_radius, center_y + core_radius,
            fill='#333333', outline='', tags='background'
        )

        self.canvas.create_oval(
            center_x - radius, center_y - radius,
            center_x + radius, center_y + radius,
            fill='', outline='#e67e22', width=2,
            tags='background'
        )

        self.canvas.create_oval(
            center_x - inner_radius, center_y - inner_radius,
            center_x + inner_radius, center_y + inner_radius,
            fill='', outline='#e67e22', width=1,
            tags='background'
        )

        center_dot_radius = 3
        self.canvas.create_oval(
            center_x - center_dot_radius, center_y - center_dot_radius,
            center_x + center_dot_radius, center_y + center_dot_radius,
            fill='#e67e22', outline='', tags='background'
        )

        logger.info("Simple background created")
    
    def create_modern_circular_background_website(self):
        logger.info("Creating simple circular background for website submenu")

        center_x, center_y = self.local_x, self.local_y
        radius = 160

        self.canvas.create_oval(
            center_x - radius, center_y - radius,
            center_x + radius, center_y + radius,
            fill='#1a1a1a', outline='', tags='background'
        )

        inner_radius = radius - 20
        self.canvas.create_oval(
            center_x - inner_radius, center_y - inner_radius,
            center_x + inner_radius, center_y + inner_radius,
            fill='#2a2a2a', outline='', tags='background'
        )

        core_radius = radius - 40
        self.canvas.create_oval(
            center_x - core_radius, center_y - core_radius,
            center_x + core_radius, center_y + core_radius,
            fill='#333333', outline='', tags='background'
        )

        self.canvas.create_oval(
            center_x - radius, center_y - radius,
            center_x + radius, center_y + radius,
            fill='', outline='#2ecc71', width=2,
            tags='background'
        )

        self.canvas.create_oval(
            center_x - inner_radius, center_y - inner_radius,
            center_x + inner_radius, center_y + inner_radius,
            fill='', outline='#2ecc71', width=1,
            tags='background'
        )

        center_dot_radius = 3
        self.canvas.create_oval(
            center_x - center_dot_radius, center_y - center_dot_radius,
            center_x + center_dot_radius, center_y + center_dot_radius,
            fill='#2ecc71', outline='', tags='background'
        )

        logger.info("Simple background created")
    
    def create_website_buttons(self):
        logger.info("Creating website buttons...")
        
        websites = self.load_website_data()
        
        radius = 110
        button_size = 70
        
        center_size = 25
        center_circle = self.canvas.create_oval(
            self.local_x - center_size,
            self.local_y - center_size,
            self.local_x + center_size,
            self.local_y + center_size,
            fill='#34495e',
            outline='white',
            width=2
        )
        
        self.canvas.create_text(
            self.local_x,
            self.local_y,
            text="웹사이트",
            font=('Arial', 9, 'bold'),
            fill='white'
        )
        
        self.buttons = []
        display_count = min(len(websites), 8)
        
        for i, website in enumerate(websites):
            if i >= 8:
                break
                
            angle = (i * (360 / display_count) - 90) * math.pi / 180
            
            btn_x = self.local_x + radius * math.cos(angle)
            btn_y = self.local_y + radius * math.sin(angle)
            
            x1 = btn_x - button_size/2
            y1 = btn_y - button_size/2
            x2 = btn_x + button_size/2
            y2 = btn_y + button_size/2
            
            btn = self.canvas.create_oval(
                x1, y1, x2, y2,
                fill=website["color"],
                outline='white',
                width=2,
                tags=f"button_{i}"
            )
            
            text = self.canvas.create_text(
                btn_x, btn_y,
                text=website['text'],
                font=('Arial', 10, 'bold'),
                fill='white',
                tags=f"button_{i}"
            )
            
            command = lambda url=website['url']: self.open_website_url(url)

            self.buttons.append({
                "id": btn,
                "text_id": text,
                "x": btn_x,
                "y": btn_y,
                "size": button_size,
                "command": command,
                "color": website['color'],
                "tag": f"button_{i}"
            })

            # 헬퍼 메서드를 사용하여 이벤트 바인딩
            self._bind_button_events(i, command)
            
            logger.info(f"Website button {i} ({website['text']}) created at ({btn_x}, {btn_y})")
        
        logger.info(f"Created {len(self.buttons)} website buttons")
    
    def load_website_data(self):
        custom_websites = load_data('custom_websites', [])
        
        default_websites = [
            {"text": "지오메디컬 링크", "url": "https://lnk.geomedical.kr", "color": "#27AE60"}
        ]
        
        all_websites = default_websites + custom_websites
        
        return all_websites
    
    def open_website_url(self, url):
        logger.info(f"Opening website: {url}")
        self.close_menu()
        webbrowser.open(url)
        logger.info(f"Website opened: {url}")
    
    def on_button_click(self, command):
        logger.info(f"Button clicked, executing command: {command}")
        try:
            command()
            logger.info("Command executed successfully")
        except Exception as e:
            logger.error(f"Error executing command: {e}")
    
    def on_click(self, event):
        x, y = event.x, event.y
        logger.debug(f"Canvas click at ({x}, {y})")
        
        center_dist = math.sqrt((x - self.local_x)**2 + (y - self.local_y)**2)
        if center_dist <= 30:
            logger.info("Center circle clicked, closing menu")
            self.close_menu()
            return
        
        for btn in self.buttons:
            btn_dist = math.sqrt((x - btn["x"])**2 + (y - btn["y"])**2)
            if btn_dist <= btn["size"] / 2:
                logger.info(f"Button clicked at ({btn['x']}, {btn['y']})")
                self.on_button_click(btn["command"])
                return
        
        logger.info("Empty space clicked, closing menu")
        self.close_menu()
    
    def close_menu(self):
        logger.info("Closing menu...")
        if self.overlay:
            self.overlay.destroy()
            self.overlay = None
            logger.info("Overlay destroyed")
        if hasattr(self, 'background') and self.background:
            self.background.destroy()
            self.background = None
            logger.info("Background destroyed")
        self.buttons = []
        
        if hasattr(self, 'status_window') and self.status_window:
            self.status_window._radial_menu_active = False
            logger.info("Radial menu active flag cleared - status clicks enabled")
        
        logger.info("Menu closed successfully")
    
    def open_webpage(self):
        logger.info("Opening website submenu...")
        self.close_menu()
        self.create_website_submenu(self.mouse_x, self.mouse_y)
    
    def open_explorer(self):
        logger.info("Opening This PC...")
        self.close_menu()
        import subprocess
        if os.name == 'nt':
            subprocess.Popen('explorer ::{20D04FE0-3AEA-1069-A2D8-08002B30309D}', shell=True,
                           creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0)
        else:
            subprocess.Popen(['xdg-open', os.path.expanduser('~')])
        logger.info("This PC opened")

    def open_program(self):
        logger.info("Opening program submenu...")
        self.close_menu()
        self.create_program_submenu(self.mouse_x, self.mouse_y)

    def check_internet(self):
        logger.info("Checking internet connection...")
        self.close_menu()
        
        def is_connected():
            return self.status_window.check_internet_connection() if self.status_window else False
        
        if is_connected():
            logger.info("Internet connection: OK")
            
            network_info = self.get_detailed_network_info()
            
            message = f"인터넷 연결 상태: 정상 🟢\n\n{network_info}"
            messagebox.showinfo("인터넷 상태", message)
        else:
            logger.info("Internet connection: OFFLINE")
            messagebox.showwarning("인터넷 상태", "인터넷 연결 상태: 오프라인 🔴\n\n연결을 확인해 주세요.")
    
    def get_network_info(self):
        try:
            ip = self.get_local_ip()

            if ip == "IP 주소를 가져올 수 없음":
                return "네트워크 연결 없음"

            ssid = self.get_wifi_ssid()

            if ssid and ssid != "":
                return f"WiFi ({ssid})"
            else:
                return f"유선 연결"

        except Exception as e:
            logger.error(f"Network info error: {e}")
            return "네트워크 연결 확인 중..."
    
    def get_detailed_network_info(self):
        try:
            ip = self.get_local_ip()

            if ip == "IP 주소를 가져올 수 없음":
                return "네트워크: 연결 없음\nIP 주소: 할당되지 않음"

            ssid = self.get_wifi_ssid()

            if ssid and ssid != "":
                return f"네트워크: WiFi ({ssid})\nIP 주소: {ip}"
            else:
                return f"네트워크: 유선 연결\nIP 주소: {ip}"

        except Exception as e:
            logger.error(f"Detailed network info error: {e}")
            return "네트워크: 정보 수집 중...\nIP 주소: 확인 중..."
    
    def get_local_ip(self):
        try:
            with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
                s.settimeout(3)
                s.connect(("8.8.8.8", 53))
                ip = s.getsockname()[0]
                if ip:
                    return ip
                else:
                    return "IP 주소를 가져올 수 없음"
        except socket.timeout:
            logger.warning("IP 주소 조회 시간 초과")
            return "IP 주소를 가져올 수 없음"
        except Exception as e:
            logger.warning(f"IP 주소 조회 실패: {e}")
            return "IP 주소를 가져올 수 없음"

    def get_wifi_ssid(self):
        current_time = time.time()

        if (hasattr(self, '_wifi_ssid_cache') and self._wifi_ssid_cache is not None and
            hasattr(self, '_wifi_ssid_cache_time') and current_time - self._wifi_ssid_cache_time < 30):
            return self._wifi_ssid_cache

        try:
            ssid = None
            if platform.system() == "Windows":
                result = subprocess.run(
                    ["netsh", "wlan", "show", "interfaces"],
                    capture_output=True,
                    text=True,
                    encoding='cp949',
                    creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0
                )

                if result.returncode == 0:
                    for line in result.stdout.split('\n'):
                        if 'SSID' in line and 'BSSID' not in line:
                            ssid = line.split(':', 1)[1].strip()
                            if ssid:
                                break

            elif platform.system() == "Darwin":
                result = subprocess.run(
                    ["/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport", "-I"],
                    capture_output=True,
                    text=True
                )

                if result.returncode == 0:
                    ssid_match = re.search(r'SSID: (.+)', result.stdout)
                    if ssid_match:
                        ssid = ssid_match.group(1).strip()

            elif platform.system() == "Linux":
                result = subprocess.run(
                    ["iwgetid", "-r"],
                    capture_output=True,
                    text=True
                )

                if result.returncode == 0:
                    ssid = result.stdout.strip()

            if not hasattr(self, '_wifi_ssid_cache'):
                self._wifi_ssid_cache = None
                self._wifi_ssid_cache_time = 0

            self._wifi_ssid_cache = ssid
            self._wifi_ssid_cache_time = current_time
            return ssid

        except Exception as e:
            logger.error(f"WiFi SSID error: {e}")
            if hasattr(self, '_wifi_ssid_cache'):
                return self._wifi_ssid_cache
            return None
    
    
    def network_recovery_step(self, step_num, command, description):
        try:
            logger.info(f"Step {step_num}: {description}")
            
            if isinstance(command, list):
                result = subprocess.run(
                    command,
                    capture_output=True,
                    text=True,
                    timeout=30,
                    creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0
                )
            else:
                result = subprocess.run(
                    command,
                    shell=True,
                    capture_output=True,
                    text=True,
                    timeout=30,
                    creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0
                )
            
            if result.returncode == 0:
                logger.info(f"Step {step_num} completed successfully")
                return True
            else:
                logger.warning(f"Step {step_num} failed: {result.stderr}")
                return False
                
        except Exception as e:
            logger.error(f"Step {step_num} error: {e}")
            return False
    
    def create_wifi_profile(self, ssid, password):
        is_hidden = (ssid == 'GEO_NET')

        non_broadcast_tag = '<nonBroadcast>true</nonBroadcast>' if is_hidden else ''

        profile_xml = f'''<?xml version="1.0"?>
<WLANProfile xmlns="http://www.microsoft.com/networking/WLAN/profile/v1">
    <name>{ssid}</name>
    <SSIDConfig>
        <SSID>
            <hex>{ssid.encode('utf-8').hex().upper()}</hex>
            <name>{ssid}</name>
        </SSID>
        {non_broadcast_tag}
    </SSIDConfig>
    <connectionType>ESS</connectionType>
    <connectionMode>auto</connectionMode>
    <autoSwitch>false</autoSwitch>
    <MSM>
        <security>
            <authEncryption>
                <authentication>WPA2PSK</authentication>
                <encryption>AES</encryption>
                <useOneX>false</useOneX>
            </authEncryption>
            <sharedKey>
                <keyType>passPhrase</keyType>
                <protected>false</protected>
                <keyMaterial>{password}</keyMaterial>
            </sharedKey>
        </security>
    </MSM>
</WLANProfile>'''
        
        cache_dir = get_cache_dir()
        profile_file = os.path.join(cache_dir, f'{ssid}.xml')
        
        with open(profile_file, 'w', encoding='utf-8') as f:
            f.write(profile_xml)
        
        return profile_file
    
    def connect_to_wifi(self, ssid, password):
        try:
            logger.info(f"Attempting to connect to WiFi: {ssid}")

            subprocess.run(['netsh', 'wlan', 'delete', 'profile', f'name={ssid}'],
                         capture_output=True, encoding='cp949',
                         creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0)

            profile_file = self.create_wifi_profile(ssid, password)
            add_result = subprocess.run(['netsh', 'wlan', 'add', 'profile', f'filename={profile_file}'],
                       capture_output=True, text=True, encoding='cp949',
                         creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0)

            try:
                os.remove(profile_file)
            except:
                pass

            logger.info(f"Profile add result: {add_result.stdout}")

            time.sleep(2)
            check_profile = subprocess.run(['netsh', 'wlan', 'show', 'profiles'],
                                         capture_output=True, text=True, encoding='cp949',
                         creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0)

            if ssid not in check_profile.stdout:
                logger.error(f"Failed to add WiFi profile for {ssid}")
                return False
            else:
                logger.info(f"WiFi profile for {ssid} successfully added")

            max_attempts = 3
            for attempt in range(max_attempts):
                logger.info(f"Connection attempt {attempt + 1}/{max_attempts}")

                time.sleep(5 if attempt > 0 else 3)

                if ssid == 'GEO_NET':
                    connect_result = subprocess.run(['netsh', 'wlan', 'connect', f'name={ssid}', f'ssid={ssid}'],
                                   capture_output=True, text=True, encoding='cp949',
                         creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0)
                else:
                    connect_result = subprocess.run(['netsh', 'wlan', 'connect', f'name={ssid}'],
                                   capture_output=True, text=True, encoding='cp949',
                         creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0)

                logger.info(f"Connection attempt result: {connect_result.stdout}")

                if '연결 요청을 완료' in connect_result.stdout or 'connection request was completed' in connect_result.stdout:
                    time.sleep(15)
                    status_result = subprocess.run(['netsh', 'wlan', 'show', 'interface'],
                                                capture_output=True, text=True, encoding='cp949',
                         creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0)

                    if ssid in status_result.stdout and ('연결됨' in status_result.stdout or 'connected' in status_result.stdout.lower()):
                        logger.info(f"Successfully connected to WiFi: {ssid}")
                        return True
                    else:
                        logger.warning(f"Connection attempt {attempt + 1} failed, retrying...")
                else:
                    logger.warning(f"Connection request failed on attempt {attempt + 1}")

            logger.error(f"Failed to connect to WiFi after {max_attempts} attempts")
            return False
            
        except Exception as e:
            logger.error(f"WiFi connection error: {e}")
            return False
    
    def auto_network_recovery(self):
        logger.info("Starting network auto recovery...")
        
        env_setting = load_data('environment', 'office')
        
        wifi_configs = {
            'office': {'ssid': 'GEO_NET', 'password': 'Geo21eye@'},
            'field': {'ssid': 'GEO_MES', 'password': 'geo123!@#'}
        }
        
        wifi_config = wifi_configs.get(env_setting, wifi_configs['office'])
        
        logger.info("Step 0: 네트워크 어댑터 활성화")
        adapter_enabled = enable_network_adapters()
        if not adapter_enabled:
            logger.warning("Network adapter activation failed, but continuing with recovery...")
        
        recovery_steps = [
            (1, "ipconfig /flushdns", "DNS 캐시 초기화"),
            (2, "ipconfig /release", "IP 주소 해제"),
            (3, "ipconfig /renew", "IP 주소 갱신"),
            (4, "netsh winsock reset", "Winsock 리셋"),
        ]
        
        for step_num, command, description in recovery_steps:
            success = self.network_recovery_step(step_num, command, description)
            if step_num == 4 and success:
                time.sleep(5)
        
        logger.info("Step 5: 무선 인터페이스 확인")
        
        has_wifi = self.check_wifi_interface()
        
        if has_wifi:
            logger.info("Step 6: WiFi 연결 시도")
            wifi_success = self.connect_to_wifi(wifi_config['ssid'], wifi_config['password'])
            
            if wifi_success:
                time.sleep(10)
                if self.check_internet_connection():
                    logger.info("Network recovery completed successfully")
                    return True
        else:
            logger.info("No wireless interface found - skipping WiFi connection")
            time.sleep(5)
            if self.check_internet_connection():
                logger.info("Network recovery completed successfully (wired connection)")
                return True
        
        logger.warning("Network recovery failed")
        return False
    
    def start_network_monitor(self):
        self.network_monitor_running = True
        
        def monitor_loop():
            import time
            check_interval = 30
            retry_interval = 60
            max_retries = 3
            retry_count = 0
            offline_detected = False
            
            logger.info("Network monitor started - checking every 30 seconds")
            
            while self.network_monitor_running:
                try:
                    unwanted_network_disconnected = self.check_and_handle_unwanted_networks()
                    
                    if unwanted_network_disconnected:
                        logger.info("Unwanted network disconnected. Waiting before recovery...")
                        time.sleep(10)
                        recovery_success = self.auto_network_recovery()
                        if recovery_success:
                            logger.info("Successfully connected to proper network after unwanted network removal")
                        else:
                            logger.warning("Failed to connect to proper network after unwanted network removal")
                        continue
                    
                    is_online = self.check_internet_connection()
                    
                    if not is_online:
                        if not offline_detected:
                            offline_detected = True
                            logger.warning("Internet connection lost. Starting recovery...")
                        
                        if retry_count < max_retries:
                            logger.info(f"Starting network recovery attempt {retry_count + 1}/{max_retries}")
                            recovery_success = self.auto_network_recovery()
                            
                            if recovery_success:
                                retry_count = 0
                                offline_detected = False
                                logger.info("Network recovery successful - back online")
                            else:
                                retry_count += 1
                                logger.warning(f"Network recovery failed. Attempt {retry_count}/{max_retries}")
                                time.sleep(retry_interval)
                        else:
                            logger.error("Maximum retry attempts reached. Waiting for next cycle...")
                            retry_count = 0
                            time.sleep(retry_interval * 2)
                    else:
                        if offline_detected:
                            logger.info("Internet connection restored")
                            offline_detected = False
                            retry_count = 0
                        
                        time.sleep(check_interval)
                    
                except Exception as e:
                    logger.error(f"Network monitor error: {e}")
                    time.sleep(60)
        
        self.network_thread = threading.Thread(target=monitor_loop, daemon=True)
        self.network_thread.start()
        logger.info("Network monitor started")
    
    def stop_network_monitor(self):
        logger.info("Stopping network monitor...")
        self.network_monitor_running = False
        if hasattr(self, 'network_thread'):
            try:
                self.network_thread.join(timeout=1)
            except:
                pass
            logger.info("Network monitor stopped")
    
    def check_internet_connection(self):
        return self.status_window.check_internet_connection()
    
    def check_wifi_interface(self):
        try:
            result = subprocess.run([
                'netsh', 'wlan', 'show', 'interfaces'
            ], capture_output=True, text=True, encoding='cp949', timeout=10,
            creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0)
            
            if ("시스템에 무선 인터페이스가 없습니다" in result.stdout or
                "There is no wireless interface on the system" in result.stdout):
                logger.info("No wireless interface detected")
                return False
            
            if "이름" in result.stdout or "Name" in result.stdout:
                logger.info("Wireless interface detected")
                return True
            
            return False
            
        except Exception as e:
            logger.error(f"Error checking wireless interface: {e}")
            return False
    
    def get_current_wifi_ssid(self):
        import time

        current_time = time.time()
        if hasattr(self, '_wifi_ssid_cache') and hasattr(self, '_wifi_ssid_cache_time'):
            if current_time - self._wifi_ssid_cache_time < 30:
                return self._wifi_ssid_cache

        try:
            result = subprocess.run([
                'netsh', 'wlan', 'show', 'interfaces'
            ], capture_output=True, text=True, encoding='cp949', timeout=10,
            creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0)

            ssid = None
            if result.returncode == 0:
                lines = result.stdout.split('\n')
                for line in lines:
                    line = line.strip()
                    if 'SSID' in line and ':' in line and 'BSSID' not in line:
                        ssid = line.split(':', 1)[1].strip()
                        if ssid and ssid != 'N/A':
                            break

            self._wifi_ssid_cache = ssid
            self._wifi_ssid_cache_time = current_time
            return ssid

        except Exception as e:
            logger.error(f"Error getting current WiFi SSID: {e}")
            if hasattr(self, '_wifi_ssid_cache'):
                return self._wifi_ssid_cache
            return None
    
    def get_current_ip_address(self):
        try:
            import socket
            import psutil

            for interface_name, addrs in psutil.net_if_addrs().items():
                for addr in addrs:
                    if addr.family == socket.AF_INET:
                        ip = addr.address
                        if (ip and
                            not ip.startswith('127.') and
                            not ip.startswith('169.254.') and
                            ip != '0.0.0.0'):
                            stats = psutil.net_if_stats().get(interface_name)
                            if stats and stats.isup:
                                return ip

            try:
                with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
                    s.connect(("8.8.8.8", 53))
                    return s.getsockname()[0]
            except:
                pass

            return None
        except Exception as e:
            logger.error(f"Error getting current IP address: {e}")
            return None
    
    def is_ip_in_range(self, ip_address, network_range):
        try:
            import ipaddress
            
            ip = ipaddress.ip_address(ip_address)
            network = ipaddress.ip_network(network_range, strict=False)
            
            return ip in network
        except Exception as e:
            logger.error(f"Error checking IP range: {e}")
            return False
    
    def disconnect_wifi_and_delete_profile(self, ssid):
        try:
            logger.info(f"Disconnecting from WiFi: {ssid}")
            disconnect_result = subprocess.run([
                'netsh', 'wlan', 'disconnect'
            ], capture_output=True, text=True, encoding='cp949', timeout=10,
            creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0)
            
            if disconnect_result.returncode == 0:
                logger.info("WiFi disconnected successfully")
            else:
                logger.warning(f"WiFi disconnect failed: {disconnect_result.stderr}")
            
            time.sleep(3)
            
            logger.info(f"Deleting WiFi profile: {ssid}")
            delete_result = subprocess.run([
                'netsh', 'wlan', 'delete', 'profile', f'name={ssid}'
            ], capture_output=True, text=True, encoding='cp949', timeout=10,
            creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0)
            
            if delete_result.returncode == 0:
                logger.info(f"WiFi profile '{ssid}' deleted successfully")
                return True
            else:
                logger.warning(f"WiFi profile deletion failed: {delete_result.stderr}")
                return False
                
        except Exception as e:
            logger.error(f"Error disconnecting WiFi and deleting profile: {e}")
            return False
    
    def check_and_handle_unwanted_networks(self):
        try:
            current_ssid = self.get_current_wifi_ssid()
            current_ip = self.get_current_ip_address()
            
            logger.debug(f"Current SSID: {current_ssid}, Current IP: {current_ip}")
            
            if current_ssid and current_ssid == "GEO Mobile Wi-Fi":
                logger.warning(f"Unwanted SSID detected: {current_ssid}")
                if self.disconnect_wifi_and_delete_profile(current_ssid):
                    logger.info("Successfully disconnected from GEO Mobile Wi-Fi")
                    return True
            
            if current_ip and self.is_ip_in_range(current_ip, "20.20.20.0/21"):
                logger.warning(f"Unwanted IP range detected: {current_ip}")
                if current_ssid:
                    if self.disconnect_wifi_and_delete_profile(current_ssid):
                        logger.info(f"Successfully disconnected from network with unwanted IP: {current_ip}")
                        return True
                else:
                    subprocess.run(['netsh', 'wlan', 'disconnect'],
                                 capture_output=True, text=True, encoding='cp949', timeout=10,
                                 creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0)
                    logger.info("Disconnected from network with unwanted IP")
                    return True
            
            return False
            
        except Exception as e:
            logger.error(f"Error checking unwanted networks: {e}")
            return False
    
    def get_rust_info(self):
        try:
            hostname = platform.node()
            logger.info(f"🔍 서버 API를 통한 RUST 정보 조회 시작 - hostname: {hostname}")
            
            try:
                import requests
                server_url = "http://192.168.0.240:8800"
                api_url = f"{server_url}/api/asset/{hostname}"
                
                response = requests.get(api_url, timeout=5)
                logger.info(f"📞 서버 API 응답 코드: {response.status_code}")
                
                if response.status_code == 200:
                    asset_data = response.json()
                    logger.info(f"📋 서버 응답 데이터 구조: {list(asset_data.keys())}")
                    
                    if asset_data.get('success') and 'asset' in asset_data:
                        asset_info = asset_data['asset']
                        rust_info = asset_info.get('rust_info')
                        logger.info(f"🔎 asset 내부 구조: {list(asset_info.keys())}")
                        logger.info(f"🔍 RUST 정보 값: {rust_info}")
                        
                        if rust_info:
                            logger.info(f"✅ 서버에서 RUST 정보 획득: {rust_info}")
                            return rust_info
                        else:
                            logger.info(f"ℹ️ 서버에는 RUST 정보가 없음, 직접 DB 조회 시도")
                    else:
                        logger.warning(f"⚠️ 서버 응답 구조 오류: {asset_data}")
                else:
                    logger.warning(f"⚠️ 서버 API 응답 오류: {response.status_code}")
                    
            except Exception as api_error:
                logger.warning(f"⚠️ 서버 API 호출 실패: {api_error}")
            
            logger.info(f"📁 직접 icons.db 조회 시도")
            icon_db_path = r'C:\Users\Administrator\Desktop\item\instance\icons.db'
            
            logger.info(f"📁 icons.db 경로: {icon_db_path}")
            
            if not os.path.exists(icon_db_path):
                logger.warning(f"❌ Icon database not found: {icon_db_path}")
                return None
                
            logger.info(f"✅ icons.db 파일 존재 확인됨")
            
            conn = sqlite3.connect(
                f"file:{icon_db_path}?mode=ro",
                uri=True,
                timeout=5.0
            )
            
            cursor = conn.cursor()
            
            cursor.execute("PRAGMA table_info(icon)")
            columns = cursor.fetchall()
            logger.info(f"🏷️ icon 테이블 컬럼: {columns}")
            
            cursor.execute("SELECT comp_no, remote_address FROM icon LIMIT 5")
            sample_data = cursor.fetchall()
            logger.info(f"📊 icon 테이블 샘플 데이터: {sample_data}")
            
            logger.info(f"🔎 검색 쿼리 실행 - WHERE comp_no = '{hostname}'")
            cursor.execute("SELECT remote_address FROM icon WHERE comp_no = ?", (hostname,))
            result = cursor.fetchone()
            logger.info(f"🎯 쿼리 결과: {result}")
            
            conn.close()
            
            if result and result[0]:
                logger.info(f"✅ RUST 정보 찾음: {result[0]}")
                return result[0]
            else:
                logger.warning(f"❌ 해당 호스트명({hostname})에 대한 RUST 정보 없음")
                return None
            
        except Exception as e:
            logger.error(f"❌ Error getting RUST info: {e}")
            import traceback
            traceback.print_exc()
            return None
    
    def show_pc_info(self):
        logger.info("Showing PC info...")
        self.close_menu()
        
        try:
            cpu_percent = psutil.cpu_percent(interval=1)
            cpu_count = psutil.cpu_count()
            
            memory = psutil.virtual_memory()
            memory_total = round(memory.total / (1024**3), 2)
            memory_used = round(memory.used / (1024**3), 2)
            memory_percent = memory.percent
            
            disk = psutil.disk_usage('/')
            disk_total = round(disk.total / (1024**3), 2)
            disk_used = round(disk.used / (1024**3), 2)
            disk_percent = disk.percent
            
            rust_info = self.get_rust_info()
            rust_display = rust_info if rust_info else "정보 없음"
            
            system_info = f"""컴퓨터 정보
            
운영체제: {platform.system()} {platform.release()}
프로세서: {self.get_cpu_info()}
CPU 코어: {cpu_count}개
CPU 사용률: {cpu_percent}%

메모리: {memory_used}GB / {memory_total}GB ({memory_percent}%)
디스크: {disk_used}GB / {disk_total}GB ({disk_percent}%)

컴퓨터 이름: {platform.node()}
RUST: {rust_display}"""
            
            messagebox.showinfo("컴퓨터 정보", system_info)
            
        except Exception as e:
            messagebox.showerror("오류", f"정보를 가져올 수 없습니다.\n{str(e)}")
    
    def open_auto_action(self):
        logger.info("Opening auto action submenu...")
        mouse_x = self.mouse_x
        mouse_y = self.mouse_y
        self.close_menu()
        
        self.create_auto_action_submenu(mouse_x, mouse_y)
    
    def create_auto_action_submenu(self, x, y):
        logger.info(f"Creating auto action submenu at position ({x}, {y})")
        
        if self.overlay:
            logger.info("Auto action submenu already open, closing...")
            self.close_menu()
            return
        
        self.mouse_x = x
        self.mouse_y = y
        
        self.overlay = tk.Toplevel()
        self.overlay.attributes('-topmost', True)
        self.overlay.overrideredirect(True)
        
        window_size = 350
        
        virtual_x = self.overlay.winfo_vrootx()
        virtual_y = self.overlay.winfo_vrooty()
        virtual_width = self.overlay.winfo_vrootwidth()
        virtual_height = self.overlay.winfo_vrootheight()
        
        window_x = x - window_size // 2
        window_y = y - window_size // 2
        
        min_x = virtual_x
        min_y = virtual_y
        max_x = virtual_x + virtual_width - window_size
        max_y = virtual_y + virtual_height - window_size
        
        if window_x < min_x:
            window_x = min_x
        elif window_x > max_x:
            window_x = max_x
        
        if window_y < min_y:
            window_y = min_y
        elif window_y > max_y:
            window_y = max_y
        
        self.overlay.geometry(f"{window_size}x{window_size}+{window_x}+{window_y}")
        
        self.overlay.attributes('-alpha', 0.9)
        self.overlay.configure(bg='#191919')
        self.overlay.wm_attributes('-transparentcolor', '#191919')
        
        self.background = tk.Toplevel()
        self.background.attributes('-topmost', True)
        self.background.attributes('-alpha', 0.01)
        self.background.overrideredirect(True)
        
        self.background.geometry(f"{virtual_width}x{virtual_height}+{virtual_x}+{virtual_y}")
        
        self.background.bind('<Button-1>', lambda e: self.close_menu())
        
        self.canvas = tk.Canvas(
            self.overlay,
            width=window_size,
            height=window_size,
            bg='#191919',
            highlightthickness=0
        )
        self.canvas.pack()
        
        self.local_x = window_size // 2
        self.local_y = window_size // 2
        
        self.create_modern_circular_background_auto_action()
        
        self.create_auto_action_buttons()
        
        self.canvas.bind('<Button-1>', self.on_click)
        self.overlay.bind('<Escape>', lambda e: self.close_menu())
        
        self.overlay.focus_set()
        self.overlay.focus_force()
        
        self.overlay.update_idletasks()
        
        logger.info("Auto action submenu created successfully")
        
        self.start_auto_action_animations()
    
    def start_asset_monitoring(self):
        logger.info("Starting background asset monitoring...")
        
        def register_asset():
            try:
                import requests
                import json
                import datetime
                import uuid
                
                server_url = "http://192.168.0.240:8800"
                
                hostname = platform.node()
                
                try:
                    try:
                        with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
                            s.settimeout(3)
                            s.connect(("8.8.8.8", 53))
                            ip_address = s.getsockname()[0]
                    except:
                        ip_address = "127.0.0.1"

                    network_info = "Network Connected"
                    
                    logger.info(f"🔍 UI 기반 IP 획득: {ip_address}")
                    logger.info(f"🌐 UI 기반 네트워크 정보: {network_info}")
                    
                    mac_address = ':'.join(['{:02x}'.format((uuid.getnode() >> elements) & 0xff) 
                                          for elements in range(5, -1, -1)])
                    
                    if "WiFi" in network_info:
                        connection_type = "무선"
                        if "(" in network_info and ")" in network_info:
                            ssid = network_info[network_info.find("(")+1:network_info.find(")")]
                        else:
                            ssid = ""
                    elif "유선" in network_info:
                        connection_type = "유선"
                        ssid = ""
                    else:
                        connection_type = "미연결"
                        ssid = ""
                    
                    logger.info(f"🔌 연결 유형: {connection_type}")
                    if ssid:
                        logger.info(f"📡 SSID: {ssid}")
                        
                except Exception as e:
                    logger.error(f"❌ UI 기반 네트워크 정보 수집 실패: {e}")
                    ip_address = "127.0.0.1"
                    mac_address = "00:00:00:00:00:00"
                    connection_type = "미연결"
                    ssid = ""
                
                try:
                    import psutil
                    ram_info = psutil.virtual_memory()
                    disk_info = psutil.disk_usage('/')
                    
                    try:
                        import cpuinfo
                        cpu_info = cpuinfo.get_cpu_info().get('brand_raw', 'Unknown CPU')
                    except:
                        cpu_info = platform.processor() or "Unknown CPU"

                    software_list = []
                    try:
                        import winreg

                        registry_paths = [
                            (winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"),
                            (winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"),
                            (winreg.HKEY_CURRENT_USER, r"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall")
                        ]

                        seen_software = set()

                        for hkey, reg_path in registry_paths:
                            try:
                                key = winreg.OpenKey(hkey, reg_path)
                                for i in range(winreg.QueryInfoKey(key)[0]):
                                    try:
                                        subkey_name = winreg.EnumKey(key, i)
                                        subkey = winreg.OpenKey(key, subkey_name)

                                        try:
                                            display_name = winreg.QueryValueEx(subkey, "DisplayName")[0]
                                            version = ""
                                            publisher = ""
                                            install_date = ""

                                            try:
                                                version = winreg.QueryValueEx(subkey, "DisplayVersion")[0]
                                            except:
                                                pass

                                            try:
                                                publisher = winreg.QueryValueEx(subkey, "Publisher")[0]
                                            except:
                                                pass

                                            # ① InstallDate 레지스트리 값 확인
                                            try:
                                                install_date_raw = winreg.QueryValueEx(subkey, "InstallDate")[0]
                                                if install_date_raw and len(str(install_date_raw)) == 8:
                                                    install_date = f"{install_date_raw[:4]}-{install_date_raw[4:6]}-{install_date_raw[6:8]}"
                                                else:
                                                    install_date = str(install_date_raw) if install_date_raw else ""
                                            except:
                                                pass

                                            # ② InstallLocation 폴더 생성일 확인 (fallback)
                                            if not install_date:
                                                try:
                                                    install_location = winreg.QueryValueEx(subkey, "InstallLocation")[0]
                                                    if install_location and os.path.exists(install_location):
                                                        import datetime
                                                        timestamp = os.path.getctime(install_location)
                                                        install_date = datetime.datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d")
                                                except:
                                                    pass

                                            # ③ UninstallString 실행 파일 생성일 확인 (fallback)
                                            if not install_date:
                                                try:
                                                    uninstall_str = winreg.QueryValueEx(subkey, "UninstallString")[0]
                                                    if uninstall_str:
                                                        exe_path = uninstall_str.strip('"').split(' ')[0]
                                                        if exe_path and os.path.exists(exe_path):
                                                            import datetime
                                                            timestamp = os.path.getctime(exe_path)
                                                            install_date = datetime.datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d")
                                                except:
                                                    pass

                                            if display_name and display_name not in seen_software:
                                                software_list.append({
                                                    "name": display_name,
                                                    "version": version,
                                                    "publisher": publisher,
                                                    "install_date": install_date
                                                })
                                                seen_software.add(display_name)
                                        except:
                                            pass

                                        winreg.CloseKey(subkey)
                                    except:
                                        continue

                                winreg.CloseKey(key)
                            except:
                                continue

                        logger.info(f"Collected {len(software_list)} software items")

                    except Exception as e:
                        logger.warning(f"Software collection failed: {e}")
                        software_list = [{"name": "Software collection failed", "version": ""}]

                    logger.info(f"수집된 소프트웨어 개수: {len(software_list)}")

                    com_ports = []
                    try:
                        import serial.tools.list_ports
                        ports = serial.tools.list_ports.comports()
                        for port in ports:
                            com_ports.append({
                                "port": port.device,
                                "description": port.description,
                                "hwid": port.hwid,
                                "active": True
                            })
                    except ImportError:
                        pass
                    
                except ImportError:
                    ram_info = type('obj', (object,), {
                        'total': 8 * 1024**3, 'used': 4 * 1024**3, 'percent': 50.0
                    })()
                    cpu_info = "Unknown CPU"
                    disk_info = type('obj', (object,), {
                        'total': 256 * 1024**3, 'used': 128 * 1024**3, 'percent': 50.0
                    })()
                    software_list = [{"name": "Python", "version": platform.python_version()}]
                    com_ports = []
                
                network_interfaces = [{
                    "name": "로컬 영역 연결",
                    "ip": ip_address,
                    "mac": mac_address,
                    "connected": True if ip_address != "127.0.0.1" else False
                }]
                
                asset_data = {
                    "hostname": hostname,
                    "ip_address": ip_address,
                    "mac_address": mac_address,
                    "status": "active",
                    "last_seen": datetime.datetime.now().isoformat(),
                    "hardware_info": {
                        "cpu_info": cpu_info,
                        "ram_total": int(ram_info.total),
                        "ram_used": int(ram_info.used),
                        "ram_percent": float(ram_info.percent),
                        "disk_total": int(disk_info.total),
                        "disk_used": int(disk_info.used),
                        "disk_percent": float(disk_info.percent),
                        "gpu_info": "N/A",
                        "motherboard": "N/A"
                    },
                    "network_info": {
                        "interfaces": network_interfaces
                    },
                    "software_info": {
                        "software_list": software_list
                    },
                    "com_ports": {
                        "ports": com_ports
                    },
                    "system_status": {
                        "cpu_percent": 0.0,
                        "ram_percent": float(ram_info.percent),
                        "disk_percent": float(disk_info.percent),
                        "uptime": 0
                    }
                }
                
                response = requests.post(f"{server_url}/api/asset/update", json=asset_data, timeout=10)
                
                if response.status_code == 200:
                    response_data = response.json()
                    asset_id = response_data.get('asset_id', 'Unknown')
                    
                    messagebox.showinfo(
                        "자산 등록 완료", 
                        f"자산이 성공적으로 등록되었습니다!\n\n"
                        f"호스트명: {hostname}\n"
                        f"IP 주소: {ip_address}\n" 
                        f"자산 ID: {asset_id}\n\n"
                        f"대시보드에서 확인하세요:\n"
                        f"http://192.168.0.240:8800/dashboard"
                    )
                    
                    try:
                        webbrowser.open("http://192.168.0.240:8800/dashboard")
                    except:
                        pass
                        
                else:
                    messagebox.showerror(
                        "자산 등록 실패", 
                        f"자산 등록에 실패했습니다.\n"
                        f"상태 코드: {response.status_code}\n"
                        f"응답: {response.text}"
                    )
                    
            except ImportError as e:
                messagebox.showerror(
                    "라이브러리 오류",
                    f"필요한 라이브러리가 없습니다: {str(e)}\n"
                    f"pip install requests 명령으로 설치해주세요."
                )
            except Exception as e:
                messagebox.showerror(
                    "오류 발생", 
                    f"자산 등록 중 오류가 발생했습니다:\n{str(e)}\n\n"
                    f"서버가 실행 중인지 확인해주세요.\n"
                    f"서버 실행: python server.py"
                )
        
        def background_monitoring():
            import random
            import time
            from datetime import datetime

            logger.info("Background monitoring thread started")

            try:
                logger.debug("Sending initial asset data...")
                register_asset_silent()
                logger.info("Initial asset data sent successfully")
            except Exception as e:
                logger.error(f"Initial asset data send failed: {e}")

            seconds_counter = 0

            while True:
                try:
                    if not is_app_exe_running():
                        logger.info("App.exe not running, stopping background monitoring")
                        break

                    if '_app_stop_event' in globals() and globals()['_app_stop_event'].is_set():
                        logger.info("Received stop signal from app.exe, stopping background monitoring")
                        break

                    if seconds_counter % 3 == 0:
                        check_module_execution_requests()

                    if seconds_counter % 10 == 0:
                        send_heartbeat()

                    if seconds_counter % 30 == 0 and seconds_counter > 0:
                        logger.debug("Sending periodic asset data...")
                        register_asset_silent()

                    time.sleep(1)
                    seconds_counter += 1

                    if seconds_counter >= 30:
                        seconds_counter = 0

                except Exception as e:
                    logger.error(f"Background monitoring error: {e}")
                    time.sleep(10)
        
        def is_app_exe_running():
            try:
                import psutil
                import os

                if os.environ.get('LAUNCHED_BY_APP') == '1':
                    for proc in psutil.process_iter(['name', 'cmdline']):
                        try:
                            if proc.info['name']:
                                name_lower = proc.info['name'].lower()
                                if name_lower == 'app.exe':
                                    return True
                                if name_lower == 'python.exe' or name_lower == 'python':
                                    cmdline = proc.info.get('cmdline', [])
                                    if cmdline and any('app.py' in str(arg) for arg in cmdline):
                                        return True
                        except (psutil.NoSuchProcess, psutil.AccessDenied):
                            continue
                    return False
                else:
                    return True

            except ImportError:
                logger.warning("psutil not available, assuming app.exe is running")
                return True
            except Exception as e:
                logger.error(f"Error checking app.exe process: {e}")
                return True
        
        def send_heartbeat():
            try:
                import requests
                import datetime
                import uuid

                server_url = "http://192.168.0.240:8800"
                hostname = platform.node()

                try:
                    with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
                        s.settimeout(3)
                        s.connect(("8.8.8.8", 53))
                        ip_address = s.getsockname()[0]
                except:
                    ip_address = "127.0.0.1"

                mac_address = ':'.join(['{:02x}'.format((uuid.getnode() >> elements) & 0xff)
                                      for elements in range(5, -1, -1)])

                app_version = globals().get('APP_VERSION')
                if not app_version:
                    app_version = os.environ.get('APP_VERSION')
                if not app_version:
                    app_version = "Unknown"

                heartbeat_data = {
                    "hostname": hostname,
                    "ip_address": ip_address,
                    "mac_address": mac_address,
                    "status": "active",
                    "timestamp": datetime.datetime.now().isoformat(),
                    "type": "heartbeat",
                    "app_version": app_version
                }

                try:
                    response = requests.post(f"{server_url}/api/asset/heartbeat", json=heartbeat_data, timeout=3)
                    if response.status_code == 200:
                        logger.debug(f"💓 Heartbeat sent successfully")
                    else:
                        response = requests.post(f"{server_url}/api/asset/update", json=heartbeat_data, timeout=3)
                        if response.status_code == 200:
                            logger.debug(f"💓 Heartbeat sent via fallback endpoint")
                        else:
                            logger.warning(f"Heartbeat failed: {response.status_code}")
                except Exception as e:
                    logger.warning(f"Heartbeat error: {e}")

            except Exception as e:
                logger.debug(f"Heartbeat error (ignored): {e}")

        def check_module_execution_requests():
            try:
                import requests
                import subprocess
                import os

                server_url = "http://192.168.0.240:8800"
                hostname = platform.node()

                def send_module_log(level, log_type, message):
                    try:
                        requests.post(
                            f"{server_url}/api/agent/log",
                            json={
                                "hostname": hostname,
                                "level": level,
                                "type": log_type,
                                "message": message
                            },
                            timeout=3
                        )
                    except:
                        pass

                response = requests.get(f"{server_url}/api/software/management-requests/{hostname}", timeout=5)

                if response.status_code != 200:
                    logger.warning(f"[MODULE] Server returned status {response.status_code}")
                    return

                data = response.json()
                requests_list = data.get('requests', [])

                if len(requests_list) > 0:
                    logger.info(f"[MODULE] Received {len(requests_list)} execution requests")
                else:
                    logger.debug(f"[MODULE] No pending requests")

                for req in requests_list:
                    req_id = req.get('id')
                    action_type = req.get('action_type')
                    module_name = req.get('software_name')

                    if action_type == 'MODULE_EXECUTE':
                        logger.info(f"[MODULE] Received execution request: {module_name} (ID: {req_id})")
                        send_module_log("INFO", "MODULE_EXECUTE", f"모듈 실행 시작: {module_name} (요청 ID: {req_id})")

                        module_path = os.path.join("modules", module_name)

                        if not os.path.exists(module_path):
                            error_msg = f"모듈 파일을 찾을 수 없음: {module_path}"
                            logger.error(f"[MODULE] {error_msg}")
                            send_module_log("ERROR", "MODULE_EXECUTE", f"❌ 모듈 실행 실패: {module_name} - {error_msg}")

                            try:
                                requests.post(
                                    f"{server_url}/api/software/management-requests/{req_id}/complete",
                                    json={"result": "failed", "message": error_msg},
                                    timeout=5
                                )
                                logger.info(f"[MODULE] Module not found error reported to server: {module_name}")
                            except Exception as report_error:
                                logger.error(f"[MODULE] Failed to report error to server: {report_error}")

                            continue

                        try:
                            if module_name.lower().endswith('.cmd'):
                                abs_path = os.path.abspath(module_path)
                                work_dir = os.path.dirname(abs_path)

                                logger.info(f"[MODULE] Executing CMD: {module_name}")
                                logger.info(f"[MODULE] Absolute path: {abs_path}")
                                logger.info(f"[MODULE] Working directory: {work_dir}")

                                if not os.path.exists(abs_path):
                                    raise FileNotFoundError(f"CMD file not found: {abs_path}")

                                file_size = os.path.getsize(abs_path)
                                logger.info(f"[MODULE] CMD file size: {file_size} bytes")

                                process = None
                                try:
                                    process = subprocess.Popen(
                                        ['cmd', '/c', 'start', '/wait', 'cmd', '/c', abs_path],
                                        cwd=work_dir,
                                        creationflags=subprocess.CREATE_NEW_CONSOLE | subprocess.CREATE_NEW_PROCESS_GROUP,
                                        shell=False
                                    )
                                    logger.info(f"[MODULE] Method 1 succeeded - CMD process started with PID: {process.pid}")
                                except Exception as e1:
                                    logger.warning(f"[MODULE] Method 1 failed: {e1}, trying method 2")
                                    try:
                                        process = subprocess.Popen(
                                            ['cmd', '/k', abs_path],
                                            cwd=work_dir,
                                            creationflags=subprocess.CREATE_NEW_CONSOLE,
                                            shell=False
                                        )
                                        logger.info(f"[MODULE] Method 2 succeeded - CMD process started with PID: {process.pid}")
                                    except Exception as e2:
                                        logger.warning(f"[MODULE] Method 2 failed: {e2}, trying method 3")
                                        os.system(f'start cmd /k "{abs_path}"')
                                        logger.info(f"[MODULE] Method 3 (os.system) executed")

                                logger.info(f"[MODULE] CMD execution started successfully: {module_name}")

                            elif module_name.lower().endswith('.py'):
                                abs_path = os.path.abspath(module_path)
                                work_dir = os.path.dirname(abs_path)

                                logger.info(f"[MODULE] Executing Python: {module_name}")
                                logger.info(f"[MODULE] Absolute path: {abs_path}")

                                if not os.path.exists(abs_path):
                                    raise FileNotFoundError(f"Python file not found: {abs_path}")

                                process = subprocess.Popen(
                                    [sys.executable, abs_path],
                                    cwd=work_dir,
                                    shell=False
                                )

                                logger.info(f"[MODULE] Python process started with PID: {process.pid}")

                            elif module_name.lower().endswith('.exe'):
                                abs_path = os.path.abspath(module_path)
                                work_dir = os.path.dirname(abs_path)

                                logger.info(f"[MODULE] Executing EXE: {module_name}")
                                logger.info(f"[MODULE] Absolute path: {abs_path}")

                                if not os.path.exists(abs_path):
                                    raise FileNotFoundError(f"EXE file not found: {abs_path}")

                                file_size = os.path.getsize(abs_path)
                                logger.info(f"[MODULE] EXE file size: {file_size} bytes")

                                process = subprocess.Popen(
                                    [abs_path],
                                    cwd=work_dir,
                                    shell=False
                                )

                                logger.info(f"[MODULE] EXE process started with PID: {process.pid}")

                            else:
                                logger.warning(f"[MODULE] Unsupported file type: {module_name}")
                                raise ValueError(f"Unsupported file type: {module_name}")

                            requests.post(
                                f"{server_url}/api/software/management-requests/{req_id}/complete",
                                json={"result": "success", "message": f"Module {module_name} executed successfully"},
                                timeout=5
                            )

                            logger.info(f"[MODULE] Execution completed and reported to server: {module_name}")
                            send_module_log("SUCCESS", "MODULE_EXECUTE", f"✅ 모듈 실행 성공: {module_name}")

                        except Exception as exec_error:
                            import traceback
                            error_details = traceback.format_exc()
                            logger.error(f"[MODULE] Execution failed for {module_name}: {exec_error}")
                            logger.error(f"[MODULE] Traceback: {error_details}")

                            error_msg = f"❌ 모듈 실행 실패: {module_name} - {str(exec_error)}"
                            send_module_log("ERROR", "MODULE_EXECUTE", error_msg)

                            try:
                                requests.post(
                                    f"{server_url}/api/software/management-requests/{req_id}/complete",
                                    json={"result": "failed", "message": f"Execution failed: {str(exec_error)}"},
                                    timeout=5
                                )
                                logger.info(f"[MODULE] Failure reported to server: {module_name}")
                            except Exception as report_error:
                                logger.error(f"[MODULE] Failed to report error to server: {report_error}")

            except Exception as e:
                import traceback
                logger.error(f"[MODULE] Check module execution error: {e}")
                logger.error(f"[MODULE] Traceback: {traceback.format_exc()}")

        def register_asset_silent():
            from datetime import datetime as dt

            def write_debug_log(message):
                try:
                    timestamp = dt.now().isoformat()
                    with open("debug.txt", "a", encoding="utf-8") as f:
                        f.write(f"[{timestamp}] [REGISTER_ASSET] {message}\n")
                except:
                    pass

            write_debug_log("Checking if app.exe is running...")
            if not is_app_exe_running():
                logger.info("App.exe not running, skipping asset update")
                write_debug_log("App.exe not running, skipping")
                return

            write_debug_log("App.exe is running, proceeding with registration")

            try:
                import requests
                import json
                import datetime
                import uuid
                
                server_url = "http://192.168.0.240:8800"
                
                hostname = platform.node()
                
                try:
                    try:
                        with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
                            s.settimeout(3)
                            s.connect(("8.8.8.8", 53))
                            ip_address = s.getsockname()[0]
                    except:
                        ip_address = "127.0.0.1"

                    network_info = "Network Connected"
                    
                    logger.info(f"🔍 UI 기반 IP 획득: {ip_address}")
                    logger.info(f"🌐 UI 기반 네트워크 정보: {network_info}")
                    
                    mac_address = ':'.join(['{:02x}'.format((uuid.getnode() >> elements) & 0xff) 
                                          for elements in range(5, -1, -1)])
                    
                    if "WiFi" in network_info:
                        connection_type = "무선"
                        if "(" in network_info and ")" in network_info:
                            ssid = network_info[network_info.find("(")+1:network_info.find(")")]
                        else:
                            ssid = ""
                    elif "유선" in network_info:
                        connection_type = "유선"
                        ssid = ""
                    else:
                        connection_type = "미연결"
                        ssid = ""
                    
                    logger.info(f"🔌 연결 유형: {connection_type}")
                    if ssid:
                        logger.info(f"📡 SSID: {ssid}")
                        
                except Exception as e:
                    logger.error(f"❌ UI 기반 네트워크 정보 수집 실패: {e}")
                    ip_address = "127.0.0.1"
                    mac_address = "00:00:00:00:00:00"
                    connection_type = "미연결"
                    ssid = ""
                
                try:
                    import psutil
                    ram_info = psutil.virtual_memory()
                    disk_info = psutil.disk_usage('/')
                    
                    try:
                        import cpuinfo
                        cpu_info = cpuinfo.get_cpu_info().get('brand_raw', 'Unknown CPU')
                    except:
                        cpu_info = platform.processor() or "Unknown CPU"

                    software_list = []
                    try:
                        import winreg

                        registry_paths = [
                            (winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"),
                            (winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"),
                            (winreg.HKEY_CURRENT_USER, r"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall")
                        ]

                        seen_software = set()

                        for hkey, reg_path in registry_paths:
                            try:
                                key = winreg.OpenKey(hkey, reg_path)
                                for i in range(winreg.QueryInfoKey(key)[0]):
                                    try:
                                        subkey_name = winreg.EnumKey(key, i)
                                        subkey = winreg.OpenKey(key, subkey_name)

                                        try:
                                            display_name = winreg.QueryValueEx(subkey, "DisplayName")[0]
                                            version = ""
                                            publisher = ""
                                            install_date = ""

                                            try:
                                                version = winreg.QueryValueEx(subkey, "DisplayVersion")[0]
                                            except:
                                                pass

                                            try:
                                                publisher = winreg.QueryValueEx(subkey, "Publisher")[0]
                                            except:
                                                pass

                                            # ① InstallDate 레지스트리 값 확인
                                            try:
                                                install_date_raw = winreg.QueryValueEx(subkey, "InstallDate")[0]
                                                if install_date_raw and len(str(install_date_raw)) == 8:
                                                    install_date = f"{install_date_raw[:4]}-{install_date_raw[4:6]}-{install_date_raw[6:8]}"
                                                else:
                                                    install_date = str(install_date_raw) if install_date_raw else ""
                                            except:
                                                pass

                                            # ② InstallLocation 폴더 생성일 확인 (fallback)
                                            if not install_date:
                                                try:
                                                    install_location = winreg.QueryValueEx(subkey, "InstallLocation")[0]
                                                    if install_location and os.path.exists(install_location):
                                                        import datetime
                                                        timestamp = os.path.getctime(install_location)
                                                        install_date = datetime.datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d")
                                                except:
                                                    pass

                                            # ③ UninstallString 실행 파일 생성일 확인 (fallback)
                                            if not install_date:
                                                try:
                                                    uninstall_str = winreg.QueryValueEx(subkey, "UninstallString")[0]
                                                    if uninstall_str:
                                                        exe_path = uninstall_str.strip('"').split(' ')[0]
                                                        if exe_path and os.path.exists(exe_path):
                                                            import datetime
                                                            timestamp = os.path.getctime(exe_path)
                                                            install_date = datetime.datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d")
                                                except:
                                                    pass

                                            if display_name and display_name not in seen_software:
                                                software_list.append({
                                                    "name": display_name,
                                                    "version": version,
                                                    "publisher": publisher,
                                                    "install_date": install_date
                                                })
                                                seen_software.add(display_name)
                                        except:
                                            pass

                                        winreg.CloseKey(subkey)
                                    except:
                                        continue

                                winreg.CloseKey(key)
                            except:
                                continue

                        logger.info(f"Collected {len(software_list)} software items")

                    except Exception as e:
                        logger.warning(f"Software collection failed: {e}")
                        software_list = [{"name": "Software collection failed", "version": ""}]

                    logger.info(f"수집된 소프트웨어 개수: {len(software_list)}")

                    com_ports = []
                    try:
                        import serial.tools.list_ports
                        ports = serial.tools.list_ports.comports()
                        for port in ports:
                            com_ports.append({
                                "port": port.device,
                                "description": port.description,
                                "hwid": port.hwid,
                                "active": True
                            })
                    except ImportError:
                        logger.warning("Serial library not available for COM port detection")
                    except Exception as e:
                        logger.error(f"COM port detection error: {e}")

                    app_version = globals().get('APP_VERSION')
                    if not app_version:
                        app_version = os.environ.get('APP_VERSION')
                    if not app_version:
                        try:
                            app_py_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'app.py')
                            if os.path.exists(app_py_path):
                                with open(app_py_path, 'r', encoding='utf-8') as f:
                                    for line in f:
                                        if line.startswith('APP_VERSION'):
                                            app_version = line.split('=')[1].strip().strip('"').strip("'")
                                            break
                        except Exception as e:
                            logger.warning(f"Failed to read app version: {e}")
                    if not app_version:
                        app_version = "Unknown"
                    
                    network_interfaces = [{
                        "name": network_info,
                        "ip": ip_address,
                        "mac": mac_address,
                        "connected": True if connection_type != "미연결" else False
                    }]
                    
                    logger.info(f"📡 인터페이스 정보: {network_info} -> {connection_type}")
                    
                    asset_data = {
                        "hostname": hostname,
                        "ip_address": ip_address,
                        "mac_address": mac_address,
                        "status": "active",
                        "last_seen": datetime.datetime.now().isoformat(),
                        "app_version": app_version,
                        "hardware_info": {
                            "cpu_info": cpu_info,
                            "ram_total": int(ram_info.total),
                            "ram_used": int(ram_info.used),
                            "ram_percent": float(ram_info.percent),
                            "disk_total": int(disk_info.total),
                            "disk_used": int(disk_info.used),
                            "disk_percent": float(disk_info.percent),
                            "gpu_info": "N/A",
                            "motherboard": "N/A"
                        },
                        "network_info": {
                            "interfaces": network_interfaces
                        },
                        "software_info": {
                            "software_list": software_list
                        },
                        "com_ports": {
                            "ports": com_ports
                        },
                        "system_status": {
                            "cpu_percent": 0.0,
                            "ram_percent": float(ram_info.percent),
                            "disk_percent": float(disk_info.percent),
                            "uptime": 0
                        }
                    }

                    write_debug_log(f"Asset data prepared:")
                    write_debug_log(f"  Hostname: {hostname}")
                    write_debug_log(f"  IP: {ip_address}")
                    write_debug_log(f"  MAC: {mac_address}")
                    write_debug_log(f"  CPU: {cpu_info}")
                    write_debug_log(f"  RAM: {ram_info.total / (1024**3):.2f} GB ({ram_info.percent}%)")
                    write_debug_log(f"  Disk: {disk_info.total / (1024**3):.2f} GB ({disk_info.percent}%)")
                    write_debug_log(f"  Software count: {len(software_list)}")
                    if len(software_list) > 0:
                        write_debug_log(f"  Sample software: {software_list[0].get('name', 'N/A')[:50]}...")
                    write_debug_log(f"  COM ports: {len(com_ports)}")
                    write_debug_log(f"  App version: {app_version}")
                    
                    def check_server_connectivity():
                        try:
                            import socket
                            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                            sock.settimeout(3)
                            result = sock.connect_ex(('192.168.0.240', 8800))
                            sock.close()
                            return result == 0
                        except Exception as e:
                            logger.debug(f"Server connectivity check failed: {e}")
                            return False
                    
                    if not check_server_connectivity():
                        logger.info("Server not available, skipping asset data transmission")
                        return
                    
                    max_retries = 3
                    retry_count = 0
                    
                    while retry_count < max_retries:
                        try:
                            logger.debug(f"Attempting to send asset data (attempt {retry_count + 1}/{max_retries})")
                            write_debug_log(f"Sending POST request to {server_url}/api/asset/update (attempt {retry_count + 1})")

                            response = requests.post(f"{server_url}/api/asset/update", json=asset_data, timeout=10)

                            write_debug_log(f"Response status: {response.status_code}")
                            write_debug_log(f"Response body: {response.text[:200]}")

                            if response.status_code == 200:
                                response_data = response.json()
                                asset_id = response_data.get('asset_id', 'Unknown')
                                logger.info(f"Asset data sent successfully - Asset ID: {asset_id}")
                                write_debug_log(f"✅ SUCCESS! Asset ID: {asset_id}")
                                return
                            else:
                                logger.warning(f"Asset data send failed - Status: {response.status_code} (attempt {retry_count + 1})")
                                write_debug_log(f"❌ Failed with status {response.status_code}")
                                
                        except requests.exceptions.RequestException as e:
                            logger.warning(f"Request failed: {e} (attempt {retry_count + 1})")
                        except Exception as e:
                            logger.warning(f"Unexpected error during transmission: {e} (attempt {retry_count + 1})")
                        
                        retry_count += 1
                        
                        if retry_count < max_retries:
                            import time
                            time.sleep(2)
                    
                    logger.error(f"Failed to send asset data after {max_retries} attempts, skipping until next interval")
                        
                except Exception as e:
                    logger.error(f"Silent asset registration error: {e}")
                    
            except ImportError as e:
                logger.error(f"Required library missing: {e}")
            except Exception as e:
                logger.error(f"Asset registration error: {e}")
        
        threading.Thread(target=background_monitoring, daemon=True).start()
        logger.info("Background asset monitoring started")
    
    def get_cpu_info(self):
        try:
            import subprocess
            result = subprocess.run([
                'wmic', 'cpu', 'get', 'name', '/format:value'
            ], capture_output=True, text=True,
            creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0)
            
            if result.returncode == 0:
                for line in result.stdout.split('\n'):
                    if line.startswith('Name='):
                        cpu_name = line.replace('Name=', '').strip()
                        if cpu_name:
                            return cpu_name
            
            try:
                import winreg
                key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, 
                                   r"HARDWARE\DESCRIPTION\System\CentralProcessor\0")
                cpu_name, _ = winreg.QueryValueEx(key, "ProcessorNameString")
                winreg.CloseKey(key)
                return cpu_name.strip()
            except:
                pass
                
            try:
                result = subprocess.run([
                    'powershell', '-Command',
                    'Get-WmiObject -Class Win32_Processor | Select-Object -ExpandProperty Name'
                ], capture_output=True, text=True,
                         creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0)
                
                if result.returncode == 0:
                    cpu_name = result.stdout.strip()
                    if cpu_name:
                        return cpu_name
            except:
                pass
                
        except Exception as e:
            logger.warning(f"CPU 정보 수집 실패: {e}")
        
        return platform.processor() or "Unknown CPU"
    
    def get_installed_software(self):
        software_list = []
        
        try:
            import winreg
            
            reg_paths = [
                r"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
                r"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
            ]
            
            for reg_path in reg_paths:
                try:
                    registry_key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, reg_path)
                    
                    for i in range(winreg.QueryInfoKey(registry_key)[0]):
                        try:
                            subkey_name = winreg.EnumKey(registry_key, i)
                            subkey = winreg.OpenKey(registry_key, subkey_name)
                            
                            try:
                                display_name, _ = winreg.QueryValueEx(subkey, "DisplayName")
                                
                                try:
                                    system_component, _ = winreg.QueryValueEx(subkey, "SystemComponent")
                                    if system_component == 1:
                                        winreg.CloseKey(subkey)
                                        continue
                                except FileNotFoundError:
                                    pass
                                
                                try:
                                    windows_installer, _ = winreg.QueryValueEx(subkey, "WindowsInstaller")
                                    if windows_installer == 1:
                                        try:
                                            no_remove, _ = winreg.QueryValueEx(subkey, "NoRemove")
                                            if no_remove == 1:
                                                winreg.CloseKey(subkey)
                                                continue
                                        except FileNotFoundError:
                                            pass
                                except FileNotFoundError:
                                    pass
                                
                                try:
                                    parent_key, _ = winreg.QueryValueEx(subkey, "ParentKeyName")
                                    if parent_key:
                                        winreg.CloseKey(subkey)
                                        continue
                                except FileNotFoundError:
                                    pass
                                
                                try:
                                    release_type, _ = winreg.QueryValueEx(subkey, "ReleaseType")
                                    if release_type and release_type.lower() in ['update', 'hotfix', 'security update']:
                                        winreg.CloseKey(subkey)
                                        continue
                                except FileNotFoundError:
                                    pass
                                
                                display_lower = display_name.lower()
                                if any(skip in display_lower for skip in [
                                    'security update', 'hotfix', 'update for', 'kb', 'service pack',
                                    '보안 업데이트', '핫픽스', '서비스 팩',
                                    
                                    'microsoft visual c++ 2', 'microsoft .net framework',
                                    'redistributable', 'runtime', 'directx',
                                    'microsoft visual c++ runtime',
                                    
                                    'windows software development kit', 'sdk',
                                    'microsoft sql server compact', 'sql server',
                                    'windows mobile', 'microsoft application error reporting',
                                    
                                    'microsoft office shared', 'office shared',
                                    'microsoft report viewer', 'report viewer',
                                    'microsoft office click-to-run',
                                    
                                    'windows driver package', 'device driver',
                                    'microsoft silverlight', 'windows live',
                                    'microsoft games for windows',
                                    
                                    'language pack', 'mui', 'lcid',
                                    'windows installer', 'msi development tools',
                                    
                                    '.net framework', 'dotnet',
                                    
                                    'microsoft visual studio', 'vs_', 'buildtools',
                                    
                                    'ms-resource:', 'packagefamilyname'
                                ]):
                                    winreg.CloseKey(subkey)
                                    continue
                                
                                if (display_lower.startswith('microsoft') and 
                                    any(sys_comp in display_lower for sys_comp in [
                                        'help viewer', 'sync framework', 'expression',
                                        'compact framework', 'enterprise library',
                                        'patterns & practices', 'chart controls',
                                        'xml core services', 'web deploy'
                                    ])):
                                    winreg.CloseKey(subkey)
                                    continue
                                
                                if not display_name.strip():
                                    winreg.CloseKey(subkey)
                                    continue
                                
                                if display_name.startswith('{') and display_name.endswith('}') and len(display_name) == 38:
                                    winreg.CloseKey(subkey)
                                    continue
                                
                                try:
                                    version, _ = winreg.QueryValueEx(subkey, "DisplayVersion")
                                except FileNotFoundError:
                                    version = "Unknown"
                                
                                try:
                                    publisher, _ = winreg.QueryValueEx(subkey, "Publisher")
                                except FileNotFoundError:
                                    publisher = "Unknown"
                                
                                install_date = None
                                
                                try:
                                    install_date_raw, _ = winreg.QueryValueEx(subkey, "InstallDate")
                                    if install_date_raw:
                                        if len(str(install_date_raw)) == 8:
                                            install_date = f"{install_date_raw[:4]}-{install_date_raw[4:6]}-{install_date_raw[6:8]}"
                                        else:
                                            install_date = str(install_date_raw)
                                except (FileNotFoundError, WindowsError):
                                    pass
                                
                                if not install_date or install_date == "Unknown":
                                    try:
                                        install_location, _ = winreg.QueryValueEx(subkey, "InstallLocation")
                                        if install_location and os.path.exists(install_location):
                                            created_time = os.path.getctime(install_location)
                                            install_date = datetime.datetime.fromtimestamp(created_time).strftime('%Y-%m-%d')
                                    except (FileNotFoundError, WindowsError, OSError):
                                        pass
                                
                                if not install_date or install_date == "Unknown":
                                    try:
                                        key_info = winreg.QueryInfoKey(subkey)
                                        if key_info and len(key_info) > 2:
                                            filetime = key_info[2]
                                            if filetime:
                                                timestamp = (filetime / 10000000.0) - 11644473600
                                                if timestamp > 0:
                                                    install_date = datetime.datetime.fromtimestamp(timestamp).strftime('%Y-%m-%d')
                                    except Exception:
                                        pass
                                
                                if not install_date or install_date == "Unknown":
                                    try:
                                        uninstall_string, _ = winreg.QueryValueEx(subkey, "UninstallString")
                                        if uninstall_string:
                                            exe_path = uninstall_string.strip('"').split('"')[0]
                                            if not os.path.exists(exe_path):
                                                import shlex
                                                parts = shlex.split(uninstall_string)
                                                if parts and os.path.exists(parts[0]):
                                                    exe_path = parts[0]
                                            
                                            if os.path.exists(exe_path):
                                                created_time = os.path.getctime(exe_path)
                                                install_date = datetime.datetime.fromtimestamp(created_time).strftime('%Y-%m-%d')
                                    except (FileNotFoundError, WindowsError, OSError):
                                        pass
                                
                                if not install_date:
                                    install_date = "Unknown"
                                
                                software_list.append({
                                    "name": display_name,
                                    "version": version,
                                    "publisher": publisher,
                                    "install_date": install_date
                                })
                                
                            except FileNotFoundError:
                                pass
                            
                            winreg.CloseKey(subkey)
                            
                        except Exception:
                            continue
                    
                    winreg.CloseKey(registry_key)
                    
                except Exception as e:
                    logger.warning(f"레지스트리 경로 {reg_path} 접근 실패: {e}")
                    continue
                    
        except ImportError:
            logger.warning("winreg 모듈을 사용할 수 없습니다.")
        except Exception as e:
            logger.warning(f"소프트웨어 목록 수집 실패: {e}")
        
        
        unique_software = []
        seen_names = set()
        
        for software in software_list:
            if software['name'] not in seen_names:
                unique_software.append(software)
                seen_names.add(software['name'])
        
        unique_software.sort(key=lambda x: x['name'].lower())
        
        logger.info(f"수집된 소프트웨어 개수: {len(unique_software)}")
        return unique_software

    def adjust_status_opacity(self):
        logger.info("Opening status opacity adjustment...")
        self.close_menu()

        import tkinter as tk
        from tkinter import ttk

        opacity_window = tk.Toplevel()
        opacity_window.title("네트워크 상태 UI 투명도 조절")
        opacity_window.geometry("400x200")
        opacity_window.resizable(False, False)

        opacity_window.attributes('-topmost', True)

        main_frame = ttk.Frame(opacity_window, padding="20")
        main_frame.pack(fill=tk.BOTH, expand=True)

        title_label = ttk.Label(main_frame, text="네트워크 상태 UI 투명도 조절", font=('Arial', 12, 'bold'))
        title_label.pack(pady=(0, 20))

        current_opacity = load_data('status_opacity', 0.85)

        opacity_frame = ttk.Frame(main_frame)
        opacity_frame.pack(fill=tk.X, pady=(0, 20))

        opacity_label = ttk.Label(opacity_frame, text="투명도:")
        opacity_label.pack(side=tk.LEFT)

        opacity_var = tk.DoubleVar(value=current_opacity)
        opacity_scale = ttk.Scale(
            opacity_frame,
            from_=0.1,
            to=1.0,
            orient=tk.HORIZONTAL,
            length=200,
            variable=opacity_var
        )
        opacity_scale.pack(side=tk.LEFT, padx=(10, 10))

        opacity_value_label = ttk.Label(opacity_frame, text=f"{current_opacity:.2f}")
        opacity_value_label.pack(side=tk.LEFT)

        def update_opacity_display(value):
            opacity_value = float(value)
            opacity_value_label.config(text=f"{opacity_value:.2f}")

            if hasattr(self, 'status_window') and self.status_window:
                status_win = self.status_window

                if hasattr(status_win, 'qt_widget') and status_win.qt_widget:
                    try:
                        status_win.qt_widget.setWindowOpacity(opacity_value)
                    except Exception as e:
                        logger.error(f"실시간 PyQt5 투명도 적용 실패: {e}")

                if hasattr(status_win, 'status_window') and status_win.status_window:
                    try:
                        if hasattr(status_win.status_window, 'winfo_exists') and status_win.status_window.winfo_exists():
                            status_win.status_window.attributes('-alpha', opacity_value)
                    except Exception as e:
                        logger.error(f"실시간 tkinter 투명도 적용 실패: {e}")

        def apply_opacity():
            opacity = opacity_var.get()
            save_data('status_opacity', opacity)

            tk.messagebox.showinfo("투명도 저장", f"네트워크 상태 UI 투명도 {opacity:.2f}가 저장되었습니다.\n다음에 프로그램을 시작할 때도 이 설정이 적용됩니다.")

        opacity_scale.config(command=update_opacity_display)

        button_frame = ttk.Frame(main_frame)
        button_frame.pack(fill=tk.X)

        apply_btn = ttk.Button(button_frame, text="저장", command=apply_opacity)
        apply_btn.pack(side=tk.LEFT, padx=(0, 10))

        close_btn = ttk.Button(button_frame, text="닫기", command=opacity_window.destroy)
        close_btn.pack(side=tk.LEFT)

    def open_settings(self):
        logger.info("Opening settings...")
        self.close_menu()
        
        if hasattr(self, 'status_window') and self.status_window:
            import time
            self.status_window._settings_button_clicked_time = time.time()
            logger.info("Settings button clicked - status clicks will be ignored for 1 second")
        
        try:
            if self.settings_window.settings_window:
                logger.info("Existing settings window found, closing...")
                self.settings_window.close_settings()
                
            self.settings_window.show_settings()
            
        except Exception as e:
            logger.error(f"Error opening settings: {e}")
            try:
                if self.status_window:
                    self.status_window.set_edit_mode(False)
            except:
                pass
    
    def perform_system_check(self):
        logger.info("Opening system check submenu...")
        mouse_x = self.mouse_x
        mouse_y = self.mouse_y
        self.close_menu()
        
        self.create_submenu(mouse_x, mouse_y)
    
    def create_website_submenu(self, x, y):
        logger.info(f"create_website_submenu called at position ({x}, {y})")
        
        if self.overlay:
            logger.info("Website submenu already open, closing...")
            self.close_menu()
            return
        
        self.mouse_x = x
        self.mouse_y = y
        
        self.overlay = tk.Toplevel()
        self.overlay.attributes('-topmost', True)
        self.overlay.overrideredirect(True)
        
        window_size = 350
        
        virtual_x = self.overlay.winfo_vrootx()
        virtual_y = self.overlay.winfo_vrooty()
        virtual_width = self.overlay.winfo_vrootwidth()
        virtual_height = self.overlay.winfo_vrootheight()
        
        window_x = x - window_size // 2
        window_y = y - window_size // 2
        
        min_x = virtual_x
        min_y = virtual_y
        max_x = virtual_x + virtual_width - window_size
        max_y = virtual_y + virtual_height - window_size
        
        if window_x < min_x:
            window_x = min_x
        elif window_x > max_x:
            window_x = max_x
        
        if window_y < min_y:
            window_y = min_y
        elif window_y > max_y:
            window_y = max_y
        
        self.overlay.geometry(f"{window_size}x{window_size}+{window_x}+{window_y}")
        
        self.overlay.attributes('-alpha', 0.9)
        self.overlay.configure(bg='#191919')
        self.overlay.wm_attributes('-transparentcolor', '#191919')
        
        self.background = tk.Toplevel()
        self.background.attributes('-topmost', True)
        self.background.attributes('-alpha', 0.01)
        self.background.overrideredirect(True)
        
        self.background.geometry(f"{virtual_width}x{virtual_height}+{virtual_x}+{virtual_y}")
        
        self.background.bind('<Button-1>', lambda e: self.close_menu())
        
        self.canvas = tk.Canvas(
            self.overlay,
            width=window_size,
            height=window_size,
            bg='#191919',
            highlightthickness=0
        )
        self.canvas.pack()
        
        self.local_x = window_size // 2
        self.local_y = window_size // 2
        
        self.create_modern_circular_background_website()
        
        self.create_website_buttons()
        
        self.canvas.bind('<Button-1>', self.on_click)
        self.overlay.bind('<Escape>', lambda e: self.close_menu())
        
        self.overlay.focus_set()
        self.overlay.focus_force()
        
        self.overlay.update_idletasks()
        
        logger.info("Website submenu created successfully")

    def create_program_submenu(self, x, y):
        logger.info(f"create_program_submenu called at position ({x}, {y})")

        if self.overlay:
            logger.info("Program submenu already open, closing...")
            self.close_menu()
            return

        self.mouse_x = x
        self.mouse_y = y

        self.overlay = tk.Toplevel()
        self.overlay.attributes('-topmost', True)
        self.overlay.overrideredirect(True)

        window_size = 350

        virtual_x = self.overlay.winfo_vrootx()
        virtual_y = self.overlay.winfo_vrooty()
        virtual_width = self.overlay.winfo_vrootwidth()
        virtual_height = self.overlay.winfo_vrootheight()

        window_x = x - window_size // 2
        window_y = y - window_size // 2

        if window_x < virtual_x:
            window_x = virtual_x
        elif window_x + window_size > virtual_x + virtual_width:
            window_x = virtual_x + virtual_width - window_size

        if window_y < virtual_y:
            window_y = virtual_y
        elif window_y + window_size > virtual_y + virtual_height:
            window_y = virtual_y + virtual_height - window_size

        self.overlay.geometry(f"{window_size}x{window_size}+{window_x}+{window_y}")

        self.overlay.attributes('-alpha', 0.9)
        self.overlay.configure(bg='#191919')
        self.overlay.wm_attributes('-transparentcolor', '#191919')

        self.background = tk.Toplevel()
        self.background.attributes('-topmost', True)
        self.background.attributes('-alpha', 0.01)
        self.background.overrideredirect(True)

        self.background.geometry(f"{virtual_width}x{virtual_height}+{virtual_x}+{virtual_y}")

        self.background.bind('<Button-1>', lambda e: self.close_menu())

        self.canvas = tk.Canvas(
            self.overlay,
            width=window_size,
            height=window_size,
            highlightthickness=0,
            bg='#191919'
        )
        self.canvas.pack()

        self.local_x = window_size // 2
        self.local_y = window_size // 2

        self.create_modern_circular_background_program()

        self.create_program_buttons()

        self.canvas.bind('<Button-1>', self.on_program_click)
        self.overlay.bind('<Escape>', lambda e: self.close_menu())

        self.overlay.focus_set()
        self.overlay.focus_force()

        self.overlay.update_idletasks()

        logger.info("Program submenu created successfully")

    def create_modern_circular_background_program(self):
        logger.info("Creating simple circular background for program submenu")

        center_x, center_y = self.local_x, self.local_y
        radius = 160

        self.canvas.create_oval(
            center_x - radius, center_y - radius,
            center_x + radius, center_y + radius,
            fill='#1a1a1a', outline='', tags='background'
        )

        inner_radius = radius - 20
        self.canvas.create_oval(
            center_x - inner_radius, center_y - inner_radius,
            center_x + inner_radius, center_y + inner_radius,
            fill='#2a2a2a', outline='', tags='background'
        )

        core_radius = radius - 40
        self.canvas.create_oval(
            center_x - core_radius, center_y - core_radius,
            center_x + core_radius, center_y + core_radius,
            fill='#333333', outline='', tags='background'
        )

        self.canvas.create_oval(
            center_x - radius, center_y - radius,
            center_x + radius, center_y + radius,
            fill='', outline='#9b59b6', width=2,
            tags='background'
        )

        self.canvas.create_oval(
            center_x - inner_radius, center_y - inner_radius,
            center_x + inner_radius, center_y + inner_radius,
            fill='', outline='#9b59b6', width=1,
            tags='background'
        )

        center_dot_radius = 3
        self.canvas.create_oval(
            center_x - center_dot_radius, center_y - center_dot_radius,
            center_x + center_dot_radius, center_y + center_dot_radius,
            fill='#9b59b6', outline='', tags='background'
        )

        logger.info("Simple background created")

    def create_hexagon(self, x, y, size, fill_color, outline_color):
        points = []
        for i in range(6):
            angle = i * 60 * math.pi / 180
            point_x = x + size * math.cos(angle)
            point_y = y + size * math.sin(angle)
            points.extend([point_x, point_y])

        self.canvas.create_polygon(
            points,
            fill=fill_color,
            outline=outline_color,
            width=1
        )

    def create_program_buttons(self):
        logger.info("Creating program buttons...")

        programs = self.load_program_data()

        radius = 110
        button_size = 70

        center_size = 25
        center_circle = self.canvas.create_oval(
            self.local_x - center_size,
            self.local_y - center_size,
            self.local_x + center_size,
            self.local_y + center_size,
            fill='#34495e',
            outline='white',
            width=2
        )

        center_text = self.canvas.create_text(
            self.local_x,
            self.local_y,
            text="프로그램",
            font=('Arial', 9, 'bold'),
            fill='white'
        )

        self.buttons = []
        if programs:
            for i, program in enumerate(programs):
                if i >= 7:
                    break

                angle = math.radians(i * (360 / len(programs)) - 90)
                btn_x = self.local_x + radius * math.cos(angle)
                btn_y = self.local_y + radius * math.sin(angle)

                program_colors = ['#e74c3c', '#3498db', '#2ecc71', '#f39c12', '#9b59b6', '#e67e22', '#1abc9c']
                button_color = program_colors[i % len(program_colors)]

                x1 = btn_x - button_size/2
                y1 = btn_y - button_size/2
                x2 = btn_x + button_size/2
                y2 = btn_y + button_size/2

                btn = self.canvas.create_oval(
                    x1, y1, x2, y2,
                    fill=button_color,
                    outline='white',
                    width=2,
                    tags=f"button_{i}"
                )

                text = self.canvas.create_text(
                    btn_x, btn_y,
                    text=program['text'],
                    font=('Arial', 9, 'bold'),
                    fill='white',
                    tags=f"button_{i}"
                )

                command = lambda path=program['path'], name=program['text']: self.execute_program(path, name)

                self.buttons.append({
                    "id": btn,
                    "text_id": text,
                    "x": btn_x,
                    "y": btn_y,
                    "size": button_size,
                    "text": program['text'],
                    "path": program['path'],
                    "command": command,
                    "color": button_color,
                    "tag": f"button_{i}"
                })

                # 헬퍼 메서드를 사용하여 이벤트 바인딩
                self._bind_button_events(i, command)

                logger.info(f"Program button {i} ({program['text']}) created at ({btn_x}, {btn_y})")

        else:
            self.canvas.create_text(
                self.local_x,
                self.local_y + 50,
                text="등록된 프로그램이 없습니다.\n설정에서 프로그램을 추가해주세요.",
                font=('Arial', 11),
                fill='white',
                justify='center'
            )

    def load_program_data(self):
        return load_data('custom_programs', [])

    def on_program_click(self, event):
        x, y = event.x, event.y
        logger.debug(f"Program canvas click at ({x}, {y})")

        center_dist = math.sqrt((x - self.local_x)**2 + (y - self.local_y)**2)
        if center_dist <= 30:
            logger.info("Center circle clicked, closing menu")
            self.close_menu()
            return

        for btn in self.buttons:
            btn_dist = math.sqrt((x - btn["x"])**2 + (y - btn["y"])**2)
            if btn_dist <= btn["size"] / 2:
                return

        if center_dist <= 160:
            logger.info("Clicked empty space in circular menu, closing")
            self.close_menu()

    def execute_program(self, program_path, program_name):
        try:
            self.close_menu()

            import subprocess
            import os

            if not os.path.exists(program_path):
                logger.error(f"Program file not found: {program_path}")
                return

            if os.name == 'nt':
                subprocess.Popen([program_path], shell=True,
                               creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0)
            else:
                subprocess.Popen([program_path])

            logger.info(f"Program executed: {program_name} -> {program_path}")

        except Exception as e:
            logger.error(f"Program execution failed: {e}")

    def create_submenu(self, x, y):
        logger.info(f"Creating submenu at position ({x}, {y})")
        
        if self.overlay:
            logger.info("Submenu already open, closing...")
            self.close_menu()
            return
        
        self.mouse_x = x
        self.mouse_y = y
        
        self.overlay = tk.Toplevel()
        self.overlay.attributes('-topmost', True)
        self.overlay.overrideredirect(True)
        
        window_size = 350
        
        virtual_x = self.overlay.winfo_vrootx()
        virtual_y = self.overlay.winfo_vrooty()
        virtual_width = self.overlay.winfo_vrootwidth()
        virtual_height = self.overlay.winfo_vrootheight()
        
        window_x = x - window_size // 2
        window_y = y - window_size // 2
        
        max_x = virtual_x + virtual_width - window_size
        max_y = virtual_y + virtual_height - window_size
        
        if window_x < virtual_x:
            window_x = virtual_x
        elif window_x > max_x:
            window_x = max_x
        
        if window_y < virtual_y:
            window_y = virtual_y
        elif window_y > max_y:
            window_y = max_y
        
        self.overlay.geometry(f"{window_size}x{window_size}+{window_x}+{window_y}")
        
        self.overlay.attributes('-alpha', 0.9)
        self.overlay.configure(bg='#191919')
        self.overlay.wm_attributes('-transparentcolor', '#191919')
        
        self.background = tk.Toplevel()
        self.background.attributes('-topmost', True)
        self.background.attributes('-alpha', 0.01)
        self.background.overrideredirect(True)
        
        self.background.geometry(f"{virtual_width}x{virtual_height}+{virtual_x}+{virtual_y}")
        
        self.background.bind('<Button-1>', lambda e: self.close_menu())
        
        self.canvas = tk.Canvas(
            self.overlay,
            width=window_size,
            height=window_size,
            bg='#191919',
            highlightthickness=0
        )
        self.canvas.pack()
        
        self.local_x = window_size // 2
        self.local_y = window_size // 2
        
        self.create_modern_circular_background_submenu()
        
        self.create_submenu_buttons()
        
        self.canvas.bind('<Button-1>', self.on_click)
        self.overlay.bind('<Escape>', lambda e: self.close_menu())
        
        self.overlay.focus_set()
        self.overlay.focus_force()
        
        self.overlay.update_idletasks()
        
        logger.info("Submenu created successfully")
    
    def create_submenu_buttons(self):
        logger.info("Creating submenu buttons...")
        
        radius = 110
        button_size = 70
        
        button_info = [
            {"text": "스캐너", "color": "#3498db", "command": self.open_scanner},
            {"text": "MES", "color": "#2ecc71", "command": self.open_mes},
            {"text": "GDRIVE", "color": "#e74c3c", "command": self.open_gdrive},
            {"text": "인터넷", "color": "#f39c12", "command": self.check_internet},
            {"text": "PC정보", "color": "#9b59b6", "command": self.show_pc_info},
            {"text": "소프트웨어", "color": "#1abc9c", "command": self.show_software_list}
        ]
        
        center_size = 25
        center_circle = self.canvas.create_oval(
            self.local_x - center_size,
            self.local_y - center_size,
            self.local_x + center_size,
            self.local_y + center_size,
            fill='#34495e',
            outline='white',
            width=2
        )
        
        self.canvas.create_text(
            self.local_x,
            self.local_y,
            text="점검",
            font=('Arial', 10, 'bold'),
            fill='white'
        )
        
        self.buttons = []
        for i, btn_info in enumerate(button_info):
            angle = (i * 60 - 90) * math.pi / 180
            
            btn_x = self.local_x + radius * math.cos(angle)
            btn_y = self.local_y + radius * math.sin(angle)
            
            x1 = btn_x - button_size/2
            y1 = btn_y - button_size/2
            x2 = btn_x + button_size/2
            y2 = btn_y + button_size/2
            
            btn = self.canvas.create_oval(
                x1, y1, x2, y2,
                fill=btn_info['color'],
                outline='white',
                width=2,
                tags=f"button_{i}"
            )
            
            text = self.canvas.create_text(
                btn_x, btn_y,
                text=btn_info['text'],
                font=('Arial', 9, 'bold'),
                fill='white',
                tags=f"button_{i}"
            )
            
            self.buttons.append({
                "id": btn,
                "text_id": text,
                "x": btn_x,
                "y": btn_y,
                "size": button_size,
                "command": btn_info['command'],
                "color": btn_info['color'],
                "tag": f"button_{i}"
            })

            # 헬퍼 메서드를 사용하여 이벤트 바인딩
            self._bind_button_events(i, btn_info['command'])
        
        logger.info(f"Created {len(self.buttons)} submenu buttons")
    
    def open_scanner(self):
        logger.info("Scanning COM ports...")
        self.close_menu()
        
        try:
            import serial
            from serial.tools import list_ports
            
            def find_active_com_ports(baud_rate=9600):
                active_ports = []
                
                for port in list_ports.comports():
                    if port.device.upper() in ("COM1", "COM2"):
                        continue
                        
                    desc_lower = (port.description or "").lower()
                    hwid_lower = (port.hwid or "").lower()
                    
                    virtual_keywords = (
                        "virtual", "bluetooth", "com0com", "eltima", "null-modem",
                        "가상", "블루투스", "표준 직렬 over bluetooth"
                    )
                    if any(k in desc_lower for k in virtual_keywords) or "bthenum" in hwid_lower:
                        continue
                    
                    try:
                        ser = serial.Serial(port.device, baud_rate, timeout=0.2)
                        ser.close()
                        active_ports.append({
                            "port": port.device,
                            "description": port.description
                        })
                    except:
                        pass
                
                return active_ports
            
            logger.info("Starting COM port scan...")
            ports = find_active_com_ports()
            
            if ports:
                port_list = []
                for port in ports:
                    port_list.append(f"🔌 {port['port']}: {port['description']}")
                
                result_msg = f"🔍 스캐너 COM 포트 검색 결과\n\n"
                result_msg += f"발견된 포트: {len(ports)}개\n\n"
                result_msg += "\n".join(port_list)
                result_msg += f"\n\n※ COM1, COM2 및 가상 포트는 제외됨"
                
                msg_title = "스캐너 포트 검색 - 완료"
                
            else:
                result_msg = f"🔍 스캐너 COM 포트 검색 결과\n\n"
                result_msg += f"사용 가능한 COM 포트를 찾을 수 없습니다.\n\n"
                result_msg += f"확인 사항:\n"
                result_msg += f"• 스캐너가 PC에 연결되어 있는지 확인\n"
                result_msg += f"• USB 케이블 연결 상태 확인\n"
                result_msg += f"• 드라이버 설치 여부 확인\n"
                result_msg += f"• 다른 프로그램에서 포트를 사용 중인지 확인"
                
                msg_title = "스캐너 포트 검색 - 포트 없음"
            
            messagebox.showinfo(msg_title, result_msg)
            logger.info(f"COM port scan completed: {len(ports)} ports found")
            
        except ImportError:
            error_msg = f"🚫 스캐너 기능 오류\n\n"
            error_msg += f"pyserial 라이브러리가 설치되지 않았습니다.\n\n"
            error_msg += f"설치 방법:\n"
            error_msg += f"pip install pyserial\n\n"
            error_msg += f"설치 후 프로그램을 다시 실행하세요."
            
            messagebox.showerror("스캐너 - 라이브러리 오류", error_msg)
            logger.error("pyserial library not installed")
            
        except Exception as e:
            error_msg = f"🚫 스캐너 COM 포트 검색 오류\n\n"
            error_msg += f"오류 내용: {str(e)}\n\n"
            error_msg += f"가능한 원인:\n"
            error_msg += f"• 시스템 권한 부족\n"
            error_msg += f"• COM 포트 접근 제한\n"
            error_msg += f"• 하드웨어 문제"
            
            messagebox.showerror("스캐너 - 시스템 오류", error_msg)
            logger.error(f"Scanner COM port scan error: {e}")
    
    def open_mes(self):
        environment = load_data('environment', 'office')
        if environment == 'office':
            target_ip = "192.168.0.250"
            test_port = 80
        else:
            target_ip = "10.10.10.10"
            test_port = 445

        logger.info(f"Testing MES connection to {target_ip}:{test_port} (environment: {environment})...")
        self.close_menu()

        try:
            import socket
            import time

            timeout = 3
            
            start_time = time.time()
            
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(timeout)
            
            try:
                result = sock.connect_ex((target_ip, test_port))
                end_time = time.time()
                response_time = round((end_time - start_time) * 1000, 2)
                
                env_display = "사무용" if environment == 'office' else "현장용"
                if result == 0:
                    status_msg = f"🟢 MES 서버 연결 성공\n\n환경: {env_display}\n서버: {target_ip}:{test_port}\n응답시간: {response_time}ms\n상태: 정상"
                    msg_title = "MES 통신 테스트 - 성공"
                else:
                    status_msg = f"🔴 MES 서버 연결 실패\n\n환경: {env_display}\n서버: {target_ip}:{test_port}\n응답시간: {response_time}ms\n상태: 연결 불가"
                    msg_title = "MES 통신 테스트 - 실패"

            except socket.timeout:
                env_display = "사무용" if environment == 'office' else "현장용"
                status_msg = f"🔴 MES 서버 연결 타임아웃\n\n환경: {env_display}\n서버: {target_ip}:{test_port}\n타임아웃: {timeout}초\n상태: 응답 없음"
                msg_title = "MES 통신 테스트 - 타임아웃"
            except Exception as e:
                env_display = "사무용" if environment == 'office' else "현장용"
                status_msg = f"🔴 MES 서버 연결 오류\n\n환경: {env_display}\n서버: {target_ip}:{test_port}\n오류: {str(e)}\n상태: 연결 실패"
                msg_title = "MES 통신 테스트 - 오류"
            finally:
                sock.close()
                
        except Exception as e:
            env_display = "사무용" if environment == 'office' else "현장용"
            status_msg = f"🔴 네트워크 테스트 실행 오류\n\n환경: {env_display}\n오류 내용: {str(e)}"
            msg_title = "MES 통신 테스트 - 시스템 오류"
            
        messagebox.showinfo(msg_title, status_msg)
        logger.info(f"MES connection test completed: {target_ip}")
    
    def open_gdrive(self):
        logger.info("Testing GDRIVE connection to 192.168.2.4...")
        self.close_menu()
        
        try:
            import socket
            import time
            
            target_ip = "192.168.2.4"
            test_port = 445
            timeout = 3
            
            start_time = time.time()
            
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(timeout)
            
            try:
                result = sock.connect_ex((target_ip, test_port))
                end_time = time.time()
                response_time = round((end_time - start_time) * 1000, 2)
                
                if result == 0:
                    status_msg = f"🟢 GDRIVE 서버 연결 성공\n\n서버: {target_ip}:{test_port}\n응답시간: {response_time}ms\n상태: 정상"
                    msg_title = "GDRIVE 통신 테스트 - 성공"
                else:
                    try:
                        socket.setdefaulttimeout(3)
                        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
                            sock.connect((target_ip, 80))
                        status_msg = f"🟡 GDRIVE 서버 네트워크 연결\n\n서버: {target_ip}\nSMB 포트: 연결 불가\n일반 포트: 연결됨\n상태: 부분 연결"
                        msg_title = "GDRIVE 통신 테스트 - 부분 성공"
                    except (socket.error, socket.timeout):
                        try:
                            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
                                sock.connect((target_ip, 443))
                            status_msg = f"🟡 GDRIVE 서버 HTTPS 연결\n\n서버: {target_ip}\nSMB 포트: 연결 불가\nHTTPS 포트: 연결됨\n상태: 부분 연결"
                            msg_title = "GDRIVE 통신 테스트 - 부분 성공"
                        except (socket.error, socket.timeout):
                            status_msg = f"🔴 GDRIVE 서버 연결 실패\n\n서버: {target_ip}:{test_port}\n응답시간: {response_time}ms\nPing: 응답 없음\n상태: 연결 불가"
                            msg_title = "GDRIVE 통신 테스트 - 실패"
                    except:
                        status_msg = f"🔴 GDRIVE 서버 연결 실패\n\n서버: {target_ip}:{test_port}\n응답시간: {response_time}ms\n상태: 연결 불가"
                        msg_title = "GDRIVE 통신 테스트 - 실패"
                    
            except socket.timeout:
                status_msg = f"🔴 GDRIVE 서버 연결 타임아웃\n\n서버: {target_ip}:{test_port}\n타임아웃: {timeout}초\n상태: 응답 없음"
                msg_title = "GDRIVE 통신 테스트 - 타임아웃"
            except Exception as e:
                status_msg = f"🔴 GDRIVE 서버 연결 오류\n\n서버: {target_ip}:{test_port}\n오류: {str(e)}\n상태: 연결 실패"
                msg_title = "GDRIVE 통신 테스트 - 오류"
            finally:
                sock.close()
                
        except Exception as e:
            status_msg = f"🔴 네트워크 테스트 실행 오류\n\n오류 내용: {str(e)}"
            msg_title = "GDRIVE 통신 테스트 - 시스템 오류"
            
        messagebox.showinfo(msg_title, status_msg)
        logger.info(f"GDRIVE connection test completed: {target_ip}")
    
    def show_software_list(self):
        logger.info("Showing installed software list...")
        self.close_menu()
        
        try:
            import subprocess
            from datetime import datetime
            
            software_list = []
            
            if platform.system() == "Windows":
                try:
                    logger.info("Getting software list from registry...")
                    ps_command = """
                    $programs = @()
                    
                    $64bit = Get-ItemProperty "HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*" -ErrorAction SilentlyContinue |
                        Where-Object { 
                            $_.DisplayName -and 
                            $_.DisplayName -ne "" -and
                            $_.SystemComponent -ne 1 -and
                            $_.ReleaseType -ne "Security Update" -and
                            $_.ReleaseType -ne "Update Rollup" -and
                            $_.ReleaseType -ne "Hotfix" -and
                            $_.ParentKeyName -eq $null -and
                            ($_.WindowsInstaller -ne 1 -or $_.NoRemove -ne 1)
                        } |
                        Select-Object DisplayName, InstallDate, DisplayVersion, Publisher
                    
                    $32bit = Get-ItemProperty "HKLM:\\Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*" -ErrorAction SilentlyContinue |
                        Where-Object { 
                            $_.DisplayName -and 
                            $_.DisplayName -ne "" -and
                            $_.SystemComponent -ne 1 -and
                            $_.ReleaseType -ne "Security Update" -and
                            $_.ReleaseType -ne "Update Rollup" -and
                            $_.ReleaseType -ne "Hotfix" -and
                            $_.ParentKeyName -eq $null -and
                            ($_.WindowsInstaller -ne 1 -or $_.NoRemove -ne 1)
                        } |
                        Select-Object DisplayName, InstallDate, DisplayVersion, Publisher
                    
                    $user = Get-ItemProperty "HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*" -ErrorAction SilentlyContinue |
                        Where-Object { 
                            $_.DisplayName -and 
                            $_.DisplayName -ne "" -and
                            $_.SystemComponent -ne 1 -and
                            $_.ReleaseType -ne "Security Update" -and
                            $_.ReleaseType -ne "Update Rollup" -and
                            $_.ReleaseType -ne "Hotfix" -and
                            $_.ParentKeyName -eq $null
                        } |
                        Select-Object DisplayName, InstallDate, DisplayVersion, Publisher
                    
                    $programs += $64bit
                    $programs += $32bit
                    $programs += $user
                    
                    $uniquePrograms = @{}
                    $programs | ForEach-Object {
                        if ($_.DisplayName -and -not $uniquePrograms.ContainsKey($_.DisplayName)) {
                            $uniquePrograms[$_.DisplayName] = $_
                        }
                    }
                    
                    $uniquePrograms.Values | Sort-Object DisplayName | ForEach-Object {
                        Write-Output "$($_.DisplayName)|$($_.DisplayVersion)|$($_.Publisher)"
                    }
                    """
                    
                    result = subprocess.run(
                        ["powershell", "-ExecutionPolicy", "Bypass", "-Command", ps_command],
                        capture_output=True,
                        text=True,
                        encoding='cp949'
                    )
                    
                    if result.returncode == 0 and result.stdout.strip():
                        lines = result.stdout.strip().split('\n')
                        for line in lines:
                            if line.strip() and '|' in line:
                                parts = line.strip().split('|')
                                if len(parts) >= 1:
                                    name = parts[0].strip()
                                    version = parts[1].strip() if len(parts) > 1 and parts[1].strip() else ""
                                    publisher = parts[2].strip() if len(parts) > 2 and parts[2].strip() else ""
                                    
                                    if version:
                                        display_text = f"{name} (v{version})"
                                    else:
                                        display_text = name
                                    
                                    if publisher:
                                        display_text += f" [{publisher}]"
                                    
                                    software_list.append(display_text)
                    else:
                        logger.error(f"PowerShell command failed: {result.stderr}")
                        software_list.append("소프트웨어 목록을 가져올 수 없습니다.")
                
                except Exception as e:
                    logger.error(f"Failed to get software list: {e}")
                    software_list.append(f"소프트웨어 목록을 가져오는 중 오류 발생: {str(e)}")
            
            else:
                software_list.append("Windows 이외의 시스템은 아직 지원되지 않습니다.")
            
            if not software_list:
                software_list.append("설치된 소프트웨어를 찾을 수 없습니다.")
            
            software_list.sort()
            
            software_window = tk.Toplevel()
            software_window.title("설치된 소프트웨어 목록")
            software_window.geometry("600x500")
            software_window.resizable(True, True)
            
            info_label = ttk.Label(
                software_window,
                text=f"총 {len(software_list)}개의 프로그램이 설치되어 있습니다.",
                font=('Arial', 10, 'bold')
            )
            info_label.pack(pady=10)
            
            text_area = scrolledtext.ScrolledText(
                software_window,
                wrap=tk.WORD,
                font=('Consolas', 9),
                padx=10,
                pady=10
            )
            text_area.pack(fill=tk.BOTH, expand=True, padx=10, pady=(0, 10))
            
            text_area.insert(tk.END, "\n".join(software_list))
            text_area.config(state=tk.DISABLED)
            
            close_btn = ttk.Button(
                software_window,
                text="닫기",
                command=software_window.destroy
            )
            close_btn.pack(pady=(0, 10))
            
            logger.info(f"Displayed {len(software_list)} installed programs")
            
        except Exception as e:
            logger.error(f"Software list error: {e}")
            messagebox.showerror("오류", f"소프트웨어 목록을 가져오는 중 오류가 발생했습니다:\n{str(e)}")

    def create_modern_circular_background_auto_action(self):
            logger.info("Creating simple circular background for auto action submenu")
            
            center_x, center_y = self.local_x, self.local_y
            radius = 160
            
            self.canvas.create_oval(
                center_x - radius, center_y - radius,
                center_x + radius, center_y + radius,
                fill='#1a1a1a', outline='', tags='background'
            )
            
            inner_radius = radius - 20
            self.canvas.create_oval(
                center_x - inner_radius, center_y - inner_radius,
                center_x + inner_radius, center_y + inner_radius,
                fill='#2a2a2a', outline='', tags='background'
            )
            
            core_radius = radius - 40
            self.canvas.create_oval(
                center_x - core_radius, center_y - core_radius,
                center_x + core_radius, center_y + core_radius,
                fill='#333333', outline='', tags='background'
            )
            
            self.canvas.create_oval(
                center_x - radius, center_y - radius,
                center_x + radius, center_y + radius,
                fill='', outline='#4a9eff', width=2,
                tags='background'
            )
            
            self.canvas.create_oval(
                center_x - inner_radius, center_y - inner_radius,
                center_x + inner_radius, center_y + inner_radius,
                fill='', outline='#4a9eff', width=1,
                tags='background'
            )
            
            center_dot_radius = 3
            self.canvas.create_oval(
                center_x - center_dot_radius, center_y - center_dot_radius,
                center_x + center_dot_radius, center_y + center_dot_radius,
                fill='#4a9eff', outline='', tags='background'
            )
            
            logger.info("Simple background created")
    
    def blend_color(self, color1, color2, ratio):
        return color1
        

    def create_auto_action_buttons(self):
        logger.info("Creating auto action buttons...")
        
        button_info = [
            {"text": "네트워크 Fix", "color": "#e74c3c", "command": self.network_fix},
            {"text": "브라우저 Fix", "color": "#3498db", "command": self.browser_fix},
            {"text": "프린터 Fix", "color": "#2ecc71", "command": self.printer_fix},
            {"text": "MES Fix", "color": "#f39c12", "command": self.mes_fix}
        ]
        
        radius = 110
        button_size = 70
        
        center_size = 25
        center_circle = self.canvas.create_oval(
            self.local_x - center_size,
            self.local_y - center_size,
            self.local_x + center_size,
            self.local_y + center_size,
            fill='#34495e',
            outline='white',
            width=2
        )
        
        self.canvas.create_text(
            self.local_x,
            self.local_y,
            text="자동조치",
            font=('Arial', 9, 'bold'),
            fill='white'
        )
        
        self.buttons = []
        for i, btn_info in enumerate(button_info):
            angle = (i * (360 / len(button_info)) - 90) * math.pi / 180
            
            btn_x = self.local_x + radius * math.cos(angle)
            btn_y = self.local_y + radius * math.sin(angle)
            
            x1 = btn_x - button_size/2
            y1 = btn_y - button_size/2
            x2 = btn_x + button_size/2
            y2 = btn_y + button_size/2
            
            btn = self.canvas.create_oval(
                x1, y1, x2, y2,
                fill=btn_info["color"],
                outline='white',
                width=2,
                tags=f"button_{i}"
            )
            
            text = self.canvas.create_text(
                btn_x, btn_y,
                text=btn_info['text'],
                font=('Arial', 9, 'bold'),
                fill='white',
                tags=f"button_{i}"
            )
            
            self.buttons.append({
                "id": btn,
                "text_id": text,
                "x": btn_x,
                "y": btn_y,
                "size": button_size,
                "command": btn_info["command"],
                "color": btn_info["color"],
                "tag": f"button_{i}"
            })

            # 헬퍼 메서드를 사용하여 이벤트 바인딩
            self._bind_button_events(i, btn_info["command"])

            logger.info(f"Auto action button {i} ({btn_info['text']}) created at ({btn_x}, {btn_y})")
        
        logger.info(f"Created {len(self.buttons)} auto action buttons")
    
    def network_fix(self):
        logger.info("Network Fix initiated")
        self.close_menu()

        # 확인 대화상자 표시
        response = messagebox.askyesno(
            "네트워크 Fix 확인",
            "네트워크 자동 복구를 시작하시겠습니까?\n\n"
            "이 작업은 다음을 수행합니다:\n"
            "• 네트워크 어댑터 재시작\n"
            "• DNS 캐시 초기화\n"
            "• IP 설정 갱신\n"
            "• MES 통신 테스트"
        )

        # 사용자가 '아니오'를 선택한 경우 작업 취소
        if not response:
            logger.info("Network Fix cancelled by user")
            return

        def run_network_fix():
            try:
                progress_window = tk.Toplevel()
                progress_window.title("네트워크 Fix")
                
                RadialMenu.active_progress_windows.append(progress_window)
                
                def on_window_close():
                    try:
                        RadialMenu.active_progress_windows.remove(progress_window)
                    except:
                        pass
                    progress_window.destroy()
                
                progress_window.protocol("WM_DELETE_WINDOW", on_window_close)
                progress_window.geometry("500x400")
                progress_window.resizable(False, False)
                progress_window.transient(self.overlay if self.overlay else None)
                progress_window.grab_set()
                
                text_area = scrolledtext.ScrolledText(progress_window, height=20, width=60)
                text_area.pack(fill='both', expand=True, padx=10, pady=10)
                
                def log_progress(message):
                    text_area.insert(tk.END, f"{message}\n")
                    text_area.see(tk.END)
                    progress_window.update()
                
                log_progress("🔧 네트워크 Fix 시작...")
                log_progress("=" * 50)
                
                is_online = self.check_internet_connection()
                if is_online:
                    log_progress("✅ 현재 인터넷 연결 상태: 온라인")
                    log_progress("⚠️  오프라인이 아니지만 강제로 네트워크 복구를 진행합니다.")
                else:
                    log_progress("❌ 현재 인터넷 연결 상태: 오프라인")
                    log_progress("🔄 네트워크 자동 복구를 시작합니다.")
                
                log_progress("\n🚀 6단계 네트워크 복구 과정 시작...")
                
                recovery_success = self.auto_network_recovery()
                
                if recovery_success:
                    log_progress("\n✅ 네트워크 복구 완료!")
                else:
                    log_progress("\n❌ 네트워크 복구 실패")
                
                log_progress("\n🔍 MES 통신 테스트 시작...")
                mes_result = self.test_mes_communication()
                
                if mes_result:
                    log_progress("✅ MES 통신 테스트 성공! 정상적으로 통신되고 있습니다.")
                else:
                    log_progress("❌ MES 통신 테스트 실패. MES 서버 연결을 확인해주세요.")
                
                log_progress("\n" + "=" * 50)
                log_progress("🎉 네트워크 Fix 완료!")
                
                button_frame = tk.Frame(progress_window)
                button_frame.pack(pady=10)
                
                close_button = tk.Button(button_frame, text="닫기", 
                                       command=on_window_close,
                                       bg='#3498db', fg='white', font=('Arial', 10, 'bold'))
                close_button.pack()
                
            except Exception as e:
                logger.error(f"Network fix error: {e}")
                messagebox.showerror("오류", f"네트워크 Fix 중 오류가 발생했습니다:\n{str(e)}")
        
        import threading
        threading.Thread(target=run_network_fix, daemon=True).start()
    
    def browser_fix(self):
        logger.info("Browser Fix initiated")
        self.close_menu()
        
        result = messagebox.askyesno(
            "브라우저 Fix 확인",
            "브라우저 Fix를 실행하시겠습니까?\n\n"
            "다음 작업이 수행됩니다:\n"
            "• Chrome 브라우저 캐시 및 쿠키 삭제\n"
            "• Edge 브라우저 캐시 및 쿠키 삭제\n"
            "• 임시 인터넷 파일 정리\n\n"
            "진행하시겠습니까?"
        )
        
        if not result:
            logger.info("Browser Fix cancelled by user")
            return
        
        def run_browser_fix():
            try:
                progress_window = tk.Toplevel()
                progress_window.title("브라우저 Fix")
                
                RadialMenu.active_progress_windows.append(progress_window)
                
                def on_window_close():
                    try:
                        RadialMenu.active_progress_windows.remove(progress_window)
                    except:
                        pass
                    progress_window.destroy()
                
                progress_window.protocol("WM_DELETE_WINDOW", on_window_close)
                progress_window.geometry("500x300")
                progress_window.resizable(False, False)
                progress_window.transient(self.overlay if self.overlay else None)
                progress_window.grab_set()
                
                text_area = scrolledtext.ScrolledText(progress_window, height=15, width=60)
                text_area.pack(fill='both', expand=True, padx=10, pady=10)
                
                def log_progress(message):
                    text_area.insert(tk.END, f"{message}\n")
                    text_area.see(tk.END)
                    progress_window.update()
                
                log_progress("🌐 브라우저 Fix 시작...")
                log_progress("=" * 50)
                
                log_progress("🔄 브라우저 프로세스 종료 중...")
                
                browser_processes = ['chrome.exe', 'msedge.exe', 'iexplore.exe']
                for process_name in browser_processes:
                    try:
                        killed_any = False
                        for proc in psutil.process_iter(['pid', 'name']):
                            try:
                                if proc.info['name'] and proc.info['name'].lower() == process_name.lower():
                                    proc.terminate()
                                    killed_any = True
                                    log_progress(f"✅ {process_name} 프로세스 종료됨 (PID: {proc.info['pid']})")
                            except (psutil.NoSuchProcess, psutil.AccessDenied):
                                pass

                        if not killed_any:
                            log_progress(f"ℹ️  {process_name} 프로세스가 실행 중이 아님")
                    except Exception as e:
                        log_progress(f"⚠️  {process_name} 종료 중 오류: {e}")
                
                time.sleep(2)
                
                log_progress("\n🗑️  Chrome 캐시/쿠키 삭제 중...")
                chrome_paths = [
                    os.path.expanduser("~\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Cache"),
                    os.path.expanduser("~\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Cookies"),
                    os.path.expanduser("~\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Web Data")
                ]
                
                for path in chrome_paths:
                    try:
                        if os.path.exists(path):
                            if os.path.isdir(path):
                                import shutil
                                shutil.rmtree(path, ignore_errors=True)
                                log_progress(f"✅ Chrome: {os.path.basename(path)} 폴더 삭제됨")
                            else:
                                os.remove(path)
                                log_progress(f"✅ Chrome: {os.path.basename(path)} 파일 삭제됨")
                        else:
                            log_progress(f"ℹ️  Chrome: {os.path.basename(path)} 없음")
                    except Exception as e:
                        log_progress(f"⚠️  Chrome {os.path.basename(path)} 삭제 실패: {e}")
                
                log_progress("\n🗑️  Edge 캐시/쿠키 삭제 중...")
                edge_paths = [
                    os.path.expanduser("~\\AppData\\Local\\Microsoft\\Edge\\User Data\\Default\\Cache"),
                    os.path.expanduser("~\\AppData\\Local\\Microsoft\\Edge\\User Data\\Default\\Cookies"),
                    os.path.expanduser("~\\AppData\\Local\\Microsoft\\Edge\\User Data\\Default\\Web Data")
                ]
                
                for path in edge_paths:
                    try:
                        if os.path.exists(path):
                            if os.path.isdir(path):
                                import shutil
                                shutil.rmtree(path, ignore_errors=True)
                                log_progress(f"✅ Edge: {os.path.basename(path)} 폴더 삭제됨")
                            else:
                                os.remove(path)
                                log_progress(f"✅ Edge: {os.path.basename(path)} 파일 삭제됨")
                        else:
                            log_progress(f"ℹ️  Edge: {os.path.basename(path)} 없음")
                    except Exception as e:
                        log_progress(f"⚠️  Edge {os.path.basename(path)} 삭제 실패: {e}")
                
                log_progress("\n" + "=" * 50)
                log_progress("🎉 브라우저 Fix 완료!")
                log_progress("ℹ️  브라우저를 다시 시작하면 캐시/쿠키가 초기화됩니다.")
                
                button_frame = tk.Frame(progress_window)
                button_frame.pack(pady=10)
                
                close_button = tk.Button(button_frame, text="닫기", 
                                       command=on_window_close,
                                       bg='#3498db', fg='white', font=('Arial', 10, 'bold'))
                close_button.pack()
                
            except Exception as e:
                logger.error(f"Browser fix error: {e}")
                messagebox.showerror("오류", f"브라우저 Fix 중 오류가 발생했습니다:\n{str(e)}")
        
        import threading
        threading.Thread(target=run_browser_fix, daemon=True).start()
    

    def printer_fix(self):
        logger.info("Printer Fix initiated")
        self.close_menu()

        # 확인 대화상자 표시
        response = messagebox.askyesno(
            "프린터 Fix 확인",
            "프린터 자동 복구를 시작하시겠습니까?\n\n"
            "이 작업은 다음을 수행합니다:\n"
            "• 프린터 스풀러 서비스 재시작\n"
            "• 인쇄 대기열 초기화\n"
            "• 프린터 드라이버 상태 확인\n"
            "• 프린터 연결 테스트"
        )

        # 사용자가 '아니오'를 선택한 경우 작업 취소
        if not response:
            logger.info("Printer Fix cancelled by user")
            return

        def run_printer_fix():
            try:
                progress_window = tk.Toplevel()
                progress_window.title("프린터 Fix")
                
                RadialMenu.active_progress_windows.append(progress_window)
                
                def on_window_close():
                    try:
                        RadialMenu.active_progress_windows.remove(progress_window)
                    except:
                        pass
                    progress_window.destroy()
                
                progress_window.protocol("WM_DELETE_WINDOW", on_window_close)
                progress_window.geometry("500x400")
                progress_window.resizable(False, False)
                progress_window.transient(self.overlay if self.overlay else None)
                progress_window.grab_set()
                
                text_area = scrolledtext.ScrolledText(progress_window, height=20, width=60)
                text_area.pack(fill='both', expand=True, padx=10, pady=10)
                
                def log_progress(message):
                    text_area.insert(tk.END, f"{message}\n")
                    text_area.see(tk.END)
                    progress_window.update()
                
                log_progress("🖨️  프린터 Fix 시작...")
                log_progress("=" * 50)
                
                log_progress("🔧 프린터 레지스트리 설정 수정 중...")
                reg_command = [
                    'REG', 'ADD', 
                    'HKLM\\System\\CurrentControlSet\\Control\\Print',
                    '/v', 'RpcAuthnLevelPrivacyEnabled',
                    '/t', 'REG_DWORD',
                    '/d', '0',
                    '/f'
                ]
                
                try:
                    result = subprocess.run(reg_command, capture_output=True, text=True, timeout=30,
                         creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0)
                    if result.returncode == 0:
                        log_progress("✅ 레지스트리 설정 수정 완료")
                    else:
                        log_progress(f"⚠️  레지스트리 수정 경고: {result.stderr.strip()}")
                except Exception as e:
                    log_progress(f"❌ 레지스트리 수정 실패: {e}")
                
                log_progress("\n🔄 Print Spooler 서비스 중지 중...")
                try:
                    result = subprocess.run(['net', 'stop', 'spooler'], 
                                          capture_output=True, text=True, timeout=30,
                         creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0)
                    if result.returncode == 0:
                        log_progress("✅ Print Spooler 서비스 중지됨")
                    else:
                        log_progress(f"⚠️  Spooler 중지 경고: {result.stderr.strip()}")
                except Exception as e:
                    log_progress(f"❌ Spooler 중지 실패: {e}")
                
                time.sleep(3)
                
                log_progress("\n🚀 Print Spooler 서비스 시작 중...")
                try:
                    result = subprocess.run(['net', 'start', 'spooler'], 
                                          capture_output=True, text=True, timeout=30,
                         creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0)
                    if result.returncode == 0:
                        log_progress("✅ Print Spooler 서비스 시작됨")
                    else:
                        log_progress(f"⚠️  Spooler 시작 경고: {result.stderr.strip()}")
                except Exception as e:
                    log_progress(f"❌ Spooler 시작 실패: {e}")
                
                log_progress("\n🔧 추가 프린터 장애 해결 작업...")
                
                spooler_path = "C:\\Windows\\System32\\spool\\PRINTERS"
                try:
                    if os.path.exists(spooler_path):
                        import glob
                        spool_files = glob.glob(os.path.join(spooler_path, "*"))
                        if spool_files:
                            for file in spool_files:
                                try:
                                    os.remove(file)
                                    log_progress(f"✅ 대기열 파일 삭제: {os.path.basename(file)}")
                                except:
                                    pass
                        else:
                            log_progress("ℹ️  프린터 대기열이 이미 비어있습니다")
                    else:
                        log_progress("ℹ️  프린터 대기열 폴더를 찾을 수 없습니다")
                except Exception as e:
                    log_progress(f"⚠️  대기열 정리 중 오류: {e}")
                
                log_progress("\n" + "=" * 50)
                log_progress("🎉 프린터 Fix 완료!")
                log_progress("ℹ️  프린터 문제가 해결되었습니다. 프린터를 다시 사용해보세요.")
                
                log_progress("\n📋 장애 지속 시 추천 명령어:")
                log_progress("1. 프린터 드라이버 재설치")
                log_progress("2. rundll32 printui.dll,PrintUIEntry /k (프린터 새로고침)")
                log_progress("3. sfc /scannow (시스템 파일 검사)")
                
                button_frame = tk.Frame(progress_window)
                button_frame.pack(pady=10)
                
                close_button = tk.Button(button_frame, text="닫기", 
                                       command=on_window_close,
                                       bg='#2ecc71', fg='white', font=('Arial', 10, 'bold'))
                close_button.pack()
                
            except Exception as e:
                logger.error(f"Printer fix error: {e}")
                messagebox.showerror("오류", f"프린터 Fix 중 오류가 발생했습니다:\n{str(e)}")
        
        import threading
        threading.Thread(target=run_printer_fix, daemon=True).start()


    def mes_fix(self):
        logger.info("MES Fix initiated")
        self.close_menu()

        # 확인 대화상자 표시
        response = messagebox.askyesno(
            "MES Fix 확인",
            "MES 시스템 복구를 시작하시겠습니까?\n\n"
            "이 작업은 다음을 수행합니다:\n"
            "• MSCOMM32.ocx 자동 설치 및 등록\n"
            "• MES 서버 IP 설정 자동 구성\n"
            "• MES 통신 상태 확인\n"
            "• 연결 오류 자동 복구"
        )

        # 사용자가 '아니오'를 선택한 경우 작업 취소
        if not response:
            logger.info("MES Fix cancelled by user")
            return

        def run_mes_fix():
            try:
                progress_window = tk.Toplevel()
                progress_window.title("MES Fix")
                
                RadialMenu.active_progress_windows.append(progress_window)
                
                def on_window_close():
                    try:
                        RadialMenu.active_progress_windows.remove(progress_window)
                    except:
                        pass
                    progress_window.destroy()
                
                progress_window.protocol("WM_DELETE_WINDOW", on_window_close)
                progress_window.geometry("500x500")
                progress_window.resizable(False, False)
                progress_window.transient(self.overlay if self.overlay else None)
                progress_window.grab_set()

                text_area = scrolledtext.ScrolledText(progress_window, height=26, width=60)
                text_area.pack(fill='both', expand=True, padx=10, pady=10)
                
                def log_progress(message):
                    text_area.insert(tk.END, f"{message}\n")
                    text_area.see(tk.END)
                    progress_window.update()
                
                log_progress("🏭 MES Fix 시작...")
                log_progress("=" * 50)

                log_progress("\n[1/2] MSCOMM32.ocx 설치 및 등록")
                log_progress("=" * 50)

                import ctypes
                import tempfile
                import shutil

                try:
                    import requests
                except ImportError:
                    log_progress("⚠️  requests 모듈이 없습니다. 다운로드를 건너뜁니다.")
                    requests = None

                is_admin = ctypes.windll.shell32.IsUserAnAdmin()
                if not is_admin:
                    log_progress("⚠️  관리자 권한이 필요합니다!")
                    log_progress("   프로그램을 관리자 권한으로 실행해주세요.")
                else:
                    log_progress("✅ 관리자 권한 확인됨")

                url = 'https://file.geomedical.kr/file/MSCOMM32.ocx'
                dest_file = os.path.join(os.environ['windir'], 'SysWOW64', 'MSCOMM32.ocx')

                if os.path.exists(dest_file):
                    log_progress(f"ℹ️  MSCOMM32.ocx가 이미 설치되어 있습니다.")
                    log_progress(f"   위치: {dest_file}")
                else:
                    log_progress(f"\n📥 파일 다운로드 중...")
                    log_progress(f"   URL: {url}")

                    temp_file = None
                    if requests:
                        try:
                            temp_file = os.path.join(tempfile.gettempdir(), 'MSCOMM32.ocx')

                            response_download = requests.get(url, timeout=30)
                            response_download.raise_for_status()

                            with open(temp_file, 'wb') as f:
                                f.write(response_download.content)

                            log_progress(f"✅ 다운로드 완료: {temp_file}")

                        except Exception as e:
                            log_progress(f"❌ 다운로드 실패: {e}")
                            log_progress("   네트워크 연결을 확인해주세요.")
                            temp_file = None

                    if temp_file and os.path.exists(temp_file):
                        log_progress(f"\n📁 SysWOW64에 복사 중...")
                        log_progress(f"   대상: {dest_file}")

                        try:
                            shutil.copy2(temp_file, dest_file)
                            log_progress(f"✅ 파일 복사 완료")

                            log_progress(f"\n⚙️  regsvr32 등록 중...")

                            result = subprocess.run(
                                ['regsvr32.exe', '/s', dest_file],
                                capture_output=True,
                                timeout=10
                            )

                            if result.returncode == 0:
                                log_progress(f"✅ MSCOMM32.ocx 등록 완료")
                            else:
                                log_progress(f"⚠️  등록 실패 (코드: {result.returncode})")

                            try:
                                os.remove(temp_file)
                                log_progress(f"✅ 임시 파일 삭제 완료")
                            except:
                                pass

                        except Exception as e:
                            log_progress(f"❌ 파일 복사/등록 실패: {e}")
                            if not is_admin:
                                log_progress("   관리자 권한으로 다시 실행해주세요.")
                    else:
                        log_progress("❌ MSCOMM32.ocx 다운로드에 실패했습니다.")
                        log_progress("   수동으로 설치가 필요할 수 있습니다.")

                log_progress("\n" + "=" * 50)
                log_progress("[2/2] MES 서버 IP 설정")
                log_progress("=" * 50)

                env_setting = load_data('environment', 'office')
                log_progress(f"현재 사용 환경: {env_setting}")

                if env_setting == 'office':
                    target_ip = "192.168.0.250,14333"
                    env_name = "사무용"
                else:
                    target_ip = "10.10.10.10,14333"
                    env_name = "현장용"

                log_progress(f"설정할 IP: {target_ip} ({env_name})")

                def update_ini_file(file_path, file_description):
                    if not os.path.exists(file_path):
                        log_progress(f"⚠️  {file_description} 파일을 찾을 수 없습니다: {file_path}")
                        log_progress(f"   파일이 없으므로 건너뜁니다.")
                        return False

                    log_progress(f"\n📁 {file_description} 파일 발견: {file_path}")

                    backup_path = file_path + ".backup"
                    try:
                        shutil.copy2(file_path, backup_path)
                        log_progress(f"✅ 백업 파일 생성: {backup_path}")
                    except Exception as e:
                        log_progress(f"⚠️  백업 생성 실패: {e}")

                    log_progress(f"🔧 IP 설정 수정 중...")
                    try:
                        encodings_to_try = ['utf-8', 'cp949', 'euc-kr', 'latin-1']
                        lines = None
                        used_encoding = None

                        for encoding in encodings_to_try:
                            try:
                                with open(file_path, 'r', encoding=encoding) as f:
                                    lines = f.readlines()
                                    used_encoding = encoding
                                    log_progress(f"✅ 파일 인코딩 감지: {encoding}")
                                    break
                            except UnicodeDecodeError:
                                continue

                        if lines is None:
                            log_progress("❌ 파일 인코딩을 감지할 수 없습니다!")
                            return False

                        in_server_section = False
                        modified = False

                        for i, line in enumerate(lines):
                            line_stripped = line.strip()

                            if line_stripped == '[SERVER]':
                                in_server_section = True
                                log_progress("✅ [SERVER] 섹션 발견")
                                continue

                            if in_server_section:
                                if line_stripped.startswith('[') and line_stripped.endswith(']'):
                                    in_server_section = False
                                    continue

                                if line_stripped.startswith('IP='):
                                    old_ip = line_stripped[3:]
                                    lines[i] = f"IP={target_ip}\n"
                                    log_progress(f"✅ IP 설정 변경: {old_ip} → {target_ip}")
                                    modified = True
                                    break

                        if not modified:
                            log_progress("❌ IP= 설정을 찾을 수 없습니다!")
                            return False

                        with open(file_path, 'w', encoding=used_encoding) as f:
                            f.writelines(lines)

                        log_progress(f"✅ {file_description} 파일 저장 완료")
                        return True

                    except Exception as e:
                        log_progress(f"❌ 파일 수정 실패: {e}")
                        return False

                config_files = [
                    ("C:\\geopop\\commlib\\pf_profile.ini", "geopop pf_profile.ini"),
                    ("C:\\GEO_WORK\\commlib\\pf_profile.ini", "GEO_WORK pf_profile.ini"),
                    ("C:\\geomedical\\Geo_ERP\\Setup.ini", "Geo_ERP Setup.ini")
                ]

                success_count = 0
                total_count = len(config_files)

                for file_path, file_desc in config_files:
                    log_progress(f"\n{'─' * 50}")
                    if update_ini_file(file_path, file_desc):
                        success_count += 1
                
                log_progress("\n" + "=" * 50)
                log_progress("🎉 MES Fix 완료!")
                log_progress(f"ℹ️  MES 서버 IP가 {env_name} 환경에 맞게 설정되었습니다.")
                log_progress(f"ℹ️  처리 결과: {success_count}/{total_count} 파일 성공")
                if success_count == 0:
                    log_progress("⚠️  모든 설정 파일을 찾을 수 없습니다.")
                    log_progress("   MES 프로그램이 설치되어 있는지 확인하세요.")
                elif success_count < total_count:
                    log_progress(f"⚠️  일부 파일({total_count - success_count}개)을 처리하지 못했습니다.")
                    log_progress("   위의 로그를 확인하세요.")
                log_progress("ℹ️  MES 프로그램을 재시작하세요.")
                
                button_frame = tk.Frame(progress_window)
                button_frame.pack(pady=10)
                
                close_button = tk.Button(button_frame, text="닫기", 
                                       command=on_window_close,
                                       bg='#f39c12', fg='white', font=('Arial', 10, 'bold'))
                close_button.pack()
                
            except Exception as e:
                logger.error(f"MES fix error: {e}")
                messagebox.showerror("오류", f"MES Fix 중 오류가 발생했습니다:\n{str(e)}")
        
        import threading
        threading.Thread(target=run_mes_fix, daemon=True).start()



    def start_auto_action_animations(self):
        if not hasattr(self, 'canvas') or not self.canvas:
            return
            
        self.fade_in_animation()
        
        self.simple_border_pulse()
    
    def fade_in_animation(self, alpha=0.1):
        if not hasattr(self, 'overlay') or not self.overlay:
            return
            
        if alpha < 0.9:
            self.overlay.attributes('-alpha', alpha)
            self.overlay.after(20, lambda: self.fade_in_animation(alpha + 0.1))
        else:
            self.overlay.attributes('-alpha', 0.9)
    
    def pulse_animation(self, scale=1.0, growing=True):
        if not hasattr(self, 'canvas') or not self.canvas:
            return
            
        try:
            for i in range(4):
                button_tag = f'button_{i}_glow'
                items = self.canvas.find_withtag(button_tag)
                if items:
                    for item in items:
                        coords = self.canvas.coords(item)
                        if len(coords) == 4:
                            center_x = (coords[0] + coords[2]) / 2
                            center_y = (coords[1] + coords[3]) / 2
                            size = (coords[2] - coords[0]) / 2
                            
                            new_size = size * scale
                            self.canvas.coords(item,
                                center_x - new_size, center_y - new_size,
                                center_x + new_size, center_y + new_size)
            
            if growing:
                new_scale = scale + 0.02
                if new_scale > 1.1:
                    new_scale = 1.1
                    growing = False
            else:
                new_scale = scale - 0.02
                if new_scale < 0.9:
                    new_scale = 0.9
                    growing = True
            
            self.overlay.after(50, lambda: self.pulse_animation(new_scale, growing))
            
        except:
            pass
    
    def rotation_animation(self, angle=0):
        if not hasattr(self, 'canvas') or not self.canvas:
            return
            
        try:
            import math
            center_x, center_y = self.local_x, self.local_y
            
            line_items = self.canvas.find_withtag('rotating_line')
            for item in line_items:
                self.canvas.delete(item)
            
            for i in range(12):
                base_angle = i * 30 + angle
                rad = math.radians(base_angle)
                x1 = center_x + math.cos(rad) * 60
                y1 = center_y + math.sin(rad) * 60
                x2 = center_x + math.cos(rad) * 130
                y2 = center_y + math.sin(rad) * 130
                
                color_val = int(abs(math.sin(rad)) * 100 + 50)
                line_color = f"#{color_val:02x}{color_val:02x}ff"
                
                self.canvas.create_line(
                    x1, y1, x2, y2,
                    fill=line_color, width=2, tags='rotating_line'
                )
            
            new_angle = (angle + 1) % 360
            self.overlay.after(30, lambda: self.rotation_animation(new_angle))
            
        except:
            pass
    def radar_sweep_animation(self, angle=0):
        if not hasattr(self, 'canvas') or not self.canvas:
            return
            
        try:
            import math
            center_x, center_y = self.local_x, self.local_y
            radius = 160
            
            self.canvas.delete('radar_sweep')
            
            rad = math.radians(angle)
            x1 = center_x + math.cos(rad) * 20
            y1 = center_y + math.sin(rad) * 20
            x2 = center_x + math.cos(rad) * radius
            y2 = center_y + math.sin(rad) * radius
            
            self.canvas.create_line(
                x1, y1, x2, y2,
                fill='#00ffff', width=3, tags='radar_sweep'
            )
            
            for i in range(5):
                trail_angle = angle - (i + 1) * 5
                trail_rad = math.radians(trail_angle)
                tx1 = center_x + math.cos(trail_rad) * 20
                ty1 = center_y + math.sin(trail_rad) * 20
                tx2 = center_x + math.cos(trail_rad) * radius
                ty2 = center_y + math.sin(trail_rad) * radius
                
                alpha = 1.0 - (i * 0.2)
                trail_color = f"#00{int(255*alpha):02x}{int(255*alpha):02x}"
                
                self.canvas.create_line(
                    tx1, ty1, tx2, ty2,
                    fill=trail_color, width=max(1, 3-i), tags='radar_sweep'
                )
            
            new_angle = (angle + 3) % 360
            self.overlay.after(50, lambda: self.radar_sweep_animation(new_angle))
            
        except:
            pass
    
    def data_stream_animation(self):
        if not hasattr(self, 'canvas') or not self.canvas:
            return
            
        try:
            import random
            import math
            center_x, center_y = self.local_x, self.local_y
            
            self.canvas.delete('data_stream')
            
            for _ in range(30):
                angle = random.randint(0, 360)
                distance = random.randint(40, 150)
                rad = math.radians(angle)
                dx = center_x + math.cos(rad) * distance
                dy = center_y + math.sin(rad) * distance
                
                dot_size = random.randint(1, 2)
                colors = ['#00ffff', '#ff00ff', '#ffff00', '#00ff00']
                dot_color = random.choice(colors)
                
                self.canvas.create_oval(
                    dx - dot_size, dy - dot_size,
                    dx + dot_size, dy + dot_size,
                    fill=dot_color, outline='', tags='data_stream'
                )
                
                trail_distance = distance - 5
                if trail_distance > 30:
                    trail_rad = math.radians(angle + 5)
                    tx = center_x + math.cos(trail_rad) * trail_distance
                    ty = center_y + math.sin(trail_rad) * trail_distance
                    
                    self.canvas.create_oval(
                        tx - 1, ty - 1, tx + 1, ty + 1,
                        fill=dot_color, outline='', tags='data_stream'
                    )
            
            self.overlay.after(100, self.data_stream_animation)
            
        except:
            pass
    
    def pulse_ring_animation(self, radius=50):
        if not hasattr(self, 'canvas') or not self.canvas:
            return
            
        try:
            center_x, center_y = self.local_x, self.local_y
            
            self.canvas.delete('pulse_ring')
            
            max_radius = 160
            if radius < max_radius:
                alpha = 1.0 - (radius / max_radius)
                color_val = int(255 * alpha)
                ring_color = f"#{color_val:02x}{color_val:02x}ff"
                
                self.canvas.create_oval(
                    center_x - radius, center_y - radius,
                    center_x + radius, center_y + radius,
                    fill='', outline=ring_color, width=2,
                    tags='pulse_ring'
                )
                
                new_radius = radius + 3
                self.overlay.after(30, lambda: self.pulse_ring_animation(new_radius))
            else:
                self.overlay.after(500, lambda: self.pulse_ring_animation(50))
            
        except:
            pass
    def simple_border_pulse(self, alpha=0.3, growing=True):
        if not hasattr(self, 'canvas') or not self.canvas:
            return
            
        try:
            center_x, center_y = self.local_x, self.local_y
            radius = 160
            
            self.canvas.delete('pulse_border')
            
            color_intensity = int(74 + (157 - 74) * alpha)
            pulse_color = f"#{color_intensity:02x}{color_intensity + 30:02x}ff"
            
            self.canvas.create_oval(
                center_x - radius - 2, center_y - radius - 2,
                center_x + radius + 2, center_y + radius + 2,
                fill='', outline=pulse_color, width=3,
                tags='pulse_border'
            )
            
            if growing:
                new_alpha = alpha + 0.03
                if new_alpha > 0.8:
                    new_alpha = 0.8
                    growing = False
            else:
                new_alpha = alpha - 0.03
                if new_alpha < 0.3:
                    new_alpha = 0.3
                    growing = True
            
            self.overlay.after(100, lambda: self.simple_border_pulse(new_alpha, growing))
            
        except:
            pass
    def test_mes_communication(self):
        try:
            env_setting = load_data('environment', 'office')
            
            if env_setting == 'office':
                mes_server = "192.168.0.250"
                mes_port = 14333
            else:
                mes_server = "10.10.10.10"
                mes_port = 14333
            
            import socket
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(5)
            
            try:
                result = sock.connect_ex((mes_server, mes_port))
                sock.close()
                return result == 0
            except Exception:
                return False
            
        except Exception as e:
            logger.error(f"MES communication test error: {e}")
            return False

    def auto_temp_cleanup(self):
        logger.info("Auto temp cleanup initiated")
        self.close_menu()
        messagebox.showinfo("자동 임시파일 삭제", "임시파일 삭제를 시작합니다.")
    
    def auto_registry_cleanup(self):
        logger.info("Auto registry cleanup initiated")
        self.close_menu()
        messagebox.showinfo("자동 레지스트리 정리", "레지스트리 정리를 시작합니다.")
    
    def auto_system_optimize(self):
        logger.info("Auto system optimize initiated")
        self.close_menu()
        messagebox.showinfo("자동 시스템 최적화", "시스템 최적화를 시작합니다.")
    
    def auto_virus_scan(self):
        logger.info("Auto virus scan initiated")
        self.close_menu()
        messagebox.showinfo("자동 바이러스 검사", "바이러스 검사를 시작합니다.")


def setup_input_listener():
    use_basic = False
    try:
        from pynput import mouse as pynput_mouse
        from pynput import keyboard as pynput_keyboard
        use_pynput = True
        logger.info("Using pynput library for input handling")
    except ImportError:
        try:
            import mouse
            import keyboard
            use_pynput = False
            logger.info("Using mouse/keyboard libraries for input handling")
        except ImportError as e:
            logger.warning(f"Failed to import mouse/keyboard libraries: {e}")
            logger.info("Will use basic tkinter mouse events only")
            use_pynput = False
            use_basic = True
    
    radial_menu = RadialMenu()
    hotkey_type = load_data('hotkey_type', 'mouse_wheel')
    
    def show_radial_menu():
        if use_basic:
            import tkinter as tk
            root = tk.Tk()
            x = root.winfo_screenwidth() // 2
            y = root.winfo_screenheight() // 2
            root.destroy()
        elif use_pynput:
            controller = pynput_mouse.Controller()
            x, y = controller.position
        else:
            x, y = mouse.get_position()
        
        logger.info(f"Hotkey triggered at ({x}, {y}) - type: {hotkey_type}")
        radial_menu.create_menu(x, y)
    
    if use_basic:
        logger.warning("Using basic mode - only mouse wheel click through manual trigger")
        logger.info("Please use the settings button to manually trigger the menu")
    elif use_pynput:
        if hotkey_type == 'mouse_wheel':
            def on_click(x, y, button, pressed):
                if button == pynput_mouse.Button.middle and pressed:
                    show_radial_menu()
            
            mouse_listener = pynput_mouse.Listener(on_click=on_click)
            mouse_listener.start()
            logger.info("Pynput mouse wheel click listener setup completed")
            
        elif hotkey_type in ['f1', 'f2', 'f3', 'f4']:
            from pynput.keyboard import Key, KeyCode
            key_map = {
                'f1': Key.f1,
                'f2': Key.f2, 
                'f3': Key.f3,
                'f4': Key.f4
            }
            target_key = key_map[hotkey_type]
            
            def on_press(key):
                if key == target_key:
                    show_radial_menu()
            
            kbd_listener = pynput_keyboard.Listener(on_press=on_press)
            kbd_listener.start()
            logger.info(f"Pynput {hotkey_type.upper()} key listener setup completed")
            
        elif hotkey_type == 'click_status':
            logger.info("Status window click mode - no global listener needed")
            
        else:
            def on_click(x, y, button, pressed):
                if button == pynput_mouse.Button.middle and pressed:
                    show_radial_menu()
            
            mouse_listener = pynput_mouse.Listener(on_click=on_click)
            mouse_listener.start()
            logger.info("Pynput default mouse wheel click listener setup completed")
            
    else:
        if hotkey_type == 'mouse_wheel':
            mouse.on_middle_click(show_radial_menu)
            logger.info("Mouse wheel click listener setup completed")
            
        elif hotkey_type in ['f1', 'f2', 'f3', 'f4']:
            key_name = hotkey_type.upper()
            keyboard.on_press_key(key_name, lambda _: show_radial_menu())
            logger.info(f"{key_name} key listener setup completed")
            
        elif hotkey_type == 'click_status':
            logger.info("Status window click mode - no global listener needed")
            
        else:
            mouse.on_middle_click(show_radial_menu)
            logger.info("Default mouse wheel click listener setup completed")
    
    return radial_menu


def setup_input_listener_for_instance(radial_menu, hotkey_type):
    logger.info(f"Setting up input listener for hotkey type: {hotkey_type}")
    
    if hasattr(radial_menu, 'cleanup_listeners'):
        radial_menu.cleanup_listeners()
    
    try:
        from pynput import mouse as pynput_mouse
        from pynput import keyboard as pynput_keyboard
        use_pynput = True
        logger.info("Using pynput library for input handling")
    except ImportError:
        try:
            import mouse
            import keyboard
            use_pynput = False
            logger.info("Using mouse/keyboard libraries for input handling")
        except ImportError as e:
            logger.error(f"Failed to import input libraries: {e}")
            return
    
    def show_radial_menu():
        if use_pynput:
            controller = pynput_mouse.Controller()
            x, y = controller.position
        else:
            x, y = mouse.get_position()
        
        logger.info(f"Hotkey triggered at ({x}, {y}) - type: {hotkey_type}")
        radial_menu.create_menu(x, y)
    
    if use_pynput:
        if hotkey_type == 'mouse_wheel':
            def on_click(x, y, button, pressed):
                if button == pynput_mouse.Button.middle and pressed:
                    show_radial_menu()
            
            mouse_listener = pynput_mouse.Listener(on_click=on_click)
            mouse_listener.start()
            radial_menu.active_listeners.append(mouse_listener)
            logger.info("Pynput mouse wheel click listener setup completed")
            
        elif hotkey_type in ['f1', 'f2', 'f3', 'f4']:
            from pynput.keyboard import Key, KeyCode
            key_map = {
                'f1': Key.f1,
                'f2': Key.f2, 
                'f3': Key.f3,
                'f4': Key.f4
            }
            target_key = key_map[hotkey_type]
            
            def on_press(key):
                if key == target_key:
                    show_radial_menu()
            
            kbd_listener = pynput_keyboard.Listener(on_press=on_press)
            kbd_listener.start()
            radial_menu.active_listeners.append(kbd_listener)
            logger.info(f"Pynput {hotkey_type.upper()} key listener setup completed")
            
        elif hotkey_type == 'click_status':
            logger.info("Status window click mode - setting up PyQt click listener if needed")
            if hasattr(radial_menu, 'status_window') and radial_menu.status_window:
                status_window = radial_menu.status_window
                if hasattr(status_window, 'qt_widget') and status_window.qt_widget:
                    x = status_window.qt_widget.x()
                    y = status_window.qt_widget.y() 
                    width = status_window.qt_widget.width()
                    height = status_window.qt_widget.height()
                    if hasattr(status_window, 'setup_pyqt_click_listener'):
                        status_window.setup_pyqt_click_listener(x, y, width, height)
            
        else:
            def on_click(x, y, button, pressed):
                if button == pynput_mouse.Button.middle and pressed:
                    show_radial_menu()
            
            mouse_listener = pynput_mouse.Listener(on_click=on_click)
            mouse_listener.start()
            radial_menu.active_listeners.append(mouse_listener)
            logger.info("Pynput default mouse wheel click listener setup completed")
            
    else:
        if hotkey_type == 'mouse_wheel':
            mouse.on_middle_click(show_radial_menu)
        elif hotkey_type in ['f1', 'f2', 'f3', 'f4']:
            keyboard.on_press_key(hotkey_type.upper(), lambda _: show_radial_menu())
        elif hotkey_type == 'ctrl_space':
            try:
                keyboard.add_hotkey('ctrl+space', show_radial_menu, suppress=True)
            except Exception as e:
                logger.error(f"Failed to register Ctrl+Space: {e}")
                def check_ctrl_space():
                    if keyboard.is_pressed('ctrl') and keyboard.is_pressed('space'):
                        show_radial_menu()
                keyboard.on_press_key('space', lambda _: check_ctrl_space())
        elif hotkey_type == 'alt_space':
            try:
                keyboard.add_hotkey('alt+space', show_radial_menu, suppress=True)
            except Exception as e:
                logger.error(f"Failed to register Alt+Space: {e}")
                def check_alt_space():
                    if keyboard.is_pressed('alt') and keyboard.is_pressed('space'):
                        show_radial_menu()
                keyboard.on_press_key('space', lambda _: check_alt_space())
        elif hotkey_type == 'click_status':
            logger.info("Status window click mode - no global listener needed")
        else:
            mouse.on_middle_click(show_radial_menu)
    
    def create_auto_action_buttons(self):
        logger.info("Creating auto action buttons...")
        
        button_info = [
            {"text": "네트워크 Fix", "color": "#e74c3c", "command": self.network_fix},
            {"text": "브라우저 Fix", "color": "#3498db", "command": self.browser_fix},
            {"text": "프린터 Fix", "color": "#2ecc71", "command": self.printer_fix},
            {"text": "MES Fix", "color": "#f39c12", "command": self.mes_fix}
        ]
        
        radius = 110
        button_size = 70
        
        center_size = 25
        center_circle = self.canvas.create_oval(
            self.local_x - center_size,
            self.local_y - center_size,
            self.local_x + center_size,
            self.local_y + center_size,
            fill='#34495e',
            outline='white',
            width=2
        )
        
        self.canvas.create_text(
            self.local_x,
            self.local_y,
            text="자동조치",
            font=('Arial', 9, 'bold'),
            fill='white'
        )
        
        self.buttons = []
        for i, btn_info in enumerate(button_info):
            angle = (i * (360 / len(button_info)) - 90) * math.pi / 180
            
            btn_x = self.local_x + radius * math.cos(angle)
            btn_y = self.local_y + radius * math.sin(angle)
            
            x1 = btn_x - button_size/2
            y1 = btn_y - button_size/2
            x2 = btn_x + button_size/2
            y2 = btn_y + button_size/2
            
            btn = self.canvas.create_oval(
                x1, y1, x2, y2,
                fill=btn_info["color"],
                outline='white',
                width=2,
                tags=f"button_{i}"
            )
            
            text = self.canvas.create_text(
                btn_x, btn_y,
                text=btn_info['text'],
                font=('Arial', 9, 'bold'),
                fill='white',
                tags=f"button_{i}"
            )
            
            self.buttons.append({
                "id": btn,
                "text_id": text,
                "x": btn_x,
                "y": btn_y,
                "size": button_size,
                "command": btn_info["command"],
                "color": btn_info["color"],
                "tag": f"button_{i}"
            })

            # 헬퍼 메서드를 사용하여 이벤트 바인딩
            self._bind_button_events(i, btn_info["command"])

            logger.info(f"Auto action button {i} ({btn_info['text']}) created at ({btn_x}, {btn_y})")
        
        logger.info(f"Created {len(self.buttons)} auto action buttons")
    
    def network_fix(self):
        logger.info("Network Fix initiated")
        self.close_menu()

        # 확인 대화상자 표시
        response = messagebox.askyesno(
            "네트워크 Fix 확인",
            "네트워크 자동 복구를 시작하시겠습니까?\n\n"
            "이 작업은 다음을 수행합니다:\n"
            "• 네트워크 어댑터 재시작\n"
            "• DNS 캐시 초기화\n"
            "• IP 설정 갱신\n"
            "• MES 통신 테스트"
        )

        # 사용자가 '아니오'를 선택한 경우 작업 취소
        if not response:
            logger.info("Network Fix cancelled by user")
            return

        def run_network_fix():
            try:
                progress_window = tk.Toplevel()
                progress_window.title("네트워크 Fix")
                
                RadialMenu.active_progress_windows.append(progress_window)
                
                def on_window_close():
                    try:
                        RadialMenu.active_progress_windows.remove(progress_window)
                    except:
                        pass
                    progress_window.destroy()
                
                progress_window.protocol("WM_DELETE_WINDOW", on_window_close)
                progress_window.geometry("500x400")
                progress_window.resizable(False, False)
                progress_window.transient(self.overlay if self.overlay else None)
                progress_window.grab_set()
                
                text_area = scrolledtext.ScrolledText(progress_window, height=20, width=60)
                text_area.pack(fill='both', expand=True, padx=10, pady=10)
                
                def log_progress(message):
                    text_area.insert(tk.END, f"{message}\n")
                    text_area.see(tk.END)
                    progress_window.update()
                
                log_progress("🔧 네트워크 Fix 시작...")
                log_progress("=" * 50)
                
                is_online = self.check_internet_connection()
                if is_online:
                    log_progress("✅ 현재 인터넷 연결 상태: 온라인")
                    log_progress("⚠️  오프라인이 아니지만 강제로 네트워크 복구를 진행합니다.")
                else:
                    log_progress("❌ 현재 인터넷 연결 상태: 오프라인")
                    log_progress("🔄 네트워크 자동 복구를 시작합니다.")
                
                log_progress("\n🚀 6단계 네트워크 복구 과정 시작...")
                
                recovery_success = self.auto_network_recovery()
                
                if recovery_success:
                    log_progress("\n✅ 네트워크 복구 완료!")
                else:
                    log_progress("\n❌ 네트워크 복구 실패")
                
                log_progress("\n🔍 MES 통신 테스트 시작...")
                mes_result = self.test_mes_communication()
                
                if mes_result:
                    log_progress("✅ MES 통신 테스트 성공! 정상적으로 통신되고 있습니다.")
                else:
                    log_progress("❌ MES 통신 테스트 실패. MES 서버 연결을 확인해주세요.")
                
                log_progress("\n" + "=" * 50)
                log_progress("🎉 네트워크 Fix 완료!")
                
                button_frame = tk.Frame(progress_window)
                button_frame.pack(pady=10)
                
                close_button = tk.Button(button_frame, text="닫기", 
                                       command=on_window_close,
                                       bg='#3498db', fg='white', font=('Arial', 10, 'bold'))
                close_button.pack()
                
            except Exception as e:
                logger.error(f"Network fix error: {e}")
                messagebox.showerror("오류", f"네트워크 Fix 중 오류가 발생했습니다:\n{str(e)}")
        
        import threading
        threading.Thread(target=run_network_fix, daemon=True).start()
    
    def browser_fix(self):
        logger.info("Browser Fix initiated")
        self.close_menu()
        
        result = messagebox.askyesno(
            "브라우저 Fix 확인",
            "브라우저 Fix를 실행하시겠습니까?\n\n"
            "다음 작업이 수행됩니다:\n"
            "• Chrome 브라우저 캐시 및 쿠키 삭제\n"
            "• Edge 브라우저 캐시 및 쿠키 삭제\n"
            "• 임시 인터넷 파일 정리\n\n"
            "진행하시겠습니까?"
        )
        
        if not result:
            logger.info("Browser Fix cancelled by user")
            return
        
        def run_browser_fix():
            try:
                progress_window = tk.Toplevel()
                progress_window.title("브라우저 Fix")
                
                RadialMenu.active_progress_windows.append(progress_window)
                
                def on_window_close():
                    try:
                        RadialMenu.active_progress_windows.remove(progress_window)
                    except:
                        pass
                    progress_window.destroy()
                
                progress_window.protocol("WM_DELETE_WINDOW", on_window_close)
                progress_window.geometry("500x300")
                progress_window.resizable(False, False)
                progress_window.transient(self.overlay if self.overlay else None)
                progress_window.grab_set()
                
                text_area = scrolledtext.ScrolledText(progress_window, height=15, width=60)
                text_area.pack(fill='both', expand=True, padx=10, pady=10)
                
                def log_progress(message):
                    text_area.insert(tk.END, f"{message}\n")
                    text_area.see(tk.END)
                    progress_window.update()
                
                log_progress("🌐 브라우저 Fix 시작...")
                log_progress("=" * 50)
                
                log_progress("🔄 브라우저 프로세스 종료 중...")
                
                browser_processes = ['chrome.exe', 'msedge.exe', 'iexplore.exe']
                for process_name in browser_processes:
                    try:
                        killed_any = False
                        for proc in psutil.process_iter(['pid', 'name']):
                            try:
                                if proc.info['name'] and proc.info['name'].lower() == process_name.lower():
                                    proc.terminate()
                                    killed_any = True
                                    log_progress(f"✅ {process_name} 프로세스 종료됨 (PID: {proc.info['pid']})")
                            except (psutil.NoSuchProcess, psutil.AccessDenied):
                                pass

                        if not killed_any:
                            log_progress(f"ℹ️  {process_name} 프로세스가 실행 중이 아님")
                    except Exception as e:
                        log_progress(f"⚠️  {process_name} 종료 중 오류: {e}")
                
                time.sleep(2)
                
                log_progress("\n🗑️  Chrome 캐시/쿠키 삭제 중...")
                chrome_paths = [
                    os.path.expanduser("~\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Cache"),
                    os.path.expanduser("~\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Cookies"),
                    os.path.expanduser("~\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Web Data")
                ]
                
                for path in chrome_paths:
                    try:
                        if os.path.exists(path):
                            if os.path.isdir(path):
                                import shutil
                                shutil.rmtree(path, ignore_errors=True)
                                log_progress(f"✅ Chrome: {os.path.basename(path)} 폴더 삭제됨")
                            else:
                                os.remove(path)
                                log_progress(f"✅ Chrome: {os.path.basename(path)} 파일 삭제됨")
                        else:
                            log_progress(f"ℹ️  Chrome: {os.path.basename(path)} 없음")
                    except Exception as e:
                        log_progress(f"⚠️  Chrome {os.path.basename(path)} 삭제 실패: {e}")
                
                log_progress("\n🗑️  Edge 캐시/쿠키 삭제 중...")
                edge_paths = [
                    os.path.expanduser("~\\AppData\\Local\\Microsoft\\Edge\\User Data\\Default\\Cache"),
                    os.path.expanduser("~\\AppData\\Local\\Microsoft\\Edge\\User Data\\Default\\Cookies"),
                    os.path.expanduser("~\\AppData\\Local\\Microsoft\\Edge\\User Data\\Default\\Web Data")
                ]
                
                for path in edge_paths:
                    try:
                        if os.path.exists(path):
                            if os.path.isdir(path):
                                import shutil
                                shutil.rmtree(path, ignore_errors=True)
                                log_progress(f"✅ Edge: {os.path.basename(path)} 폴더 삭제됨")
                            else:
                                os.remove(path)
                                log_progress(f"✅ Edge: {os.path.basename(path)} 파일 삭제됨")
                        else:
                            log_progress(f"ℹ️  Edge: {os.path.basename(path)} 없음")
                    except Exception as e:
                        log_progress(f"⚠️  Edge {os.path.basename(path)} 삭제 실패: {e}")
                
                log_progress("\n" + "=" * 50)
                log_progress("🎉 브라우저 Fix 완료!")
                log_progress("ℹ️  브라우저를 다시 시작하면 캐시/쿠키가 초기화됩니다.")
                
                button_frame = tk.Frame(progress_window)
                button_frame.pack(pady=10)
                
                close_button = tk.Button(button_frame, text="닫기", 
                                       command=on_window_close,
                                       bg='#3498db', fg='white', font=('Arial', 10, 'bold'))
                close_button.pack()
                
            except Exception as e:
                logger.error(f"Browser fix error: {e}")
                messagebox.showerror("오류", f"브라우저 Fix 중 오류가 발생했습니다:\n{str(e)}")
        
        import threading
        threading.Thread(target=run_browser_fix, daemon=True).start()
    

    def printer_fix(self):
        logger.info("Printer Fix initiated")
        self.close_menu()

        # 확인 대화상자 표시
        response = messagebox.askyesno(
            "프린터 Fix 확인",
            "프린터 자동 복구를 시작하시겠습니까?\n\n"
            "이 작업은 다음을 수행합니다:\n"
            "• 프린터 스풀러 서비스 재시작\n"
            "• 인쇄 대기열 초기화\n"
            "• 프린터 드라이버 상태 확인\n"
            "• 프린터 연결 테스트"
        )

        # 사용자가 '아니오'를 선택한 경우 작업 취소
        if not response:
            logger.info("Printer Fix cancelled by user")
            return

        def run_printer_fix():
            try:
                progress_window = tk.Toplevel()
                progress_window.title("프린터 Fix")
                
                RadialMenu.active_progress_windows.append(progress_window)
                
                def on_window_close():
                    try:
                        RadialMenu.active_progress_windows.remove(progress_window)
                    except:
                        pass
                    progress_window.destroy()
                
                progress_window.protocol("WM_DELETE_WINDOW", on_window_close)
                progress_window.geometry("500x400")
                progress_window.resizable(False, False)
                progress_window.transient(self.overlay if self.overlay else None)
                progress_window.grab_set()
                
                text_area = scrolledtext.ScrolledText(progress_window, height=20, width=60)
                text_area.pack(fill='both', expand=True, padx=10, pady=10)
                
                def log_progress(message):
                    text_area.insert(tk.END, f"{message}\n")
                    text_area.see(tk.END)
                    progress_window.update()
                
                log_progress("🖨️  프린터 Fix 시작...")
                log_progress("=" * 50)
                
                log_progress("🔧 프린터 레지스트리 설정 수정 중...")
                reg_command = [
                    'REG', 'ADD', 
                    'HKLM\\System\\CurrentControlSet\\Control\\Print',
                    '/v', 'RpcAuthnLevelPrivacyEnabled',
                    '/t', 'REG_DWORD',
                    '/d', '0',
                    '/f'
                ]
                
                try:
                    result = subprocess.run(reg_command, capture_output=True, text=True, timeout=30,
                         creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0)
                    if result.returncode == 0:
                        log_progress("✅ 레지스트리 설정 수정 완료")
                    else:
                        log_progress(f"⚠️  레지스트리 수정 경고: {result.stderr.strip()}")
                except Exception as e:
                    log_progress(f"❌ 레지스트리 수정 실패: {e}")
                
                log_progress("\n🔄 Print Spooler 서비스 중지 중...")
                try:
                    result = subprocess.run(['net', 'stop', 'spooler'], 
                                          capture_output=True, text=True, timeout=30,
                         creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0)
                    if result.returncode == 0:
                        log_progress("✅ Print Spooler 서비스 중지됨")
                    else:
                        log_progress(f"⚠️  Spooler 중지 경고: {result.stderr.strip()}")
                except Exception as e:
                    log_progress(f"❌ Spooler 중지 실패: {e}")
                
                time.sleep(3)
                
                log_progress("\n🚀 Print Spooler 서비스 시작 중...")
                try:
                    result = subprocess.run(['net', 'start', 'spooler'], 
                                          capture_output=True, text=True, timeout=30,
                         creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0)
                    if result.returncode == 0:
                        log_progress("✅ Print Spooler 서비스 시작됨")
                    else:
                        log_progress(f"⚠️  Spooler 시작 경고: {result.stderr.strip()}")
                except Exception as e:
                    log_progress(f"❌ Spooler 시작 실패: {e}")
                
                log_progress("\n🔧 추가 프린터 장애 해결 작업...")
                
                spooler_path = "C:\\Windows\\System32\\spool\\PRINTERS"
                try:
                    if os.path.exists(spooler_path):
                        import glob
                        spool_files = glob.glob(os.path.join(spooler_path, "*"))
                        if spool_files:
                            for file in spool_files:
                                try:
                                    os.remove(file)
                                    log_progress(f"✅ 대기열 파일 삭제: {os.path.basename(file)}")
                                except:
                                    pass
                        else:
                            log_progress("ℹ️  프린터 대기열이 이미 비어있습니다")
                    else:
                        log_progress("ℹ️  프린터 대기열 폴더를 찾을 수 없습니다")
                except Exception as e:
                    log_progress(f"⚠️  대기열 정리 중 오류: {e}")
                
                log_progress("\n" + "=" * 50)
                log_progress("🎉 프린터 Fix 완료!")
                log_progress("ℹ️  프린터 문제가 해결되었습니다. 프린터를 다시 사용해보세요.")
                
                log_progress("\n📋 장애 지속 시 추천 명령어:")
                log_progress("1. 프린터 드라이버 재설치")
                log_progress("2. rundll32 printui.dll,PrintUIEntry /k (프린터 새로고침)")
                log_progress("3. sfc /scannow (시스템 파일 검사)")
                
                button_frame = tk.Frame(progress_window)
                button_frame.pack(pady=10)
                
                close_button = tk.Button(button_frame, text="닫기", 
                                       command=on_window_close,
                                       bg='#2ecc71', fg='white', font=('Arial', 10, 'bold'))
                close_button.pack()
                
            except Exception as e:
                logger.error(f"Printer fix error: {e}")
                messagebox.showerror("오류", f"프린터 Fix 중 오류가 발생했습니다:\n{str(e)}")
        
        import threading
        threading.Thread(target=run_printer_fix, daemon=True).start()


    def mes_fix(self):
        logger.info("MES Fix initiated")
        self.close_menu()

        # 확인 대화상자 표시
        response = messagebox.askyesno(
            "MES Fix 확인",
            "MES 시스템 복구를 시작하시겠습니까?\n\n"
            "이 작업은 다음을 수행합니다:\n"
            "• MSCOMM32.ocx 자동 설치 및 등록\n"
            "• MES 서버 IP 설정 자동 구성\n"
            "• MES 통신 상태 확인\n"
            "• 연결 오류 자동 복구"
        )

        # 사용자가 '아니오'를 선택한 경우 작업 취소
        if not response:
            logger.info("MES Fix cancelled by user")
            return

        def run_mes_fix():
            try:
                progress_window = tk.Toplevel()
                progress_window.title("MES Fix")
                
                RadialMenu.active_progress_windows.append(progress_window)
                
                def on_window_close():
                    try:
                        RadialMenu.active_progress_windows.remove(progress_window)
                    except:
                        pass
                    progress_window.destroy()
                
                progress_window.protocol("WM_DELETE_WINDOW", on_window_close)
                progress_window.geometry("500x500")
                progress_window.resizable(False, False)
                progress_window.transient(self.overlay if self.overlay else None)
                progress_window.grab_set()

                text_area = scrolledtext.ScrolledText(progress_window, height=26, width=60)
                text_area.pack(fill='both', expand=True, padx=10, pady=10)
                
                def log_progress(message):
                    text_area.insert(tk.END, f"{message}\n")
                    text_area.see(tk.END)
                    progress_window.update()
                
                log_progress("🏭 MES Fix 시작...")
                log_progress("=" * 50)

                log_progress("\n[1/2] MSCOMM32.ocx 설치 및 등록")
                log_progress("=" * 50)

                import ctypes
                import tempfile
                import shutil

                try:
                    import requests
                except ImportError:
                    log_progress("⚠️  requests 모듈이 없습니다. 다운로드를 건너뜁니다.")
                    requests = None

                is_admin = ctypes.windll.shell32.IsUserAnAdmin()
                if not is_admin:
                    log_progress("⚠️  관리자 권한이 필요합니다!")
                    log_progress("   프로그램을 관리자 권한으로 실행해주세요.")
                else:
                    log_progress("✅ 관리자 권한 확인됨")

                url = 'https://file.geomedical.kr/file/MSCOMM32.ocx'
                dest_file = os.path.join(os.environ['windir'], 'SysWOW64', 'MSCOMM32.ocx')

                if os.path.exists(dest_file):
                    log_progress(f"ℹ️  MSCOMM32.ocx가 이미 설치되어 있습니다.")
                    log_progress(f"   위치: {dest_file}")
                else:
                    log_progress(f"\n📥 파일 다운로드 중...")
                    log_progress(f"   URL: {url}")

                    temp_file = None
                    if requests:
                        try:
                            temp_file = os.path.join(tempfile.gettempdir(), 'MSCOMM32.ocx')

                            response_download = requests.get(url, timeout=30)
                            response_download.raise_for_status()

                            with open(temp_file, 'wb') as f:
                                f.write(response_download.content)

                            log_progress(f"✅ 다운로드 완료: {temp_file}")

                        except Exception as e:
                            log_progress(f"❌ 다운로드 실패: {e}")
                            log_progress("   네트워크 연결을 확인해주세요.")
                            temp_file = None

                    if temp_file and os.path.exists(temp_file):
                        log_progress(f"\n📁 SysWOW64에 복사 중...")
                        log_progress(f"   대상: {dest_file}")

                        try:
                            shutil.copy2(temp_file, dest_file)
                            log_progress(f"✅ 파일 복사 완료")

                            log_progress(f"\n⚙️  regsvr32 등록 중...")

                            result = subprocess.run(
                                ['regsvr32.exe', '/s', dest_file],
                                capture_output=True,
                                timeout=10
                            )

                            if result.returncode == 0:
                                log_progress(f"✅ MSCOMM32.ocx 등록 완료")
                            else:
                                log_progress(f"⚠️  등록 실패 (코드: {result.returncode})")

                            try:
                                os.remove(temp_file)
                                log_progress(f"✅ 임시 파일 삭제 완료")
                            except:
                                pass

                        except Exception as e:
                            log_progress(f"❌ 파일 복사/등록 실패: {e}")
                            if not is_admin:
                                log_progress("   관리자 권한으로 다시 실행해주세요.")
                    else:
                        log_progress("❌ MSCOMM32.ocx 다운로드에 실패했습니다.")
                        log_progress("   수동으로 설치가 필요할 수 있습니다.")

                log_progress("\n" + "=" * 50)
                log_progress("[2/2] MES 서버 IP 설정")
                log_progress("=" * 50)

                env_setting = load_data('environment', 'office')
                log_progress(f"현재 사용 환경: {env_setting}")

                if env_setting == 'office':
                    target_ip = "192.168.0.250,14333"
                    env_name = "사무용"
                else:
                    target_ip = "10.10.10.10,14333"
                    env_name = "현장용"

                log_progress(f"설정할 IP: {target_ip} ({env_name})")

                def update_ini_file(file_path, file_description):
                    if not os.path.exists(file_path):
                        log_progress(f"⚠️  {file_description} 파일을 찾을 수 없습니다: {file_path}")
                        log_progress(f"   파일이 없으므로 건너뜁니다.")
                        return False

                    log_progress(f"\n📁 {file_description} 파일 발견: {file_path}")

                    backup_path = file_path + ".backup"
                    try:
                        shutil.copy2(file_path, backup_path)
                        log_progress(f"✅ 백업 파일 생성: {backup_path}")
                    except Exception as e:
                        log_progress(f"⚠️  백업 생성 실패: {e}")

                    log_progress(f"🔧 IP 설정 수정 중...")
                    try:
                        encodings_to_try = ['utf-8', 'cp949', 'euc-kr', 'latin-1']
                        lines = None
                        used_encoding = None

                        for encoding in encodings_to_try:
                            try:
                                with open(file_path, 'r', encoding=encoding) as f:
                                    lines = f.readlines()
                                    used_encoding = encoding
                                    log_progress(f"✅ 파일 인코딩 감지: {encoding}")
                                    break
                            except UnicodeDecodeError:
                                continue

                        if lines is None:
                            log_progress("❌ 파일 인코딩을 감지할 수 없습니다!")
                            return False

                        in_server_section = False
                        modified = False

                        for i, line in enumerate(lines):
                            line_stripped = line.strip()

                            if line_stripped == '[SERVER]':
                                in_server_section = True
                                log_progress("✅ [SERVER] 섹션 발견")
                                continue

                            if in_server_section:
                                if line_stripped.startswith('[') and line_stripped.endswith(']'):
                                    in_server_section = False
                                    continue

                                if line_stripped.startswith('IP='):
                                    old_ip = line_stripped[3:]
                                    lines[i] = f"IP={target_ip}\n"
                                    log_progress(f"✅ IP 설정 변경: {old_ip} → {target_ip}")
                                    modified = True
                                    break

                        if not modified:
                            log_progress("❌ IP= 설정을 찾을 수 없습니다!")
                            return False

                        with open(file_path, 'w', encoding=used_encoding) as f:
                            f.writelines(lines)

                        log_progress(f"✅ {file_description} 파일 저장 완료")
                        return True

                    except Exception as e:
                        log_progress(f"❌ 파일 수정 실패: {e}")
                        return False

                config_files = [
                    ("C:\\geopop\\commlib\\pf_profile.ini", "geopop pf_profile.ini"),
                    ("C:\\GEO_WORK\\commlib\\pf_profile.ini", "GEO_WORK pf_profile.ini"),
                    ("C:\\geomedical\\Geo_ERP\\Setup.ini", "Geo_ERP Setup.ini")
                ]

                success_count = 0
                total_count = len(config_files)

                for file_path, file_desc in config_files:
                    log_progress(f"\n{'─' * 50}")
                    if update_ini_file(file_path, file_desc):
                        success_count += 1
                
                log_progress("\n" + "=" * 50)
                log_progress("🎉 MES Fix 완료!")
                log_progress(f"ℹ️  MES 서버 IP가 {env_name} 환경에 맞게 설정되었습니다.")
                log_progress(f"ℹ️  처리 결과: {success_count}/{total_count} 파일 성공")
                if success_count == 0:
                    log_progress("⚠️  모든 설정 파일을 찾을 수 없습니다.")
                    log_progress("   MES 프로그램이 설치되어 있는지 확인하세요.")
                elif success_count < total_count:
                    log_progress(f"⚠️  일부 파일({total_count - success_count}개)을 처리하지 못했습니다.")
                    log_progress("   위의 로그를 확인하세요.")
                log_progress("ℹ️  MES 프로그램을 재시작하세요.")
                
                button_frame = tk.Frame(progress_window)
                button_frame.pack(pady=10)
                
                close_button = tk.Button(button_frame, text="닫기", 
                                       command=on_window_close,
                                       bg='#f39c12', fg='white', font=('Arial', 10, 'bold'))
                close_button.pack()
                
            except Exception as e:
                logger.error(f"MES fix error: {e}")
                messagebox.showerror("오류", f"MES Fix 중 오류가 발생했습니다:\n{str(e)}")
        
        import threading
        threading.Thread(target=run_mes_fix, daemon=True).start()



    def start_auto_action_animations(self):
        if not hasattr(self, 'canvas') or not self.canvas:
            return
            
        self.fade_in_animation()
        
        self.simple_border_pulse()
    
    def fade_in_animation(self, alpha=0.1):
        if not hasattr(self, 'overlay') or not self.overlay:
            return
            
        if alpha < 0.9:
            self.overlay.attributes('-alpha', alpha)
            self.overlay.after(20, lambda: self.fade_in_animation(alpha + 0.1))
        else:
            self.overlay.attributes('-alpha', 0.9)
    
    def pulse_animation(self, scale=1.0, growing=True):
        if not hasattr(self, 'canvas') or not self.canvas:
            return
            
        try:
            for i in range(4):
                button_tag = f'button_{i}_glow'
                items = self.canvas.find_withtag(button_tag)
                if items:
                    for item in items:
                        coords = self.canvas.coords(item)
                        if len(coords) == 4:
                            center_x = (coords[0] + coords[2]) / 2
                            center_y = (coords[1] + coords[3]) / 2
                            size = (coords[2] - coords[0]) / 2
                            
                            new_size = size * scale
                            self.canvas.coords(item,
                                center_x - new_size, center_y - new_size,
                                center_x + new_size, center_y + new_size)
            
            if growing:
                new_scale = scale + 0.02
                if new_scale > 1.1:
                    new_scale = 1.1
                    growing = False
            else:
                new_scale = scale - 0.02
                if new_scale < 0.9:
                    new_scale = 0.9
                    growing = True
            
            self.overlay.after(50, lambda: self.pulse_animation(new_scale, growing))
            
        except:
            pass
    
    def rotation_animation(self, angle=0):
        if not hasattr(self, 'canvas') or not self.canvas:
            return
            
        try:
            import math
            center_x, center_y = self.local_x, self.local_y
            
            line_items = self.canvas.find_withtag('rotating_line')
            for item in line_items:
                self.canvas.delete(item)
            
            for i in range(12):
                base_angle = i * 30 + angle
                rad = math.radians(base_angle)
                x1 = center_x + math.cos(rad) * 60
                y1 = center_y + math.sin(rad) * 60
                x2 = center_x + math.cos(rad) * 130
                y2 = center_y + math.sin(rad) * 130
                
                color_val = int(abs(math.sin(rad)) * 100 + 50)
                line_color = f"#{color_val:02x}{color_val:02x}ff"
                
                self.canvas.create_line(
                    x1, y1, x2, y2,
                    fill=line_color, width=2, tags='rotating_line'
                )
            
            new_angle = (angle + 1) % 360
            self.overlay.after(30, lambda: self.rotation_animation(new_angle))
            
        except:
            pass
    def test_mes_communication(self):
        try:
            env_setting = load_data('environment', 'office')
            
            if env_setting == 'office':
                mes_server = "192.168.0.250"
                mes_port = 14333
            else:
                mes_server = "10.10.10.10"
                mes_port = 14333
            
            import socket
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(5)
            
            try:
                result = sock.connect_ex((mes_server, mes_port))
                sock.close()
                return result == 0
            except Exception:
                return False
            
        except Exception as e:
            logger.error(f"MES communication test error: {e}")
            return False

    def auto_temp_cleanup(self):
        logger.info("Auto temp cleanup initiated")
        self.close_menu()
        messagebox.showinfo("자동 임시파일 삭제", "임시파일 삭제를 시작합니다.")
    
    def auto_registry_cleanup(self):
        logger.info("Auto registry cleanup initiated")
        self.close_menu()
        messagebox.showinfo("자동 레지스트리 정리", "레지스트리 정리를 시작합니다.")
    
    def auto_system_optimize(self):
        logger.info("Auto system optimize initiated")
        self.close_menu()
        messagebox.showinfo("자동 시스템 최적화", "시스템 최적화를 시작합니다.")
    
    def auto_virus_scan(self):
        logger.info("Auto virus scan initiated")
        self.close_menu()
        messagebox.showinfo("자동 바이러스 검사", "바이러스 검사를 시작합니다.")


def force_exit_handler(signum=None, frame=None):
    logger.info(f"Received exit signal: {signum}")
    cleanup_all_ui()
    logger.info("Force exit handler completed")
    os._exit(0)

def atexit_handler():
    logger.info("AtExit handler called")
    cleanup_all_ui()
    logger.info("AtExit handler completed")

def cleanup_all_ui():
    global radial_menu, status_window

    logger.info("Starting comprehensive UI resource cleanup...")
    
    if 'radial_menu' in globals() and radial_menu:
        try:
            logger.info("Cleaning up RadialMenu...")
            
            if hasattr(radial_menu, 'stop_network_monitor'):
                radial_menu.stop_network_monitor()
            
            if hasattr(radial_menu, 'close_menu'):
                radial_menu.close_menu()
            
            if hasattr(radial_menu, 'cleanup_listeners'):
                radial_menu.cleanup_listeners()
            
            if hasattr(radial_menu, 'status_window') and radial_menu.status_window:
                logger.info("Destroying RadialMenu's StatusWindow...")
                radial_menu.status_window.destroy()
                radial_menu.status_window = None
            
            if hasattr(radial_menu, 'settings_window') and radial_menu.settings_window:
                if hasattr(radial_menu.settings_window, 'settings_window') and radial_menu.settings_window.settings_window:
                    try:
                        radial_menu.settings_window.settings_window.destroy()
                    except:
                        pass
                radial_menu.settings_window = None
                
            if hasattr(radial_menu, 'active_progress_windows'):
                for window in list(radial_menu.active_progress_windows):
                    try:
                        window.destroy()
                    except:
                        pass
                radial_menu.active_progress_windows.clear()
                
            logger.info("RadialMenu cleanup completed")
                
        except Exception as e:
            logger.error(f"Error cleaning up RadialMenu: {e}")
    
    if 'status_window' in globals() and status_window:
        try:
            logger.info("Cleaning up independent StatusWindow...")
            status_window.destroy()
        except Exception as e:
            logger.error(f"Error cleaning up StatusWindow: {e}")
    
    try:
        import tkinter as tk
        logger.info("Cleaning up all Tkinter windows...")

        if hasattr(tk, '_default_root') and tk._default_root:
            try:
                # 애플리케이션이 이미 파괴되었는지 확인
                tk._default_root.winfo_exists()

                children = list(tk._default_root.winfo_children())
                for child in children:
                    if isinstance(child, tk.Toplevel):
                        try:
                            if child.winfo_exists():
                                child.destroy()
                                logger.info(f"Destroyed Toplevel window: {child}")
                        except Exception as e:
                            logger.debug(f"Error destroying Toplevel: {e}")

                # 남은 모든 윈도우 정리
                all_windows = tk._default_root.winfo_children() if tk._default_root else []
                for window in all_windows:
                    try:
                        if hasattr(window, 'winfo_exists') and window.winfo_exists():
                            if hasattr(window, 'destroy'):
                                window.destroy()
                    except:
                        pass
            except tk.TclError:
                # 애플리케이션이 이미 파괴됨 - 정상적인 상황
                logger.info("Tkinter application already destroyed - cleanup skipped")
            except Exception as e:
                logger.debug(f"Error during tkinter cleanup: {e}")

    except Exception as e:
        logger.debug(f"Error cleaning up tkinter windows: {e}")
    
    try:
        from PyQt5.QtWidgets import QApplication
        if QApplication.instance():
            logger.info("Cleaning up PyQt application...")
            app = QApplication.instance()
            
            all_widgets = app.allWidgets()
            for widget in all_widgets:
                try:
                    widget.hide()
                    widget.close()
                    widget.deleteLater()
                except:
                    pass
            
            app.processEvents()
            app.quit()
            logger.info("PyQt application cleaned up")
            
    except ImportError:
        pass
    except Exception as e:
        logger.error(f"Error cleaning up PyQt: {e}")
    
    try:
        import PySimpleGUI as sg
        sg.Window.close_all()
        logger.info("PySimpleGUI windows closed")
    except ImportError:
        pass
    except Exception as e:
        logger.error(f"Error cleaning up PySimpleGUI: {e}")
    
    try:
        try:
            import mouse
            mouse.unhook_all()
            logger.info("Mouse hooks cleaned up")
        except:
            pass
            
        try:
            import keyboard
            keyboard.unhook_all()
            logger.info("Keyboard hooks cleaned up")
        except:
            pass
            
            
    except Exception as e:
        logger.error(f"Error cleaning up global listeners: {e}")
    
    try:
        import threading
        alive_threads = [t for t in threading.enumerate() if t != threading.current_thread()]
        for thread in alive_threads:
            if thread.is_alive() and not thread.daemon:
                logger.info(f"Found non-daemon thread: {thread.name}")
                try:
                    thread.join(timeout=1.0)
                    if thread.is_alive():
                        logger.warning(f"Thread {thread.name} still alive after join")
                except Exception as e:
                    logger.error(f"Error joining thread {thread.name}: {e}")
    except Exception as e:
        logger.error(f"Error cleaning up threads: {e}")

    globals()['radial_menu'] = None
    globals()['status_window'] = None

    import gc
    for i in range(3):
        collected = gc.collect()
        logger.info(f"Garbage collection pass {i+1}: collected {collected} objects")

    try:
        sys.stdout.flush()
        sys.stderr.flush()
    except:
        pass

    logger.info("Comprehensive UI cleanup completed successfully")

def main():
    atexit.register(atexit_handler)

    # signal은 메인 스레드에서만 작동하므로 체크
    try:
        import threading
        if threading.current_thread() is threading.main_thread():
            signal.signal(signal.SIGTERM, force_exit_handler)
            signal.signal(signal.SIGINT, force_exit_handler)
            if hasattr(signal, 'SIGBREAK'):
                signal.signal(signal.SIGBREAK, force_exit_handler)
            logger.info("Signal handlers registered successfully")
        else:
            logger.warning("Not in main thread, skipping signal handler registration")
    except Exception as e:
        logger.warning(f"Could not register signal handlers: {e}")

    if '_app_stop_event' in globals() and globals()['_app_stop_event'].is_set():
        logger.info("Stop event detected, cleaning up before start...")
        cleanup_all_ui()

    logger.info("Starting GeoMedical Helper...")

    from PyQt5.QtWidgets import QApplication
    import sys
    app = QApplication.instance() or QApplication(sys.argv)

    root = tk.Tk()
    root.withdraw()

    app.aboutToQuit.connect(cleanup_all_ui)
    root.protocol("WM_DELETE_WINDOW", lambda: (cleanup_all_ui(), root.quit()))
    
    if 'set_root' in globals():
        set_root(root)
    
    logger.info(f"GeoMedical Helper v{MAIN_VERSION} Started")
    print(f"GeoMedical Helper v{MAIN_VERSION} Started")
    
    try:
        import mouse
        
        if 'radial_menu' in globals() and globals()['radial_menu'] is not None:
            logger.info("Found existing radial_menu instance, cleaning up first...")
            cleanup_all_ui()
            time.sleep(1)
        
        radial_menu = RadialMenu()
        
        radial_menu.status_window.radial_menu = radial_menu
        
        hotkey_type = load_data('hotkey_type', 'mouse_wheel')
        setup_input_listener_for_instance(radial_menu, hotkey_type)
        
        logger.info("Initializing network monitoring...")
        radial_menu.start_network_monitor()
        
        initial_connection = radial_menu.check_internet_connection()
        logger.info(f"Initial internet connection status: {'ONLINE' if initial_connection else 'OFFLINE'}")

        logger.info("강제로 상태창 표시 시도...")
        radial_menu.status_window.show_status_window()

        logger.info("Initializing background asset monitoring...")
        radial_menu.start_asset_monitoring()
        logger.info("Background asset monitoring started successfully")
        
        hotkey_type = load_data('hotkey_type', 'mouse_wheel')
        hotkey_messages = {
            'mouse_wheel': '마우스 휠 버튼을 클릭하면 퀵 메뉴가 나타납니다.',
            'f1': 'F1 키를 누르면 퀵 메뉴가 나타납니다.',
            'f2': 'F2 키를 누르면 퀵 메뉴가 나타납니다.',
            'f3': 'F3 키를 누르면 퀵 메뉴가 나타납니다.',
            'f4': 'F4 키를 누르면 퀵 메뉴가 나타납니다.',
            'click_status': '네트워크 상태 표시창을 클릭하면 퀵 메뉴가 나타납니다.'
        }
        
        hotkey_message = hotkey_messages.get(hotkey_type, '마우스 휠 버튼을 클릭하면 퀵 메뉴가 나타납니다.')
        
        
        root.mainloop()
        
    except ImportError:
        messagebox.showerror(
            "라이브러리 오류",
            "mouse 라이브러리가 설치되어 있지 않습니다.\n\n"
            "다음 명령어로 설치해주세요:\n"
            "pip install mouse\n\n"
            "관리자 권한이 필요할 수 있습니다."
        )
        
    except Exception as e:
        messagebox.showerror("오류", f"프로그램 실행 중 오류가 발생했습니다.\n{str(e)}")
        report_error(e, "Main execution")
    
    finally:
        logger.info("Main function ending, cleaning up UI...")
        cleanup_all_ui()


if 'psutil' not in globals():
    class psutil:
        @staticmethod
        def cpu_percent(interval=1):
            return 0
        
        @staticmethod
        def cpu_count():
            return os.cpu_count() or 1
        
        @staticmethod
        def virtual_memory():
            class Memory:
                total = 8 * 1024**3
                used = 4 * 1024**3
                percent = 50
            return Memory()
        
        @staticmethod
        def disk_usage(path):
            class Disk:
                total = 256 * 1024**3
                used = 128 * 1024**3
                percent = 50
            return Disk()
    
    def create_modern_circular_background_auto_action(self):
            logger.info("Creating simple circular background for auto action submenu")
            
            center_x, center_y = self.local_x, self.local_y
            radius = 160
            
            self.canvas.create_oval(
                center_x - radius, center_y - radius,
                center_x + radius, center_y + radius,
                fill='#1a1a1a', outline='', tags='background'
            )
            
            inner_radius = radius - 20
            self.canvas.create_oval(
                center_x - inner_radius, center_y - inner_radius,
                center_x + inner_radius, center_y + inner_radius,
                fill='#2a2a2a', outline='', tags='background'
            )
            
            core_radius = radius - 40
            self.canvas.create_oval(
                center_x - core_radius, center_y - core_radius,
                center_x + core_radius, center_y + core_radius,
                fill='#333333', outline='', tags='background'
            )
            
            self.canvas.create_oval(
                center_x - radius, center_y - radius,
                center_x + radius, center_y + radius,
                fill='', outline='#4a9eff', width=2,
                tags='background'
            )
            
            self.canvas.create_oval(
                center_x - inner_radius, center_y - inner_radius,
                center_x + inner_radius, center_y + inner_radius,
                fill='', outline='#4a9eff', width=1,
                tags='background'
            )
            
            center_dot_radius = 3
            self.canvas.create_oval(
                center_x - center_dot_radius, center_y - center_dot_radius,
                center_x + center_dot_radius, center_y + center_dot_radius,
                fill='#4a9eff', outline='', tags='background'
            )
            
            logger.info("Simple background created")
    
    def blend_color(self, color1, color2, ratio):
        return color1
        

    def create_auto_action_buttons(self):
        logger.info("Creating auto action buttons...")
        
        button_info = [
            {"text": "네트워크 Fix", "color": "#e74c3c", "command": self.network_fix},
            {"text": "브라우저 Fix", "color": "#3498db", "command": self.browser_fix},
            {"text": "프린터 Fix", "color": "#2ecc71", "command": self.printer_fix},
            {"text": "MES Fix", "color": "#f39c12", "command": self.mes_fix}
        ]
        
        radius = 110
        button_size = 70
        
        center_size = 25
        center_circle = self.canvas.create_oval(
            self.local_x - center_size,
            self.local_y - center_size,
            self.local_x + center_size,
            self.local_y + center_size,
            fill='#34495e',
            outline='white',
            width=2
        )
        
        self.canvas.create_text(
            self.local_x,
            self.local_y,
            text="자동조치",
            font=('Arial', 9, 'bold'),
            fill='white'
        )
        
        self.buttons = []
        for i, btn_info in enumerate(button_info):
            angle = (i * (360 / len(button_info)) - 90) * math.pi / 180
            
            btn_x = self.local_x + radius * math.cos(angle)
            btn_y = self.local_y + radius * math.sin(angle)
            
            x1 = btn_x - button_size/2
            y1 = btn_y - button_size/2
            x2 = btn_x + button_size/2
            y2 = btn_y + button_size/2
            
            btn = self.canvas.create_oval(
                x1, y1, x2, y2,
                fill=btn_info["color"],
                outline='white',
                width=2,
                tags=f"button_{i}"
            )
            
            text = self.canvas.create_text(
                btn_x, btn_y,
                text=btn_info['text'],
                font=('Arial', 9, 'bold'),
                fill='white',
                tags=f"button_{i}"
            )
            
            self.buttons.append({
                "id": btn,
                "text_id": text,
                "x": btn_x,
                "y": btn_y,
                "size": button_size,
                "command": btn_info["command"],
                "color": btn_info["color"],
                "tag": f"button_{i}"
            })

            # 헬퍼 메서드를 사용하여 이벤트 바인딩
            self._bind_button_events(i, btn_info["command"])

            logger.info(f"Auto action button {i} ({btn_info['text']}) created at ({btn_x}, {btn_y})")
        
        logger.info(f"Created {len(self.buttons)} auto action buttons")
    
    def network_fix(self):
        logger.info("Network Fix initiated")
        self.close_menu()

        # 확인 대화상자 표시
        response = messagebox.askyesno(
            "네트워크 Fix 확인",
            "네트워크 자동 복구를 시작하시겠습니까?\n\n"
            "이 작업은 다음을 수행합니다:\n"
            "• 네트워크 어댑터 재시작\n"
            "• DNS 캐시 초기화\n"
            "• IP 설정 갱신\n"
            "• MES 통신 테스트"
        )

        # 사용자가 '아니오'를 선택한 경우 작업 취소
        if not response:
            logger.info("Network Fix cancelled by user")
            return

        def run_network_fix():
            try:
                progress_window = tk.Toplevel()
                progress_window.title("네트워크 Fix")
                
                RadialMenu.active_progress_windows.append(progress_window)
                
                def on_window_close():
                    try:
                        RadialMenu.active_progress_windows.remove(progress_window)
                    except:
                        pass
                    progress_window.destroy()
                
                progress_window.protocol("WM_DELETE_WINDOW", on_window_close)
                progress_window.geometry("500x400")
                progress_window.resizable(False, False)
                progress_window.transient(self.overlay if self.overlay else None)
                progress_window.grab_set()
                
                text_area = scrolledtext.ScrolledText(progress_window, height=20, width=60)
                text_area.pack(fill='both', expand=True, padx=10, pady=10)
                
                def log_progress(message):
                    text_area.insert(tk.END, f"{message}\n")
                    text_area.see(tk.END)
                    progress_window.update()
                
                log_progress("🔧 네트워크 Fix 시작...")
                log_progress("=" * 50)
                
                is_online = self.check_internet_connection()
                if is_online:
                    log_progress("✅ 현재 인터넷 연결 상태: 온라인")
                    log_progress("⚠️  오프라인이 아니지만 강제로 네트워크 복구를 진행합니다.")
                else:
                    log_progress("❌ 현재 인터넷 연결 상태: 오프라인")
                    log_progress("🔄 네트워크 자동 복구를 시작합니다.")
                
                log_progress("\n🚀 6단계 네트워크 복구 과정 시작...")
                
                recovery_success = self.auto_network_recovery()
                
                if recovery_success:
                    log_progress("\n✅ 네트워크 복구 완료!")
                else:
                    log_progress("\n❌ 네트워크 복구 실패")
                
                log_progress("\n🔍 MES 통신 테스트 시작...")
                mes_result = self.test_mes_communication()
                
                if mes_result:
                    log_progress("✅ MES 통신 테스트 성공! 정상적으로 통신되고 있습니다.")
                else:
                    log_progress("❌ MES 통신 테스트 실패. MES 서버 연결을 확인해주세요.")
                
                log_progress("\n" + "=" * 50)
                log_progress("🎉 네트워크 Fix 완료!")
                
                button_frame = tk.Frame(progress_window)
                button_frame.pack(pady=10)
                
                close_button = tk.Button(button_frame, text="닫기", 
                                       command=on_window_close,
                                       bg='#3498db', fg='white', font=('Arial', 10, 'bold'))
                close_button.pack()
                
            except Exception as e:
                logger.error(f"Network fix error: {e}")
                messagebox.showerror("오류", f"네트워크 Fix 중 오류가 발생했습니다:\n{str(e)}")
        
        import threading
        threading.Thread(target=run_network_fix, daemon=True).start()
    
    def browser_fix(self):
        logger.info("Browser Fix initiated")
        self.close_menu()
        
        result = messagebox.askyesno(
            "브라우저 Fix 확인",
            "브라우저 Fix를 실행하시겠습니까?\n\n"
            "다음 작업이 수행됩니다:\n"
            "• Chrome 브라우저 캐시 및 쿠키 삭제\n"
            "• Edge 브라우저 캐시 및 쿠키 삭제\n"
            "• 임시 인터넷 파일 정리\n\n"
            "진행하시겠습니까?"
        )
        
        if not result:
            logger.info("Browser Fix cancelled by user")
            return
        
        def run_browser_fix():
            try:
                progress_window = tk.Toplevel()
                progress_window.title("브라우저 Fix")
                
                RadialMenu.active_progress_windows.append(progress_window)
                
                def on_window_close():
                    try:
                        RadialMenu.active_progress_windows.remove(progress_window)
                    except:
                        pass
                    progress_window.destroy()
                
                progress_window.protocol("WM_DELETE_WINDOW", on_window_close)
                progress_window.geometry("500x300")
                progress_window.resizable(False, False)
                progress_window.transient(self.overlay if self.overlay else None)
                progress_window.grab_set()
                
                text_area = scrolledtext.ScrolledText(progress_window, height=15, width=60)
                text_area.pack(fill='both', expand=True, padx=10, pady=10)
                
                def log_progress(message):
                    text_area.insert(tk.END, f"{message}\n")
                    text_area.see(tk.END)
                    progress_window.update()
                
                log_progress("🌐 브라우저 Fix 시작...")
                log_progress("=" * 50)
                
                log_progress("🔄 브라우저 프로세스 종료 중...")
                
                browser_processes = ['chrome.exe', 'msedge.exe', 'iexplore.exe']
                for process_name in browser_processes:
                    try:
                        killed_any = False
                        for proc in psutil.process_iter(['pid', 'name']):
                            try:
                                if proc.info['name'] and proc.info['name'].lower() == process_name.lower():
                                    proc.terminate()
                                    killed_any = True
                                    log_progress(f"✅ {process_name} 프로세스 종료됨 (PID: {proc.info['pid']})")
                            except (psutil.NoSuchProcess, psutil.AccessDenied):
                                pass

                        if not killed_any:
                            log_progress(f"ℹ️  {process_name} 프로세스가 실행 중이 아님")
                    except Exception as e:
                        log_progress(f"⚠️  {process_name} 종료 중 오류: {e}")
                
                time.sleep(2)
                
                log_progress("\n🗑️  Chrome 캐시/쿠키 삭제 중...")
                chrome_paths = [
                    os.path.expanduser("~\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Cache"),
                    os.path.expanduser("~\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Cookies"),
                    os.path.expanduser("~\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Web Data")
                ]
                
                for path in chrome_paths:
                    try:
                        if os.path.exists(path):
                            if os.path.isdir(path):
                                import shutil
                                shutil.rmtree(path, ignore_errors=True)
                                log_progress(f"✅ Chrome: {os.path.basename(path)} 폴더 삭제됨")
                            else:
                                os.remove(path)
                                log_progress(f"✅ Chrome: {os.path.basename(path)} 파일 삭제됨")
                        else:
                            log_progress(f"ℹ️  Chrome: {os.path.basename(path)} 없음")
                    except Exception as e:
                        log_progress(f"⚠️  Chrome {os.path.basename(path)} 삭제 실패: {e}")
                
                log_progress("\n🗑️  Edge 캐시/쿠키 삭제 중...")
                edge_paths = [
                    os.path.expanduser("~\\AppData\\Local\\Microsoft\\Edge\\User Data\\Default\\Cache"),
                    os.path.expanduser("~\\AppData\\Local\\Microsoft\\Edge\\User Data\\Default\\Cookies"),
                    os.path.expanduser("~\\AppData\\Local\\Microsoft\\Edge\\User Data\\Default\\Web Data")
                ]
                
                for path in edge_paths:
                    try:
                        if os.path.exists(path):
                            if os.path.isdir(path):
                                import shutil
                                shutil.rmtree(path, ignore_errors=True)
                                log_progress(f"✅ Edge: {os.path.basename(path)} 폴더 삭제됨")
                            else:
                                os.remove(path)
                                log_progress(f"✅ Edge: {os.path.basename(path)} 파일 삭제됨")
                        else:
                            log_progress(f"ℹ️  Edge: {os.path.basename(path)} 없음")
                    except Exception as e:
                        log_progress(f"⚠️  Edge {os.path.basename(path)} 삭제 실패: {e}")
                
                log_progress("\n" + "=" * 50)
                log_progress("🎉 브라우저 Fix 완료!")
                log_progress("ℹ️  브라우저를 다시 시작하면 캐시/쿠키가 초기화됩니다.")
                
                button_frame = tk.Frame(progress_window)
                button_frame.pack(pady=10)
                
                close_button = tk.Button(button_frame, text="닫기", 
                                       command=on_window_close,
                                       bg='#3498db', fg='white', font=('Arial', 10, 'bold'))
                close_button.pack()
                
            except Exception as e:
                logger.error(f"Browser fix error: {e}")
                messagebox.showerror("오류", f"브라우저 Fix 중 오류가 발생했습니다:\n{str(e)}")
        
        import threading
        threading.Thread(target=run_browser_fix, daemon=True).start()
    

    def printer_fix(self):
        logger.info("Printer Fix initiated")
        self.close_menu()

        # 확인 대화상자 표시
        response = messagebox.askyesno(
            "프린터 Fix 확인",
            "프린터 자동 복구를 시작하시겠습니까?\n\n"
            "이 작업은 다음을 수행합니다:\n"
            "• 프린터 스풀러 서비스 재시작\n"
            "• 인쇄 대기열 초기화\n"
            "• 프린터 드라이버 상태 확인\n"
            "• 프린터 연결 테스트"
        )

        # 사용자가 '아니오'를 선택한 경우 작업 취소
        if not response:
            logger.info("Printer Fix cancelled by user")
            return

        def run_printer_fix():
            try:
                progress_window = tk.Toplevel()
                progress_window.title("프린터 Fix")
                
                RadialMenu.active_progress_windows.append(progress_window)
                
                def on_window_close():
                    try:
                        RadialMenu.active_progress_windows.remove(progress_window)
                    except:
                        pass
                    progress_window.destroy()
                
                progress_window.protocol("WM_DELETE_WINDOW", on_window_close)
                progress_window.geometry("500x400")
                progress_window.resizable(False, False)
                progress_window.transient(self.overlay if self.overlay else None)
                progress_window.grab_set()
                
                text_area = scrolledtext.ScrolledText(progress_window, height=20, width=60)
                text_area.pack(fill='both', expand=True, padx=10, pady=10)
                
                def log_progress(message):
                    text_area.insert(tk.END, f"{message}\n")
                    text_area.see(tk.END)
                    progress_window.update()
                
                log_progress("🖨️  프린터 Fix 시작...")
                log_progress("=" * 50)
                
                log_progress("🔧 프린터 레지스트리 설정 수정 중...")
                reg_command = [
                    'REG', 'ADD', 
                    'HKLM\\System\\CurrentControlSet\\Control\\Print',
                    '/v', 'RpcAuthnLevelPrivacyEnabled',
                    '/t', 'REG_DWORD',
                    '/d', '0',
                    '/f'
                ]
                
                try:
                    result = subprocess.run(reg_command, capture_output=True, text=True, timeout=30,
                         creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0)
                    if result.returncode == 0:
                        log_progress("✅ 레지스트리 설정 수정 완료")
                    else:
                        log_progress(f"⚠️  레지스트리 수정 경고: {result.stderr.strip()}")
                except Exception as e:
                    log_progress(f"❌ 레지스트리 수정 실패: {e}")
                
                log_progress("\n🔄 Print Spooler 서비스 중지 중...")
                try:
                    result = subprocess.run(['net', 'stop', 'spooler'], 
                                          capture_output=True, text=True, timeout=30,
                         creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0)
                    if result.returncode == 0:
                        log_progress("✅ Print Spooler 서비스 중지됨")
                    else:
                        log_progress(f"⚠️  Spooler 중지 경고: {result.stderr.strip()}")
                except Exception as e:
                    log_progress(f"❌ Spooler 중지 실패: {e}")
                
                time.sleep(3)
                
                log_progress("\n🚀 Print Spooler 서비스 시작 중...")
                try:
                    result = subprocess.run(['net', 'start', 'spooler'], 
                                          capture_output=True, text=True, timeout=30,
                         creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0)
                    if result.returncode == 0:
                        log_progress("✅ Print Spooler 서비스 시작됨")
                    else:
                        log_progress(f"⚠️  Spooler 시작 경고: {result.stderr.strip()}")
                except Exception as e:
                    log_progress(f"❌ Spooler 시작 실패: {e}")
                
                log_progress("\n🔧 추가 프린터 장애 해결 작업...")
                
                spooler_path = "C:\\Windows\\System32\\spool\\PRINTERS"
                try:
                    if os.path.exists(spooler_path):
                        import glob
                        spool_files = glob.glob(os.path.join(spooler_path, "*"))
                        if spool_files:
                            for file in spool_files:
                                try:
                                    os.remove(file)
                                    log_progress(f"✅ 대기열 파일 삭제: {os.path.basename(file)}")
                                except:
                                    pass
                        else:
                            log_progress("ℹ️  프린터 대기열이 이미 비어있습니다")
                    else:
                        log_progress("ℹ️  프린터 대기열 폴더를 찾을 수 없습니다")
                except Exception as e:
                    log_progress(f"⚠️  대기열 정리 중 오류: {e}")
                
                log_progress("\n" + "=" * 50)
                log_progress("🎉 프린터 Fix 완료!")
                log_progress("ℹ️  프린터 문제가 해결되었습니다. 프린터를 다시 사용해보세요.")
                
                log_progress("\n📋 장애 지속 시 추천 명령어:")
                log_progress("1. 프린터 드라이버 재설치")
                log_progress("2. rundll32 printui.dll,PrintUIEntry /k (프린터 새로고침)")
                log_progress("3. sfc /scannow (시스템 파일 검사)")
                
                button_frame = tk.Frame(progress_window)
                button_frame.pack(pady=10)
                
                close_button = tk.Button(button_frame, text="닫기", 
                                       command=on_window_close,
                                       bg='#2ecc71', fg='white', font=('Arial', 10, 'bold'))
                close_button.pack()
                
            except Exception as e:
                logger.error(f"Printer fix error: {e}")
                messagebox.showerror("오류", f"프린터 Fix 중 오류가 발생했습니다:\n{str(e)}")
        
        import threading
        threading.Thread(target=run_printer_fix, daemon=True).start()


    def mes_fix(self):
        logger.info("MES Fix initiated")
        self.close_menu()

        # 확인 대화상자 표시
        response = messagebox.askyesno(
            "MES Fix 확인",
            "MES 시스템 복구를 시작하시겠습니까?\n\n"
            "이 작업은 다음을 수행합니다:\n"
            "• MSCOMM32.ocx 자동 설치 및 등록\n"
            "• MES 서버 IP 설정 자동 구성\n"
            "• MES 통신 상태 확인\n"
            "• 연결 오류 자동 복구"
        )

        # 사용자가 '아니오'를 선택한 경우 작업 취소
        if not response:
            logger.info("MES Fix cancelled by user")
            return

        def run_mes_fix():
            try:
                progress_window = tk.Toplevel()
                progress_window.title("MES Fix")
                
                RadialMenu.active_progress_windows.append(progress_window)
                
                def on_window_close():
                    try:
                        RadialMenu.active_progress_windows.remove(progress_window)
                    except:
                        pass
                    progress_window.destroy()
                
                progress_window.protocol("WM_DELETE_WINDOW", on_window_close)
                progress_window.geometry("500x500")
                progress_window.resizable(False, False)
                progress_window.transient(self.overlay if self.overlay else None)
                progress_window.grab_set()

                text_area = scrolledtext.ScrolledText(progress_window, height=26, width=60)
                text_area.pack(fill='both', expand=True, padx=10, pady=10)
                
                def log_progress(message):
                    text_area.insert(tk.END, f"{message}\n")
                    text_area.see(tk.END)
                    progress_window.update()
                
                log_progress("🏭 MES Fix 시작...")
                log_progress("=" * 50)

                log_progress("\n[1/2] MSCOMM32.ocx 설치 및 등록")
                log_progress("=" * 50)

                import ctypes
                import tempfile
                import shutil

                try:
                    import requests
                except ImportError:
                    log_progress("⚠️  requests 모듈이 없습니다. 다운로드를 건너뜁니다.")
                    requests = None

                is_admin = ctypes.windll.shell32.IsUserAnAdmin()
                if not is_admin:
                    log_progress("⚠️  관리자 권한이 필요합니다!")
                    log_progress("   프로그램을 관리자 권한으로 실행해주세요.")
                else:
                    log_progress("✅ 관리자 권한 확인됨")

                url = 'https://file.geomedical.kr/file/MSCOMM32.ocx'
                dest_file = os.path.join(os.environ['windir'], 'SysWOW64', 'MSCOMM32.ocx')

                if os.path.exists(dest_file):
                    log_progress(f"ℹ️  MSCOMM32.ocx가 이미 설치되어 있습니다.")
                    log_progress(f"   위치: {dest_file}")
                else:
                    log_progress(f"\n📥 파일 다운로드 중...")
                    log_progress(f"   URL: {url}")

                    temp_file = None
                    if requests:
                        try:
                            temp_file = os.path.join(tempfile.gettempdir(), 'MSCOMM32.ocx')

                            response_download = requests.get(url, timeout=30)
                            response_download.raise_for_status()

                            with open(temp_file, 'wb') as f:
                                f.write(response_download.content)

                            log_progress(f"✅ 다운로드 완료: {temp_file}")

                        except Exception as e:
                            log_progress(f"❌ 다운로드 실패: {e}")
                            log_progress("   네트워크 연결을 확인해주세요.")
                            temp_file = None

                    if temp_file and os.path.exists(temp_file):
                        log_progress(f"\n📁 SysWOW64에 복사 중...")
                        log_progress(f"   대상: {dest_file}")

                        try:
                            shutil.copy2(temp_file, dest_file)
                            log_progress(f"✅ 파일 복사 완료")

                            log_progress(f"\n⚙️  regsvr32 등록 중...")

                            result = subprocess.run(
                                ['regsvr32.exe', '/s', dest_file],
                                capture_output=True,
                                timeout=10
                            )

                            if result.returncode == 0:
                                log_progress(f"✅ MSCOMM32.ocx 등록 완료")
                            else:
                                log_progress(f"⚠️  등록 실패 (코드: {result.returncode})")

                            try:
                                os.remove(temp_file)
                                log_progress(f"✅ 임시 파일 삭제 완료")
                            except:
                                pass

                        except Exception as e:
                            log_progress(f"❌ 파일 복사/등록 실패: {e}")
                            if not is_admin:
                                log_progress("   관리자 권한으로 다시 실행해주세요.")
                    else:
                        log_progress("❌ MSCOMM32.ocx 다운로드에 실패했습니다.")
                        log_progress("   수동으로 설치가 필요할 수 있습니다.")

                log_progress("\n" + "=" * 50)
                log_progress("[2/2] MES 서버 IP 설정")
                log_progress("=" * 50)

                env_setting = load_data('environment', 'office')
                log_progress(f"현재 사용 환경: {env_setting}")

                if env_setting == 'office':
                    target_ip = "192.168.0.250,14333"
                    env_name = "사무용"
                else:
                    target_ip = "10.10.10.10,14333"
                    env_name = "현장용"

                log_progress(f"설정할 IP: {target_ip} ({env_name})")

                def update_ini_file(file_path, file_description):
                    if not os.path.exists(file_path):
                        log_progress(f"⚠️  {file_description} 파일을 찾을 수 없습니다: {file_path}")
                        log_progress(f"   파일이 없으므로 건너뜁니다.")
                        return False

                    log_progress(f"\n📁 {file_description} 파일 발견: {file_path}")

                    backup_path = file_path + ".backup"
                    try:
                        shutil.copy2(file_path, backup_path)
                        log_progress(f"✅ 백업 파일 생성: {backup_path}")
                    except Exception as e:
                        log_progress(f"⚠️  백업 생성 실패: {e}")

                    log_progress(f"🔧 IP 설정 수정 중...")
                    try:
                        encodings_to_try = ['utf-8', 'cp949', 'euc-kr', 'latin-1']
                        lines = None
                        used_encoding = None

                        for encoding in encodings_to_try:
                            try:
                                with open(file_path, 'r', encoding=encoding) as f:
                                    lines = f.readlines()
                                    used_encoding = encoding
                                    log_progress(f"✅ 파일 인코딩 감지: {encoding}")
                                    break
                            except UnicodeDecodeError:
                                continue

                        if lines is None:
                            log_progress("❌ 파일 인코딩을 감지할 수 없습니다!")
                            return False

                        in_server_section = False
                        modified = False

                        for i, line in enumerate(lines):
                            line_stripped = line.strip()

                            if line_stripped == '[SERVER]':
                                in_server_section = True
                                log_progress("✅ [SERVER] 섹션 발견")
                                continue

                            if in_server_section:
                                if line_stripped.startswith('[') and line_stripped.endswith(']'):
                                    in_server_section = False
                                    continue

                                if line_stripped.startswith('IP='):
                                    old_ip = line_stripped[3:]
                                    lines[i] = f"IP={target_ip}\n"
                                    log_progress(f"✅ IP 설정 변경: {old_ip} → {target_ip}")
                                    modified = True
                                    break

                        if not modified:
                            log_progress("❌ IP= 설정을 찾을 수 없습니다!")
                            return False

                        with open(file_path, 'w', encoding=used_encoding) as f:
                            f.writelines(lines)

                        log_progress(f"✅ {file_description} 파일 저장 완료")
                        return True

                    except Exception as e:
                        log_progress(f"❌ 파일 수정 실패: {e}")
                        return False

                config_files = [
                    ("C:\\geopop\\commlib\\pf_profile.ini", "geopop pf_profile.ini"),
                    ("C:\\GEO_WORK\\commlib\\pf_profile.ini", "GEO_WORK pf_profile.ini"),
                    ("C:\\geomedical\\Geo_ERP\\Setup.ini", "Geo_ERP Setup.ini")
                ]

                success_count = 0
                total_count = len(config_files)

                for file_path, file_desc in config_files:
                    log_progress(f"\n{'─' * 50}")
                    if update_ini_file(file_path, file_desc):
                        success_count += 1
                
                log_progress("\n" + "=" * 50)
                log_progress("🎉 MES Fix 완료!")
                log_progress(f"ℹ️  MES 서버 IP가 {env_name} 환경에 맞게 설정되었습니다.")
                log_progress(f"ℹ️  처리 결과: {success_count}/{total_count} 파일 성공")
                if success_count == 0:
                    log_progress("⚠️  모든 설정 파일을 찾을 수 없습니다.")
                    log_progress("   MES 프로그램이 설치되어 있는지 확인하세요.")
                elif success_count < total_count:
                    log_progress(f"⚠️  일부 파일({total_count - success_count}개)을 처리하지 못했습니다.")
                    log_progress("   위의 로그를 확인하세요.")
                log_progress("ℹ️  MES 프로그램을 재시작하세요.")
                
                button_frame = tk.Frame(progress_window)
                button_frame.pack(pady=10)
                
                close_button = tk.Button(button_frame, text="닫기", 
                                       command=on_window_close,
                                       bg='#f39c12', fg='white', font=('Arial', 10, 'bold'))
                close_button.pack()
                
            except Exception as e:
                logger.error(f"MES fix error: {e}")
                messagebox.showerror("오류", f"MES Fix 중 오류가 발생했습니다:\n{str(e)}")
        
        import threading
        threading.Thread(target=run_mes_fix, daemon=True).start()



    def start_auto_action_animations(self):
        if not hasattr(self, 'canvas') or not self.canvas:
            return
            
        self.fade_in_animation()
        
        self.simple_border_pulse()
    
    def fade_in_animation(self, alpha=0.1):
        if not hasattr(self, 'overlay') or not self.overlay:
            return
            
        if alpha < 0.9:
            self.overlay.attributes('-alpha', alpha)
            self.overlay.after(20, lambda: self.fade_in_animation(alpha + 0.1))
        else:
            self.overlay.attributes('-alpha', 0.9)
    
    def pulse_animation(self, scale=1.0, growing=True):
        if not hasattr(self, 'canvas') or not self.canvas:
            return
            
        try:
            for i in range(4):
                button_tag = f'button_{i}_glow'
                items = self.canvas.find_withtag(button_tag)
                if items:
                    for item in items:
                        coords = self.canvas.coords(item)
                        if len(coords) == 4:
                            center_x = (coords[0] + coords[2]) / 2
                            center_y = (coords[1] + coords[3]) / 2
                            size = (coords[2] - coords[0]) / 2
                            
                            new_size = size * scale
                            self.canvas.coords(item,
                                center_x - new_size, center_y - new_size,
                                center_x + new_size, center_y + new_size)
            
            if growing:
                new_scale = scale + 0.02
                if new_scale > 1.1:
                    new_scale = 1.1
                    growing = False
            else:
                new_scale = scale - 0.02
                if new_scale < 0.9:
                    new_scale = 0.9
                    growing = True
            
            self.overlay.after(50, lambda: self.pulse_animation(new_scale, growing))
            
        except:
            pass
    
    def rotation_animation(self, angle=0):
        if not hasattr(self, 'canvas') or not self.canvas:
            return
            
        try:
            import math
            center_x, center_y = self.local_x, self.local_y
            
            line_items = self.canvas.find_withtag('rotating_line')
            for item in line_items:
                self.canvas.delete(item)
            
            for i in range(12):
                base_angle = i * 30 + angle
                rad = math.radians(base_angle)
                x1 = center_x + math.cos(rad) * 60
                y1 = center_y + math.sin(rad) * 60
                x2 = center_x + math.cos(rad) * 130
                y2 = center_y + math.sin(rad) * 130
                
                color_val = int(abs(math.sin(rad)) * 100 + 50)
                line_color = f"#{color_val:02x}{color_val:02x}ff"
                
                self.canvas.create_line(
                    x1, y1, x2, y2,
                    fill=line_color, width=2, tags='rotating_line'
                )
            
            new_angle = (angle + 1) % 360
            self.overlay.after(30, lambda: self.rotation_animation(new_angle))
            
        except:
            pass
    def test_mes_communication(self):
        try:
            env_setting = load_data('environment', 'office')
            
            if env_setting == 'office':
                mes_server = "192.168.0.250"
                mes_port = 14333
            else:
                mes_server = "10.10.10.10"
                mes_port = 14333
            
            import socket
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(5)
            
            try:
                result = sock.connect_ex((mes_server, mes_port))
                sock.close()
                return result == 0
            except Exception:
                return False
            
        except Exception as e:
            logger.error(f"MES communication test error: {e}")
            return False

    def auto_temp_cleanup(self):
        logger.info("Auto temp cleanup initiated")
        self.close_menu()
        messagebox.showinfo("자동 임시파일 삭제", "임시파일 삭제를 시작합니다.")
    
    def auto_registry_cleanup(self):
        logger.info("Auto registry cleanup initiated")
        self.close_menu()
        messagebox.showinfo("자동 레지스트리 정리", "레지스트리 정리를 시작합니다.")
    
    def auto_system_optimize(self):
        logger.info("Auto system optimize initiated")
        self.close_menu()
        messagebox.showinfo("자동 시스템 최적화", "시스템 최적화를 시작합니다.")
    
    def auto_virus_scan(self):
        logger.info("Auto virus scan initiated")
        self.close_menu()
        messagebox.showinfo("자동 바이러스 검사", "바이러스 검사를 시작합니다.")


if __name__ == "__main__":
    main()