#!/usr/bin/env python3
"""
GeoMedical Helper - Main.py
마우스 휠 클릭으로 활성화되는 퀵 액세스 메뉴

=== MAIN.PY VERSION INFORMATION ===
MAIN_VERSION = "1.2.0"
MAIN_BUILD_DATE = "2025-08-04"
MAIN_UPDATE_LOG = [
    "1.2.0 (2025-08-04): 버전 관리 시스템 개선, 코드 내 버전 관리로 변경",
    "1.1.0 (2025-08-03): 네트워크 어댑터 관리 기능 개선",
    "1.0.0 (2025-08-01): 초기 마우스휠 기반 UI 시스템 구축"
]
"""

# =============================================================================
# MAIN.PY 버전 정보 - 여기서 직접 관리
# =============================================================================
MAIN_VERSION = "1.2.0"
MAIN_BUILD_DATE = "2025-08-04"
MAIN_DESCRIPTION = "GeoMedical Helper GUI with Mouse Wheel Activation"

# PyQt5 라이브러리 (선택적)
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
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')
        
        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을 사용해서 비활성화된 어댑터 조회 및 활성화
        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"
        '''
        
        # PowerShell 실행
        result = subprocess.run([
            'powershell', '-Command', powershell_script
        ], capture_output=True, text=True, timeout=30, encoding='cp949')
        
        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:
        # __file__이 정의된 경우 (일반 실행)
        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}")
        # 모든 오류 발생 시 현재 작업 디렉토리의 cache 폴더 사용
        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):
    """
    캐시 폴더의 app_data.json에 데이터를 저장합니다.
    """
    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):
    """
    캐시 폴더의 app_data.json에서 데이터를 불러옵니다.
    파일이 없으면 기본값으로 초기화합니다.
    """
    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
    else:
        # 파일이 없으면 기본값으로 초기화
        if default is not None:
            save_data(key, default)
        return default

    return default

# Phase 2 수정: 로깅 시스템 개선 - RotatingFileHandler 추가
from logging.handlers import RotatingFileHandler

log_file_path = get_cache_file_path('debug.txt')

handlers = [
    logging.StreamHandler(),
    RotatingFileHandler(
        log_file_path,
        maxBytes=5*1024*1024,          # 5MB 제한
        backupCount=3,                  # 최대 3개 백업 파일 유지 (debug.txt.1, debug.txt.2, debug.txt.3)
        encoding='utf-8'
    )
]

logging.basicConfig(
    level=logging.INFO,  # Phase 2: DEBUG에서 INFO로 변경 - 불필요한 디버그 로그 제거
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=handlers
)
logger = logging.getLogger(__name__)

logger.info("="*60)
logger.info("✅ [Phase 2] Logging system initialized with RotatingFileHandler")
logger.info(f"✅ [Phase 2] Log file: {log_file_path}")
logger.info(f"✅ [Phase 2] Log level: INFO")
logger.info(f"✅ [Phase 2] Max log file size: 5MB")
logger.info(f"✅ [Phase 2] Backup count: 3")
logger.info("="*60)

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 create_status_window(self):
        """상태 표시 창 생성 - PyQt5 전용"""
        if hasattr(self, 'qt_widget') and self.qt_widget:
            return

        logger.info("Creating PyQt5 status window...")

        # PyQt5 import
        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

        # QApplication 인스턴스 확인/생성
        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:
            # 기본 위치 계산
            desktop = QDesktopWidget()
            screen_rect = desktop.screenGeometry()
            screen_width = screen_rect.width()
            screen_height = screen_rect.height()
            taskbar_height = 50
            x = screen_width - window_width - 2
            y = screen_height - window_height - taskbar_height

        if self.edit_mode:
            # 편집 모드: tkinter 창으로 생성 (드래그 가능)
            self.create_edit_mode_window(x, y, window_width, window_height)
        else:
            # 일반 모드: PyQt5 창 생성
            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):
        """PyQt5 일반 모드 창 생성"""
        try:
            from PyQt5.QtWidgets import QWidget, QLabel, QVBoxLayout
            from PyQt5.QtCore import Qt, QTimer
            from PyQt5.QtGui import QFont

            # PyQt 위젯 생성
            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)  # 5초마다 업데이트

            # 창 표시
            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}")

            # tkinter 관련 변수 초기화 (호환성)
            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):
        """완벽한 PyQt5 클릭 통과 창 - 제공된 코드 기반"""
        try:
            from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout
            from PyQt5.QtCore import Qt, QTimer
            from PyQt5.QtGui import QFont
            import sys
            
            # QApplication 인스턴스 확인/생성
            if not QApplication.instance():
                self.qt_app = QApplication(sys.argv)
            else:
                self.qt_app = QApplication.instance()
            
            # QWidget 생성
            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)  # 5초마다 업데이트
            
            # 창 표시
            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()
            
            # 클릭 이벤트 처리를 위한 전역 마우스 리스너 설정 (click_status 모드용)
            self.setup_pyqt_click_listener(x, y, width, height)
            
            # tkinter 창은 None으로 설정 (PyQt 사용 중임을 표시)
            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):
        """완벽한 PyQt 창의 상태 업데이트"""
        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:
                    # PyQt5.15+ 사용
                    text_width = font_metrics.horizontalAdvance(self.qt_label.text())
                except AttributeError:
                    # 구버전 PyQt5 사용
                    text_width = font_metrics.width(self.qt_label.text())
                text_height = font_metrics.height()
                
                # 패딩 추가 (좌우 16px, 상하 16px)
                widget_width = text_width + 16
                widget_height = max(text_height + 16, 80)  # 최소 높이 80
                
                # 창 크기 조정
                self.qt_widget.resize(widget_width, widget_height)
                
                # 오른쪽 정렬을 위해 x 좌표 재계산 (편집모드, 드래그 중, 사용자가 이미 이동한 경우에는 비활성화)
                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):
        """PyQt 클릭 통과 창에 대한 클릭 이벤트 리스너 설정"""
        try:
            # 현재 실행키 설정이 click_status인지 확인
            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
            
            # 기존 PyQt 클릭 리스너가 있으면 먼저 정리
            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
            
            # pynput을 사용한 전역 마우스 클릭 리스너 설정
            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:
                            # PyQt 창의 현재 위치와 크기 가져오기
                            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:
                                    # 중복 클릭 방지 (PyQt 리스너용) - 시간 단축
                                    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 = widget_x + widget_width // 2
                                    center_y = widget_y + widget_height // 2
                                    
                                    # 설정 창이 열려있는지 확인
                                    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
                                    
                                    # 설정 버튼 클릭 직후 1초간 무시
                                    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):
        """오토핫키 스타일 창 생성 - +LastFound +AlwaysOnTop -Caption +ToolWindow 구현"""
        try:
            import ctypes
            from ctypes import wintypes
            
            # tkinter 창 생성
            self.status_window = tk.Toplevel()
            self.status_window.overrideredirect(True)  # -Caption (타이틀바 제거)
            self.status_window.attributes('-topmost', True)  # +AlwaysOnTop
            # 네트워크 상태 창에 저장된 투명도 값 적용 (기본값: 0.85)
            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)
            
            # 창이 완전히 생성된 후 Windows API 적용
            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()
            
            # Windows API 상수
            GWL_EXSTYLE = -20
            WS_EX_LAYERED = 0x00080000
            WS_EX_TRANSPARENT = 0x00000020
            WS_EX_NOACTIVATE = 0x08000000
            WS_EX_TOOLWINDOW = 0x00000080
            
            # 현재 Extended Style 가져오기
            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)
            
            # Extended Style 적용
            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  # 90% 불투명도로 증가
                )
                
                # 최상위 유지
                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):
        """편집 모드용 창 생성 (드래그 가능) - 네트워크 상태 UI와 동일한 크기"""
        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 = "🔴 편집모드 - 드래그하여 이동"

        # 저장된 PyQt5 창 크기 사용 (실제 표시되는 크기)
        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:
            # 기본값: 네트워크 상태 UI와 비슷한 크기
            dynamic_width = 150
            dynamic_height = 35
            logger.info(f"Using default size: {dynamic_width}x{dynamic_height}")

        # 항상 저장된 위치(x, y) 사용
        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):
        """클릭 통과용 창 생성 - 방법 1: 투명색 지정 방식"""
        self.status_window = tk.Toplevel()
        self.status_window.overrideredirect(True)
        self.status_window.attributes('-topmost', True)
        # 네트워크 상태 창에 저장된 투명도 값 적용 (기본값: 0.85)
        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:
                # 방법 1: 특정 색상을 투명하게 만들어 클릭 통과 구현
                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}")
                # 대안: Win32 API 방식
                self.create_win32_click_through_window(x, y, width, height)
        else:
            # Linux/Mac용 일반 창
            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):
        """Win32 API를 사용한 클릭 통과 창 생성"""
        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)
            # 네트워크 상태 창에 저장된 투명도 값 적용 (기본값: 0.85)
            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)
            
            # Win32 API로 클릭 통과 설정
            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):
        """Win32 API를 사용한 강화된 클릭 통과"""
        if platform.system() != "Windows":
            return
            
        try:
            import ctypes
            hwnd = self.status_window.winfo_id()
            
            # 다중 방식 시도
            # 방식 1: 기본 투명 설정
            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:
                # 방식 2: 창을 최하위로 이동
                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}")
            # 최후 수단: PyQt 방식 제안
            logger.info("Consider using PyQt for better click-through support")
    
    def create_pyqt_click_through_window(self, x, y, width, height):
        """PyQt 기반 클릭 통과 창 생성"""
        try:
            from PyQt5.QtWidgets import QApplication, QWidget, QLabel
            from PyQt5.QtCore import Qt, QTimer
            from PyQt5.QtGui import QFont
            import sys
            
            # QApplication 인스턴스 확인/생성
            if not QApplication.instance():
                self.qt_app = QApplication(sys.argv)
            else:
                self.qt_app = QApplication.instance()
            
            # PyQt 위젯 생성
            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)  # 5초마다 업데이트

            # 상태창 클릭이 실행키로 설정된 경우 클릭 이벤트 처리
            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} 적용됨")

            # tkinter 창은 None으로 설정 (PyQt 사용 중임을 표시)
            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):
        """PyQt5 창 클릭 이벤트 처리"""
        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:
                # pynput 먼저 시도
                try:
                    from pynput import mouse as pynput_mouse
                    controller = pynput_mouse.Controller()
                    x, y = controller.position
                except ImportError:
                    # pynput이 없으면 mouse 라이브러리 사용
                    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):
        """PyQt 창의 상태 업데이트"""
        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):
        """PySimpleGUI 방식 클릭 통과 창 생성"""
        try:
            import PySimpleGUI as sg
            import threading
            
            # PySimpleGUI 테마 설정
            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'  # 특정 색상을 투명하게 만듦
            )
            
            # Windows에서 클릭 통과 설정
            if platform.system() == "Windows":
                try:
                    import ctypes
                    hwnd = self.sg_window.TKroot.winfo_id()
                    
                    # Win32 API로 클릭 통과 설정
                    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()
            
            # tkinter 창은 None으로 설정
            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):
        """PySimpleGUI 이벤트 루프"""
        try:
            while self.sg_running and hasattr(self, 'sg_window'):
                event, values = self.sg_window.read(timeout=5000)  # 5초 타임아웃
                
                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):
        """PySimpleGUI 상태 업데이트"""
        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):
        """Qt WA_TransparentForMouseEvents 방식"""
        try:
            from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout
            from PyQt5.QtCore import Qt, QTimer
            from PyQt5.QtGui import QFont
            import sys
            
            # QApplication 인스턴스 확인/생성
            if not QApplication.instance():
                self.qt_app = QApplication(sys.argv)
            else:
                self.qt_app = QApplication.instance()
            
            # Qt 위젯 생성
            self.qt_widget = QWidget()
            
            # 핵심: WA_TransparentForMouseEvents 설정
            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} 적용됨")

            # tkinter 창은 None으로 설정
            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')
            
            # 네트워크 상태 창에 저장된 투명도 값 적용 (기본값: 0.9)
            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)
            # 네트워크 상태 창에 저장된 투명도 값 적용 (기본값: 0.9)
            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)
            
            # 클릭 이벤트 바인딩 (non-edit mode)
            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()
            
            # 마우스가 창 근처(50픽셀 이내)에 있는지 확인
            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")
            
            # 100ms 후 다시 확인
            self.status_window.after(100, self.check_mouse_position)
            
        except Exception as e:
            logger.error(f"Mouse position check error: {e}")
            # 오류 발생시 1초 후 다시 시도
            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)
            # 네트워크 상태 창에 저장된 투명도 값 적용 (기본값: 0.7)
            saved_opacity = load_data('status_opacity', 0.7)
            self.status_window.attributes('-alpha', saved_opacity)
            self.status_window.configure(bg='#2c3e50')
            
            # 작은 크기로 시작 (20x20)
            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)
            # 네트워크 상태 창에 저장된 투명도 값 적용 (기본값: 0.95)
            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)
            
            # 3초 후 자동으로 숨김
            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):
        """Windows 전용 클릭 통과 적용"""
        if platform.system() != "Windows":
            return
            
        try:
            import ctypes
            hwnd = self.status_window.winfo_id()
            
            # Windows API를 사용한 클릭 통과 설정
            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):
        """클릭 통과 설정 - Windows 11 호환성 개선"""
        if not self.status_window:
            return
            
        if platform.system() == "Windows":
            try:
                import ctypes
                from ctypes import wintypes
                
                # Windows API 상수
                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:
                    # 방법 1: 완전한 클릭 통과 모드
                    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:
                # PyQt5 창인 경우
                pos = self.qt_widget.pos()
                x = pos.x()
                y = pos.y()
            elif self.status_window:
                # tkinter 창인 경우
                x = self.status_window.winfo_x()
                y = self.status_window.winfo_y()
            else:
                return
                
                # 창 숨기기
                self.status_window.withdraw()
                
                # 100ms 후 다시 표시
                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:
                # PyQt5 창인 경우
                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:
                # tkinter 창인 경우
                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)  # 최상위 제거
                # 네트워크 상태 창에 저장된 투명도 값 적용 (기본값: 0.7)
                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):
        """강화된 클릭 통과 설정 적용 (Windows 11 전용)"""
        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()
                
                # 방법 1: 윈도우를 완전히 배경으로 이동
                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
                )
                
                # 방법 2: 윈도우를 비활성화
                WS_DISABLED = 0x08000000
                GWL_STYLE = -16
                
                style = ctypes.windll.user32.GetWindowLongW(hwnd, GWL_STYLE)
                ctypes.windll.user32.SetWindowLongW(hwnd, GWL_STYLE, style | WS_DISABLED)
                
                # 방법 3: 마우스 이벤트를 무시하도록 설정
                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
            
            # 설정 버튼 클릭 직후 1초간 무시
            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:
                    # pynput 먼저 시도
                    try:
                        from pynput import mouse as pynput_mouse
                        controller = pynput_mouse.Controller()
                        x, y = controller.position
                    except ImportError:
                        # pynput이 없으면 mouse 라이브러리 사용
                        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()

        # 새로운 모드로 창 재생성 (visible 상태일 때만)
        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):
        """모든 창 정리 (PyQt5, tkinter 모두)"""
        try:
            # 현재 위치 저장
            x, y = None, None

            # PyQt5 창 정리
            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()  # Qt 객체 완전 정리
                    self.qt_widget = None
                except Exception as e:
                    logger.error(f"Error closing PyQt5 window: {e}")

            # tkinter 창 정리
            if hasattr(self, 'status_window') and self.status_window:
                try:
                    if self.status_window.winfo_exists():
                        if x is None:  # PyQt5에서 위치를 못 가져왔으면 tkinter에서 가져오기
                            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:
                # Windows에서 가상 화면 크기 가져오기 (듀얼 모니터 포함)
                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:
            # 편집 모드에서만 드래그가 가능하므로 tkinter 창 위치만 저장
            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 = "🔴 오프라인"
                        
                        # UI 업데이트는 메인 스레드에서 실행
                        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}")
                        
                        # 메인 스레드가 살아있는지 확인 후 UI 업데이트
                        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}")
                        # 오류 발생 시 UI 업데이트
                        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}")
                        
                        # 메인 스레드가 살아있는지 확인 후 UI 업데이트
                        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="❓ 상태 확인 실패")
        
        # 5초마다 업데이트 (편집 모드에서는 업데이트 빈도 줄임)
        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):
        """인터넷 연결 확인 - 완전한 socket 방식 (CMD 창 없음)"""
        # 다중 DNS 서버와 포트로 빠른 연결 확인
        dns_servers = [
            ("8.8.8.8", 53),      # Google DNS
            ("1.1.1.1", 53),      # Cloudflare DNS
            ("208.67.222.222", 53), # OpenDNS
            ("8.8.4.4", 53),      # Google DNS 보조
        ]

        for server, port in dns_servers:
            try:
                # 각 서버마다 2초 타임아웃으로 빠른 확인
                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

        # 모든 DNS 서버 실패 시 HTTP 포트로 재시도
        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 주소 가져오기
            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):
        """로컬 IP 주소 가져오기"""
        try:
            # Google DNS에 연결하여 로컬 IP 확인
            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):
        """현재 연결된 WiFi SSID 가져오기"""
        try:
            if platform.system() == "Windows":
                # Windows에서 현재 연결된 WiFi 정보 가져오기
                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:
                    # SSID 추출
                    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":  # macOS
                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":
                # Linux에서 iwgetid 명령어 사용
                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
        
        # PyQt 위젯 제거 - 강화된 정리
        if hasattr(self, 'qt_widget') and self.qt_widget:
            try:
                # Qt 타이머 정리
                if hasattr(self, 'qt_timer') and self.qt_timer:
                    self.qt_timer.stop()
                    self.qt_timer = None
                
                # Qt 위젯 숨기기 및 닫기
                self.qt_widget.hide()
                self.qt_widget.close()
                
                # Qt 이벤트 루프에서 위젯 완전 제거
                self.qt_widget.deleteLater()
                
                # PyQt 마우스 리스너 정리
                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
                
                # Qt 애플리케이션 정리
                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
        
        # PySimpleGUI 창 제거
        if hasattr(self, 'sg_window') and self.sg_window:
            try:
                self.sg_window.close()
                self.sg_running = False
            except:
                pass
            self.sg_window = None
        
        # tkinter 창 제거
        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
            
        # tkinter 창 숨기기
        if self.status_window:
            self.status_window.destroy()
            self.status_window = None
            self.status_label = None
            
        # PyQt 창 숨기기
        if hasattr(self, 'qt_widget') and self.qt_widget:
            self.qt_widget.hide()
            if hasattr(self, 'qt_timer'):
                self.qt_timer.stop()
            # PyQt 클릭 리스너 정리
            if hasattr(self, 'pyqt_mouse_listener'):
                try:
                    self.pyqt_mouse_listener.stop()
                    logger.info("PyQt mouse listener stopped")
                except:
                    pass
                
        # PySimpleGUI 창 숨기기
        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():
            # tkinter 창이 있으면 다시 표시
            self.status_window.deiconify()
            self.status_window.lift()
        elif hasattr(self, 'qt_widget') and self.qt_widget:
            # PyQt 창이 있으면 다시 표시
            self.qt_widget.show()
            if hasattr(self, 'qt_timer'):
                self.qt_timer.start(5000)
        elif hasattr(self, 'sg_window') and self.sg_window:
            # PySimpleGUI 창이 있으면 다시 표시
            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
        
    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:
                # 창이 유효하지 않으면 None으로 설정
                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("1400x700")  # 넓이를 더 늘려서 모든 버튼이 완전히 보이도록
        self.settings_window.resizable(True, True)  # 가로세로 모두 크기 조절 가능
        
        # 메인 캔버스와 스크롤바 생성
        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 = ttk.Frame(self.scrollable_frame, padding="10")
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 마우스 휠 스크롤 지원
        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)
        
        # 제목
        title_label = ttk.Label(main_frame, text="GeoMedical Helper 설정", font=('Arial', 12, 'bold'))
        title_label.pack(pady=(0, 20))

        # 상단 섹션: 상태 표시와 실행키 설정을 나란히 배치
        top_frame = ttk.Frame(main_frame)
        top_frame.pack(fill=tk.X, pady=(0, 10))

        # 상태 표시 설정 (왼쪽) - 넓이를 줄여서 빈 공간 최소화
        status_frame = ttk.LabelFrame(top_frame, text="상태 표시", padding="10")
        status_frame.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 10))
        status_frame.configure(width=400)  # 고정 넓이로 빈 공간 최소화
        
        self.status_var = tk.BooleanVar(value=self.status_enabled)
        status_check = ttk.Checkbutton(
            status_frame,
            text="인터넷 상태 표시",
            variable=self.status_var,
            command=self.toggle_status_display
        )
        status_check.pack(anchor=tk.W)
        
        # 설명
        desc_label = ttk.Label(
            status_frame,
            text="• 인터넷 연결 상태, 네트워크 이름, IP 주소를 표시합니다.\n• 오토핫키 스타일 방식으로 완전한 클릭 통과를 구현합니다.\n• 10가지 표시 방식을 지원하여 어떤 환경에서도 작동합니다.",
            font=('Arial', 9),
            foreground='gray'
        )
        desc_label.pack(anchor=tk.W, pady=(5, 0))
        
        # 위치 조정 프레임
        position_frame = ttk.Frame(status_frame)
        position_frame.pack(fill=tk.X, pady=(10, 0))
        
        # 편집 모드 안내
        edit_mode_frame = ttk.Frame(position_frame)
        edit_mode_frame.pack(fill=tk.X, pady=(0, 10))
        
        # 편집 모드 표시
        self.edit_mode_label = ttk.Label(
            edit_mode_frame,
            text="🔓 편집 모드 활성화됨 - 상태 창을 드래그하여 이동 가능",
            font=('Arial', 9, 'bold'),
            foreground='#e74c3c'
        )
        self.edit_mode_label.pack(anchor=tk.W)
        
        # 위치 조정 안내
        position_info = ttk.Label(
            position_frame,
            text="• 상태 창의 빨간 테두리를 드래그하여 위치 조정\n• 설정 창을 닫으면 편집 모드가 해제됩니다\n• 일반 모드에서는 상태 창 뒤의 버튼을 클릭할 수 있습니다",
            font=('Arial', 8),
            foreground='gray'
        )
        position_info.pack(anchor=tk.W, pady=(0, 5))
        
        # 버튼 프레임
        button_frame = ttk.Frame(position_frame)
        button_frame.pack(fill=tk.X)
        
        reset_position_btn = ttk.Button(
            button_frame,
            text="위치 초기화",
            command=self.reset_status_position
        )
        reset_position_btn.pack(side=tk.LEFT)

        # 네트워크 상태 UI 투명도 조절 컨트롤러
        ttk.Label(button_frame, text="네트워크 상태 UI 투명도:").pack(side=tk.LEFT, padx=(20, 5))

        self.network_opacity_var = tk.DoubleVar(value=load_data('network_status_opacity', 0.9))
        self.network_opacity_scale = ttk.Scale(
            button_frame,
            from_=0.1,
            to=1.0,
            orient=tk.HORIZONTAL,
            variable=self.network_opacity_var,
            command=self.update_network_opacity,
            length=150
        )
        self.network_opacity_scale.pack(side=tk.LEFT, padx=(0, 10))

        # 투명도 값 표시
        self.opacity_value_label = ttk.Label(button_frame, text=f"{self.network_opacity_var.get():.1f}")
        self.opacity_value_label.pack(side=tk.LEFT)

        # 현재 위치 표시
        self.position_label = ttk.Label(
            position_frame,
            text="",
            font=('Arial', 8),
            foreground='gray'
        )
        self.position_label.pack(anchor=tk.W, pady=(5, 0))
        
        # 현재 위치 업데이트
        self.update_position_label()
        
        # 실행키 설정 프레임 (오른쪽)
        hotkey_frame = ttk.LabelFrame(top_frame, text="실행키 설정", padding="10")
        hotkey_frame.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 10))
        hotkey_frame.configure(width=350)  # 적절한 크기로 설정
        
        # 현재 설정된 실행키 표시
        current_hotkey = load_data('hotkey_type', 'mouse_wheel')
        hotkey_names = {
            'mouse_wheel': '마우스휠 버튼 (기본값)',
            'f1': 'F1 키',
            'f2': 'F2 키', 
            'f3': 'F3 키',
            'f4': 'F4 키',
            'click_status': '상태창 클릭'
        }
        
        # 실행키 선택 드롭다운
        hotkey_select_frame = ttk.Frame(hotkey_frame)
        hotkey_select_frame.pack(fill=tk.X, pady=(0, 10))
        
        hotkey_label = ttk.Label(hotkey_select_frame, text="실행키 선택:", font=('Arial', 9))
        hotkey_label.pack(side=tk.LEFT, padx=(0, 10))
        
        # 드롭다운 콤보박스
        self.hotkey_var = tk.StringVar(value=current_hotkey)
        self.hotkey_combo = ttk.Combobox(
            hotkey_select_frame,
            textvariable=self.hotkey_var,
            values=list(hotkey_names.values()),
            state='readonly',
            width=30
        )
        # 현재 값 설정
        self.hotkey_combo.set(hotkey_names.get(current_hotkey, '마우스휠 버튼 (기본값)'))
        self.hotkey_combo.pack(side=tk.LEFT)
        
        # 변경 이벤트 바인딩
        self.hotkey_combo.bind('<<ComboboxSelected>>', self.on_hotkey_selected)
        
        # 현재 실행키 표시 라벨
        self.current_key_label = ttk.Label(
            hotkey_frame,
            text=f"✓ 현재 활성화된 실행키: {hotkey_names.get(current_hotkey, '마우스휠 버튼')}",
            font=('Arial', 9, 'bold'),
            foreground='#27ae60'
        )
        self.current_key_label.pack(anchor=tk.W, pady=(5, 0))
        
        # 실행키 설명
        hotkey_desc = ttk.Label(
            hotkey_frame,
            text="• 마우스휠 버튼: 마우스 휠을 눌러서 실행 (권장)\n• 키보드: 선택한 키를 눌러서 실행\n• 상태창 클릭: 네트워크 상태 표시창을 클릭하여 실행",
            font=('Arial', 8),
            foreground='gray'
        )
        hotkey_desc.pack(anchor=tk.W, pady=(5, 0))
        
        
        # 사용 환경 선택 그룹
        env_frame = ttk.LabelFrame(main_frame, text="사용 환경", padding="10")
        env_frame.pack(fill=tk.X, pady=(20, 10))
        
        # 저장된 환경 설정을 GUI 표시용으로 변환
        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)
        
        office_radio = ttk.Radiobutton(
            env_frame,
            text="사무용",
            variable=self.env_var,
            value="사무용",
            command=self.on_environment_changed
        )
        office_radio.pack(anchor=tk.W, pady=(0, 5))
        
        field_radio = ttk.Radiobutton(
            env_frame,
            text="현장용",
            variable=self.env_var,
            value="현장용",
            command=self.on_environment_changed
        )
        field_radio.pack(anchor=tk.W)
        
        # 하단 섹션: 웹사이트 관리와 프로그램 관리를 나란히 배치
        bottom_frame = ttk.Frame(main_frame)
        bottom_frame.pack(fill=tk.X, pady=(20, 10))

        # 웹사이트 관리 섹션 (왼쪽)
        website_frame = ttk.LabelFrame(bottom_frame, text="웹사이트 관리", padding="10")
        website_frame.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 10))
        website_frame.configure(width=580)  # 적절한 크기로 설정
        
        # 현재 웹사이트 목록 표시
        self.websites_label = ttk.Label(website_frame, text="등록된 웹사이트:", font=('Arial', 10, 'bold'))
        self.websites_label.pack(anchor=tk.W, pady=(0, 5))
        
        # 웹사이트 목록을 표시할 프레임
        self.website_list_frame = ttk.Frame(website_frame)
        self.website_list_frame.pack(fill=tk.X, pady=(0, 10))
        
        # 웹사이트 목록 업데이트
        self.update_website_list()
        
        # 새 웹사이트 추가 섹션
        add_info_label = ttk.Label(website_frame, text="새 웹사이트 추가 (최대 7개까지 가능):", 
                                  font=('Arial', 9), foreground='gray')
        add_info_label.pack(anchor=tk.W, pady=(10, 5))
        
        add_frame = ttk.Frame(website_frame)
        add_frame.pack(fill=tk.X, pady=(0, 0))
        
        # 텍스트 입력
        ttk.Label(add_frame, text="표시 텍스트:").grid(row=0, column=0, sticky=tk.W, padx=(0, 5))
        self.website_text_var = tk.StringVar()
        self.website_text_entry = ttk.Entry(add_frame, textvariable=self.website_text_var, width=15)
        self.website_text_entry.grid(row=0, column=1, padx=(0, 10))
        
        # URL 입력
        ttk.Label(add_frame, text="주소(URL):").grid(row=0, column=2, sticky=tk.W, padx=(0, 5))
        self.website_url_var = tk.StringVar()
        self.website_url_entry = ttk.Entry(add_frame, textvariable=self.website_url_var, width=25)
        self.website_url_entry.grid(row=0, column=3, padx=(0, 10))
        
        # 추가 버튼
        self.add_website_btn = ttk.Button(add_frame, text="추가", command=self.add_website)
        self.add_website_btn.grid(row=0, column=4)

        # 프로그램 관리 섹션 (오른쪽)
        program_frame = ttk.LabelFrame(bottom_frame, text="프로그램 관리", padding="10")
        program_frame.pack(side=tk.LEFT, fill=tk.Y)
        program_frame.configure(width=580)  # 적절한 크기로 설정

        # 현재 프로그램 목록 표시
        self.programs_label = ttk.Label(program_frame, text="등록된 프로그램:", font=('Arial', 10, 'bold'))
        self.programs_label.pack(anchor=tk.W, pady=(0, 5))

        # 프로그램 목록을 표시할 프레임
        self.program_list_frame = ttk.Frame(program_frame)
        self.program_list_frame.pack(fill=tk.X, pady=(0, 10))

        # 프로그램 목록 업데이트
        self.update_program_list()

        # 새 프로그램 추가 섹션
        add_program_info_label = ttk.Label(program_frame, text="새 프로그램 추가 (최대 7개까지 가능):",
                                          font=('Arial', 9), foreground='gray')
        add_program_info_label.pack(anchor=tk.W, pady=(10, 5))

        add_program_frame = ttk.Frame(program_frame)
        add_program_frame.pack(fill=tk.X, pady=(0, 0))

        # 텍스트 입력
        ttk.Label(add_program_frame, text="표시 텍스트:").grid(row=0, column=0, sticky=tk.W, padx=(0, 5))
        self.program_text_var = tk.StringVar()
        self.program_text_entry = ttk.Entry(add_program_frame, textvariable=self.program_text_var, width=15)
        self.program_text_entry.grid(row=0, column=1, padx=(0, 10))

        # 경로 입력
        ttk.Label(add_program_frame, text="파일 경로:").grid(row=0, column=2, sticky=tk.W, padx=(0, 5))
        self.program_path_var = tk.StringVar()
        self.program_path_entry = ttk.Entry(add_program_frame, textvariable=self.program_path_var, width=25)
        self.program_path_entry.grid(row=0, column=3, padx=(0, 10))

        # 찾아보기 버튼
        self.browse_program_btn = ttk.Button(add_program_frame, text="찾아보기", command=self.browse_program_path)
        self.browse_program_btn.grid(row=0, column=4, padx=(0, 5))

        # 추가 버튼
        self.add_program_btn = ttk.Button(add_program_frame, text="추가", command=self.add_program)
        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)  # UI가 완전히 로드된 후 실행
        
    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):
        """사용 환경 변경 시 호출"""
        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:
            # 저장된 환경 설정을 다시 읽어와서 GUI에 반영
            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:
            # RadialMenu 인스턴스가 있는지 확인
            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:
                # RadialMenu가 없으면 재시작 필요
                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', [])
        
        # 라벨 텍스트 업데이트 (개수 표시)
        total_count = len(custom_websites) + 1  # 기본 지오메디컬 링크 1개 포함
        if hasattr(self, 'websites_label'):
            self.websites_label.config(text=f"등록된 웹사이트 ({total_count}/8):")
        
        # 기본 지오메디컬 링크 표시
        default_label = ttk.Label(self.website_list_frame, 
                                 text="• 지오메디컬 링크 (https://lnk.geomedical.kr) [기본]",
                                 font=('Arial', 9))
        default_label.pack(anchor=tk.W)
        
        # 사용자 정의 웹사이트 표시
        for i, website in enumerate(custom_websites):
            website_frame = ttk.Frame(self.website_list_frame)
            website_frame.pack(fill=tk.X, pady=1)
            
            # 웹사이트 정보 표시
            info_text = f"• {website['text']} ({website['url']})"
            website_label = ttk.Label(website_frame, text=info_text, font=('Arial', 9))
            website_label.pack(side=tk.LEFT)
            
            # 삭제 버튼
            delete_btn = ttk.Button(website_frame, text="삭제", 
                                  command=lambda idx=i: self.delete_website(idx),
                                  width=6)
            delete_btn.pack(side=tk.RIGHT)
        
        # 스크롤 영역 업데이트
        self.settings_window.update_idletasks()  # UI 요소들의 크기 계산 완료 대기
        if hasattr(self, 'canvas'):
            self.canvas.configure(scrollregion=self.canvas.bbox("all"))
        
        # 동적 높이 조절
        self.adjust_window_height()
        
        # 추가 버튼 상태 업데이트 (7개 이상이면 비활성화)
        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
        
        # URL 형식 검증 (간단한 검증)
        if not (url.startswith('http://') or url.startswith('https://')):
            url = 'https://' + url
        
        # 현재 웹사이트 목록 가져오기
        custom_websites = load_data('custom_websites', [])
        
        # 개수 제한 확인 (기본 지오메디컬 링크 1개 + 사용자 정의 7개 = 최대 8개)
        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 hasattr(self, 'programs_label'):
            self.programs_label.config(text=f"등록된 프로그램 ({len(custom_programs)}/7):")

        # 사용자 정의 프로그램 표시
        for i, program in enumerate(custom_programs):
            program_frame = ttk.Frame(self.program_list_frame)
            program_frame.pack(fill=tk.X, pady=1)

            # 프로그램 정보 표시
            info_text = f"• {program['text']} ({program['path']})"
            program_label = ttk.Label(program_frame, text=info_text, font=('Arial', 9))
            program_label.pack(side=tk.LEFT)

            # 삭제 버튼
            delete_btn = ttk.Button(program_frame, text="삭제",
                                  command=lambda idx=i: self.delete_program(idx),
                                  width=6)
            delete_btn.pack(side=tk.RIGHT)

        # 스크롤 영역 업데이트
        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', [])

        # 개수 제한 확인 (최대 7개)
        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))  # 화면 높이의 80% 또는 1200px
            
            # 여백을 고려한 적절한 창 높이 계산
            # (컨텐츠 높이 + 여백 + 스크롤바 공간)
            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)
        
        # user_has_moved 플래그를 False로 설정하여 오른쪽 정렬 복원
        self.status_window.user_has_moved = False
        save_data('user_has_moved', False)
        
        # 상태 창이 열려있으면 재생성
        if self.status_window.is_visible:
            self.status_window.hide_status_window()
            self.status_window.show_status_window()
        
        # 위치 라벨 업데이트
        self.update_position_label()
        
        logger.info("Status window position reset - right alignment restored")

    def update_network_opacity(self, value):
        """네트워크 상태 UI 투명도 업데이트"""
        opacity = float(value)
        save_data('network_status_opacity', opacity)

        # 투명도 값 라벨 업데이트
        if hasattr(self, 'opacity_value_label'):
            self.opacity_value_label.config(text=f"{opacity:.1f}")

        # 네트워크 상태 창이 열려있으면 즉시 적용
        if hasattr(self, 'status_window') and self.status_window and self.status_window.is_visible:
            # 편집 모드인 경우: 편집 모드 창을 일시적으로 숨기고 네트워크 상태 UI를 보여줌
            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()

                # 일시적으로 PyQt5 창 생성해서 투명도 미리보기
                self.status_window.set_edit_mode(False)  # 일시적으로 편집 모드 해제

                # PyQt5 창에 투명도 적용
                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()

                # 1초 후 다시 편집 모드로 복원
                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:
                # 일반 모드: PyQt5 창에 직접 투명도 적용
                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:
                        # 0.5초 후 새로운 방식으로 재생성
                        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):
        """PyQt 모드 시도"""
        try:
            # PyQt5 설치 확인
            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):
        """PySimpleGUI 모드 시도"""
        try:
            # PySimpleGUI 설치 확인
            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 close_settings(self):
        """설정 창 닫기"""
        try:
            # 설정 창이 닫힐 때 편집 모드 해제
            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 = []
    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 = []  # 활성 리스너 목록

        # WiFi SSID 캐시 (30초 간격으로 갱신)
        self._wifi_ssid_cache = None
        self._wifi_ssid_cache_time = 0
        
        # 백그라운드 자산 모니터링 시작 (5초 후 실행하여 초기 CMD 창 방지)
        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}")

        # pynput 리스너 정리
        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()
        
        # StatusWindow의 PyQt 클릭 리스너 정리
        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}")
        
        # mouse/keyboard 라이브러리 이벤트 정리 (가능한 경우)
        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})")

        # Phase 1 수정: 상태창의 클릭 통과 임시 비활성화 (원형 메뉴 버튼 클릭 가능하도록)
        try:
            if hasattr(self, 'status_window') and self.status_window:
                if hasattr(self.status_window, 'qt_widget') and self.status_window.qt_widget:
                    from PyQt5.QtCore import Qt
                    current_flags = self.status_window.qt_widget.windowFlags()
                    # WindowTransparentForInput 플래그 제거 (클릭 수신 가능하도록)
                    new_flags = current_flags & ~Qt.WindowTransparentForInput
                    self.status_window.qt_widget.setWindowFlags(new_flags)
                    self.status_window.qt_widget.show()  # 플래그 변경 후 다시 표시 필요
                    logger.info("✅ [Phase 1] Status window click-through disabled for radial menu")
        except Exception as e:
            logger.error(f"Failed to disable status window click-through: {e}")

        # 진행창이 열려있으면 모두 닫기
        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.attributes('-topmost', True)
        self.overlay.overrideredirect(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  # 버튼 영역 크기 (확대)
        
        # 멀티 모니터 지원을 위한 가상 화면 정보 가져오기
        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}")
        
        # 원형 GUI를 위한 투명 배경 설정 (메인 메뉴)
        # 저장된 원형 UI 투명도 값 적용 (기본값: 0.9)
        self.overlay.attributes('-alpha', 0.9)
        self.overlay.configure(bg='#191919')  # 투명 처리될 배경색
        # 지정된 색상을 투명하게 만들어 원형 효과 구현
        self.overlay.wm_attributes('-transparentcolor', '#191919')
        
        # 투명 배경 설정을 위한 전체 화면 오버레이
        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())

        # Z-order 설정: background를 overlay 바로 아래에 배치
        self.background.attributes('-topmost', True)
        self.overlay.attributes('-topmost', True)

        # overlay를 background 위로 올리기
        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):
        """4개의 버튼을 원형으로 배치"""
        logger.info("Creating buttons...")
        
        radius = 110  # 중심에서 버튼까지의 거리 (확대)
        button_size = 80  # 버튼 크기 (확대)
        
        # 버튼 정보 (6개 - 프로그램 바로가기 버튼 추가)
        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):
            # 각도 계산 (위쪽부터 시작, 6개 버튼이므로 60도 간격)
            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"],
                "tag": f"button_{i}"
            })
            
            # 마우스 오버 효과 (캔버스 태그에 바인딩)
            # 클로저 문제를 해결하기 위해 즉시 실행 함수 사용
            self.canvas.tag_bind(f"button_{i}", '<Enter>', 
                (lambda tag: lambda e: self.on_button_hover(tag, True))(f"button_{i}"))
            self.canvas.tag_bind(f"button_{i}", '<Leave>', 
                (lambda tag: lambda e: self.on_button_hover(tag, False))(f"button_{i}"))
            self.canvas.tag_bind(f"button_{i}", '<Button-1>', 
                (lambda cmd: lambda e: self.on_button_click(cmd))(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:
            # tag로 해당 버튼의 모든 요소들 찾기
            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='#FFD700', width=3)
                    elif item_type == 'text':  # 버튼 텍스트
                        # 텍스트를 살짝 그림자 효과로
                        self.canvas.itemconfig(item, fill='#FFD700')
                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='white', width=2)
                    elif item_type == 'text':  # 버튼 텍스트
                        self.canvas.itemconfig(item, fill='white')
                logger.debug(f"Button {tag} hover effect removed")
        except Exception as e:
            logger.error(f"Error in on_button_hover: {e}")
    
    def _brighten_color(self, color):
        """색상을 밝게 변경"""
        color_map = {
            '#e74c3c': '#FF6B6B',  # 빨간색 -> 밝은 빨간색
            '#f39c12': '#FFB84D',  # 주황색 -> 밝은 주황색  
            '#e67e22': '#FF8C42',  # 점검 주황색 -> 밝은 주황색
            '#27ae60': '#58D68D',  # 초록색 -> 밝은 초록색
            '#3498db': '#5DADE2',  # 파란색 -> 밝은 파란색
            '#9b59b6': '#BB8FCE',  # 보라색 -> 밝은 보라색
            '#34495e': '#5D6D7E',  # 회색 -> 밝은 회색
            '#2ecc71': '#58D68D',  # MES 초록색 -> 밝은 초록색
            '#1abc9c': '#5DDBCB'   # 청록색 -> 밝은 청록색
        }
        return color_map.get(color, '#FFFFFF')  # 기본값은 흰색
    
    def _get_original_button_color(self, tag):
        """버튼의 원래 색상 반환 (메인 메뉴와 서브메뉴 모두 지원)"""
        # 메인 메뉴 버튼 색상 (실제 button_info와 일치)
        main_button_colors = {
            'button_0': '#3498db',  # 웹페이지 (파란색)
            'button_1': '#2ecc71',  # 탐색기 (초록색)
            'button_2': '#e67e22',  # 점검 (주황색)
            'button_3': '#f39c12',  # 자동조치 (주황색)
            'button_4': '#9b59b6'   # 설정 (보라색)
        }
        
        # 서브메뉴 버튼 색상 (점검 메뉴의 6개 버튼)
        submenu_button_colors = {
            'button_0': '#3498db',  # 스캐너 (파란색)
            'button_1': '#2ecc71',  # MES (초록색)
            'button_2': '#e74c3c',  # GDRIVE (빨간색)
            'button_3': '#f39c12',  # 인터넷 (주황색)
            'button_4': '#9b59b6',  # PC정보 (보라색)
            'button_5': '#1abc9c'   # 소프트웨어 (청록색)
        }
        
        # 웹사이트 서브메뉴인지 확인 (buttons에 color 정보가 있으면)
        if hasattr(self, 'buttons') and len(self.buttons) > 0:
            # buttons 배열에서 해당 tag에 맞는 색상 찾기
            for button in self.buttons:
                if button.get("tag") == tag and "color" in button:
                    return button["color"]
        
        # 점검 서브메뉴인지 확인 (서브메뉴는 6개 버튼을 가지므로)
        if hasattr(self, 'buttons') and len(self.buttons) == 6:
            return submenu_button_colors.get(tag, '#34495e')
        else:
            return main_button_colors.get(tag, '#34495e')
    
    def create_modern_circular_background(self):
        """현대적인 원형 배경 생성"""
        # 메인 원형 배경 (블러 효과를 위한 다중 원)
        center_x = self.local_x
        center_y = self.local_y
        
        # 외부 블러 효과를 위한 여러 원 (350px 창에 맞게 조정)
        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:
            # tkinter에서는 직접적인 alpha 지원이 제한적이므로 색상 조합으로 투명도 효과 구현
            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

        # 1. 메인 배경 원 (어두운 회색)
        self.canvas.create_oval(
            center_x - radius, center_y - radius,
            center_x + radius, center_y + radius,
            fill='#1a1a1a', outline='', tags='background'
        )

        # 2. 내부 원 (조금 더 밝은 회색)
        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'
        )

        # 3. 중앙 원 (버튼 영역)
        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'
        )

        # 4. 심플한 외곽 테두리 (점검용 색상)
        self.canvas.create_oval(
            center_x - radius, center_y - radius,
            center_x + radius, center_y + radius,
            fill='', outline='#e67e22', width=2,
            tags='background'
        )

        # 5. 내부 테두리 (얇은 라인)
        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'
        )

        # 6. 중앙 포인트 (작은 점)
        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

        # 1. 메인 배경 원 (어두운 회색)
        self.canvas.create_oval(
            center_x - radius, center_y - radius,
            center_x + radius, center_y + radius,
            fill='#1a1a1a', outline='', tags='background'
        )

        # 2. 내부 원 (조금 더 밝은 회색)
        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'
        )

        # 3. 중앙 원 (버튼 영역)
        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'
        )

        # 4. 심플한 외곽 테두리 (웹사이트용 색상)
        self.canvas.create_oval(
            center_x - radius, center_y - radius,
            center_x + radius, center_y + radius,
            fill='', outline='#2ecc71', width=2,
            tags='background'
        )

        # 5. 내부 테두리 (얇은 라인)
        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'
        )

        # 6. 중앙 포인트 (작은 점)
        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 = []
        # 실제 표시할 버튼 개수 계산 (최대 8개)
        display_count = min(len(websites), 8)
        
        for i, website in enumerate(websites):
            if i >= 8:  # 최대 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}"
            )
            
            # 버튼 정보 저장
            self.buttons.append({
                "id": btn,
                "text_id": text,
                "x": btn_x,
                "y": btn_y,
                "size": button_size,
                "command": lambda url=website['url']: self.open_website_url(url),
                "color": website['color'],
                "tag": f"button_{i}"
            })
            
            # 호버 이벤트 바인딩
            self.canvas.tag_bind(f"button_{i}", '<Enter>', 
                (lambda tag: lambda e: self.on_button_hover(tag, True))(f"button_{i}"))
            self.canvas.tag_bind(f"button_{i}", '<Leave>', 
                (lambda tag: lambda e: self.on_button_hover(tag, False))(f"button_{i}"))
            self.canvas.tag_bind(f"button_{i}", '<Button-1>', 
                (lambda cmd: lambda e: self.on_button_click(cmd))(lambda url=website['url']: self.open_website_url(url)))
            
            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):
        """지정된 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 = []

        # Phase 1 수정: 상태창의 클릭 통과 다시 활성화 (원래 상태로 복구)
        try:
            if hasattr(self, 'status_window') and self.status_window:
                if hasattr(self.status_window, 'qt_widget') and self.status_window.qt_widget:
                    from PyQt5.QtCore import Qt
                    hotkey_type = load_data('hotkey_type', 'mouse_wheel')

                    # click_status가 아닐 때만 클릭 통과 활성화
                    if hotkey_type != 'click_status':
                        current_flags = self.status_window.qt_widget.windowFlags()
                        new_flags = current_flags | Qt.WindowTransparentForInput
                        self.status_window.qt_widget.setWindowFlags(new_flags)
                        self.status_window.qt_widget.show()
                        logger.info("✅ [Phase 1] Status window click-through re-enabled")
                    else:
                        logger.info("✅ [Phase 1] Status window remains clickable (click_status mode)")
        except Exception as e:
            logger.error(f"Failed to restore status window click-through: {e}")

        # 상태창에 라디얼 메뉴 비활성 플래그 설정
        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):
        """내 PC 탐색기 열기"""
        logger.info("Opening This PC...")
        self.close_menu()
        import subprocess
        if os.name == 'nt':  # Windows - 내 PC 열기
            subprocess.Popen('explorer ::{20D04FE0-3AEA-1069-A2D8-08002B30309D}', shell=True)
        else:  # Linux/Mac
            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():
            # StatusWindow의 개선된 check_internet_connection 사용
            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 주소 가져오기
            ip = self.get_local_ip()

            # 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 주소 가져오기
            ip = self.get_local_ip()

            # 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):
        """로컬 IP 주소 가져오기"""
        try:
            # Google DNS에 연결하여 로컬 IP 확인
            with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
                s.settimeout(3)  # 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):
        """현재 연결된 WiFi SSID 가져오기 (캐시된 버전, 30초마다 갱신)"""
        current_time = time.time()

        # 캐시가 유효한 경우 (30초 이내)
        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":
                # Windows에서 현재 연결된 WiFi 정보 가져오기
                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:
                    # SSID 추출
                    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":  # macOS
                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":
                # Linux에서 iwgetid 명령어 사용
                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
                )
            else:
                result = subprocess.run(
                    command,
                    shell=True,
                    capture_output=True,
                    text=True,
                    timeout=30
                )
            
            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):
        """WiFi 프로필 XML 생성"""
        # GEO_NET은 숨겨진 네트워크
        is_hidden = (ssid == 'GEO_NET')

        # nonBroadcast 태그 추가 여부
        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):
        """WiFi 연결 (재시도 로직 포함)"""
        try:
            logger.info(f"Attempting to connect to WiFi: {ssid}")

            # 기존 프로필 삭제
            subprocess.run(['netsh', 'wlan', 'delete', 'profile', f'name={ssid}'],
                         capture_output=True, encoding='cp949')

            # 새 프로필 생성 및 추가
            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')

            # 임시 파일 삭제
            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')

            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")

            # 연결 시도 (최대 3회)
            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)

                # 숨겨진 네트워크의 경우 SSID를 명시적으로 지정
                if ssid == 'GEO_NET':
                    connect_result = subprocess.run(['netsh', 'wlan', 'connect', f'name={ssid}', f'ssid={ssid}'],
                                   capture_output=True, text=True, encoding='cp949')
                else:
                    connect_result = subprocess.run(['netsh', 'wlan', 'connect', f'name={ssid}'],
                                   capture_output=True, text=True, encoding='cp949')

                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')

                    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 설정
        wifi_configs = {
            'office': {'ssid': 'GEO_NET', 'password': 'Geo21eye@'},
            'field': {'ssid': 'GEO_MES', 'password': 'geo123!@#'}
        }
        
        wifi_config = wifi_configs.get(env_setting, wifi_configs['office'])
        
        # 0단계: 네트워크 어댑터 활성화 (6단계 시작 전)
        logger.info("Step 0: 네트워크 어댑터 활성화")
        adapter_enabled = enable_network_adapters()
        if not adapter_enabled:
            logger.warning("Network adapter activation failed, but continuing with recovery...")
        
        # 5단계 네트워크 복구 과정 (고정 IP 환경을 위해 IP reset 제거)
        recovery_steps = [
            (1, "ipconfig /flushdns", "DNS 캐시 초기화"),
            (2, "ipconfig /release", "IP 주소 해제"),
            (3, "ipconfig /renew", "IP 주소 갱신"),
            (4, "netsh winsock reset", "Winsock 리셋"),
        ]
        
        # 1-4단계 실행
        for step_num, command, description in recovery_steps:
            success = self.network_recovery_step(step_num, command, description)
            if step_num == 4 and success:
                # Winsock reset 후 잠시 대기
                time.sleep(5)
        
        # 5단계: 무선 인터페이스 확인 및 WiFi 연결
        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  # monitor_loop 함수 내부에서 time 모듈 import
            check_interval = 30  # 30초마다 확인
            retry_interval = 60  # 복구 시도 후 1분 대기
            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)  # 정상 상태에서는 30초 대기
                    
                except Exception as e:
                    logger.error(f"Network monitor error: {e}")
                    time.sleep(60)  # 에러 시 1분 대기
        
        # 별도 스레드에서 모니터링 시작
        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):
        """인터넷 연결 확인 - RadialMenu용"""
        # StatusWindow의 메서드 사용
        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):
        """현재 연결된 WiFi SSID 확인 - 캐싱으로 호출 빈도 감소"""
        import time

        # 캐시 확인 (30초간 캐시 유지)
        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:
            # netsh 명령 실행 (필요한 경우에만)
            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 : GEO Mobile Wi-Fi" 형태에서 SSID 추출
                        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):
        """현재 IP 주소 확인 - Python 네이티브 방법 (CMD 창 없음)"""
        try:
            import socket
            import psutil

            # psutil로 네트워크 인터페이스 확인 (CMD 명령 없음)
            for interface_name, addrs in psutil.net_if_addrs().items():
                for addr in addrs:
                    if addr.family == socket.AF_INET:  # IPv4만
                        ip = addr.address
                        # 로컬호스트와 APIPA 주소 제외
                        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

            # psutil이 실패하면 socket 방법으로 폴백
            try:
                with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
                    # Google DNS에 연결하여 로컬 IP 확인
                    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):
        """IP 주소가 특정 네트워크 범위에 속하는지 확인"""
        try:
            import ipaddress
            
            # IP 주소와 네트워크 범위를 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):
        """WiFi 연결 끊기 및 프로필 삭제"""
        try:
            # WiFi 연결 끊기
            logger.info(f"Disconnecting from WiFi: {ssid}")
            disconnect_result = subprocess.run([
                'netsh', 'wlan', 'disconnect'
            ], capture_output=True, text=True, encoding='cp949', timeout=10)
            
            if disconnect_result.returncode == 0:
                logger.info("WiFi disconnected successfully")
            else:
                logger.warning(f"WiFi disconnect failed: {disconnect_result.stderr}")
            
            # 잠시 대기
            time.sleep(3)
            
            # WiFi 프로필 삭제
            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)
            
            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:
            # 현재 연결된 SSID 확인
            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}")
            
            # GEO Mobile Wi-Fi 연결 확인
            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
            
            # IP 주소가 20.20.20.1/21 범위인지 확인
            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:
                    # SSID를 찾을 수 없는 경우 단순히 연결 끊기만 수행
                    subprocess.run(['netsh', 'wlan', 'disconnect'], 
                                 capture_output=True, text=True, encoding='cp949', timeout=10)
                    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):
        """서버 API를 통해 RUST 정보 조회 (fallback으로 icons.db 직접 조회)"""
        try:
            # 먼저 서버 API를 통해 조회 시도
            hostname = platform.node()
            logger.info(f"🔍 서버 API를 통한 RUST 정보 조회 시작 - hostname: {hostname}")
            
            try:
                import requests
                # 서버 API 호출
                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())}")
                    
                    # 응답 구조: {'success': True, 'asset': {...}}
                    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}")
            
            # 서버 API 실패 시 직접 icons.db 조회 (fallback)
            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}")
            
            # RUST 정보 조회
            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 정보
            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 정보 가져오기
            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}")
        
        # 원형 GUI를 위한 투명 배경 설정
        # 저장된 원형 UI 투명도 값 적용 (기본값: 0.9)
        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
                
                # 서버 URL
                server_url = "http://192.168.0.240:8800"
                
                # 시스템 정보 수집
                hostname = platform.node()
                
                # 네트워크 정보 수집 (UI 상태 표시와 동일한 로직 사용)
                try:
                    # 네트워크 상태 UI에서 사용하는 함수들을 직접 호출
                    # IP 주소 가져오기
                    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 주소 획득
                    mac_address = ':'.join(['{:02x}'.format((uuid.getnode() >> elements) & 0xff) 
                                          for elements in range(5, -1, -1)])
                    
                    # 연결 유형 결정 (UI와 동일한 로직)
                    if "WiFi" in network_info:
                        connection_type = "무선"
                        # SSID 추출
                        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}")
                    # fallback
                    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('/')
                    
                    # CPU 정보 정확히 가져오기
                    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 = ""
                                            try:
                                                version = winreg.QueryValueEx(subkey, "DisplayVersion")[0]
                                            except:
                                                pass

                                            # 유효한 프로그램 이름만 추가 (중복 제거)
                                            if display_name and display_name not in seen_software:
                                                software_list.append({
                                                    "name": display_name,
                                                    "version": version
                                                })
                                                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 포트 정보 수집
                    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:
                    # psutil이 없는 경우 기본값
                    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

            def write_debug_log(message):
                """디버그 로그 파일에 기록"""
                try:
                    timestamp = datetime.now().isoformat()
                    with open("debug.txt", "a", encoding="utf-8") as f:
                        f.write(f"[{timestamp}] [BACKGROUND_MONITORING] {message}\n")
                except:
                    pass

            write_debug_log("Background monitoring thread started")
            write_debug_log(f"Thread ID: {threading.current_thread().ident}")

            # 첫 번째 실행은 즉시 수행
            try:
                logger.info("Sending initial asset data...")
                write_debug_log("Sending initial asset data...")
                register_asset_silent()
                logger.info("Initial asset data sent successfully")
                write_debug_log("Initial asset data sent successfully")
            except Exception as e:
                logger.error(f"Initial asset data send failed: {e}")
                write_debug_log(f"Initial asset data send failed: {e}")
            
            while True:
                try:
                    write_debug_log("Starting new monitoring cycle")

                    # app.exe가 실행 중이 아니면 종료
                    if not is_app_exe_running():
                        logger.info("App.exe not running, stopping background monitoring")
                        write_debug_log("App.exe not running, stopping")
                        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")
                        write_debug_log("Stop signal received")
                        break

                    # 첫 heartbeat 즉시 전송
                    write_debug_log("Sending heartbeat...")
                    send_heartbeat()

                    # 자산 데이터 전송 (메시지 박스 없이)
                    write_debug_log("Sending asset data...")
                    register_asset_silent()

                    # 30초 간격으로 자산 업데이트 (대시보드와 동기화)
                    interval = 30
                    logger.info(f"Next asset monitoring in {interval} seconds")
                    write_debug_log(f"Waiting {interval} seconds for next cycle")

                    # 1초 단위로 체크하면서 대기, 10초마다 heartbeat 전송
                    for i in range(interval):
                        if not is_app_exe_running():
                            logger.info("App.exe not running, stopping background monitoring")
                            write_debug_log("App.exe not running during wait, stopping")
                            return
                        if '_app_stop_event' in globals() and globals()['_app_stop_event'].is_set():
                            logger.info("Received stop signal from app.exe, stopping background monitoring")
                            write_debug_log("Stop signal received during wait")
                            return

                        # 10초마다 heartbeat 전송 (서버 활성 상태 유지)
                        if i > 0 and i % 10 == 0:
                            write_debug_log(f"Sending periodic heartbeat (i={i})")
                            send_heartbeat()

                        time.sleep(1)
                    
                except Exception as e:
                    logger.error(f"Background monitoring error: {e}")
                    # 오류 발생 시 5분 후 재시도
                    time.sleep(300)
        
        def is_app_exe_running():
            """app.exe 프로세스가 실행 중인지 확인 (개발/운영 환경 모두 지원)"""
            try:
                import psutil
                import os

                # 개발 환경 체크: LAUNCHED_BY_APP 환경변수가 설정되어 있으면 app.py가 실행한 것
                if os.environ.get('LAUNCHED_BY_APP') == '1':
                    # app.py가 실행했다면, python.exe나 app.exe 중 하나를 찾음
                    for proc in psutil.process_iter(['name', 'cmdline']):
                        try:
                            if proc.info['name']:
                                name_lower = proc.info['name'].lower()
                                # app.exe 또는 python.exe app.py를 찾음
                                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:
                    # 독립 실행 모드 (main.py만 실행된 경우) - 항상 True
                    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():
            """서버에 간단한 heartbeat 신호 전송 (활성 상태 유지용)"""
            try:
                import requests
                import datetime
                import uuid

                server_url = "http://192.168.0.240:8800"
                hostname = platform.node()

                # IP 주소 가져오기
                try:
                    # Google DNS에 연결하여 로컬 IP 확인
                    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 주소 가져오기
                mac_address = ':'.join(['{:02x}'.format((uuid.getnode() >> elements) & 0xff)
                                      for elements in range(5, -1, -1)])

                # 간단한 heartbeat 데이터
                heartbeat_data = {
                    "hostname": hostname,
                    "ip_address": ip_address,
                    "mac_address": mac_address,
                    "status": "active",
                    "timestamp": datetime.datetime.now().isoformat(),
                    "type": "heartbeat"
                }

                # 서버로 heartbeat 전송 (올바른 엔드포인트 사용)
                try:
                    response = requests.post(f"{server_url}/api/asset/heartbeat", json=heartbeat_data, timeout=3)
                    if response.status_code == 200:
                        logger.info(f"💓 Heartbeat sent successfully to /api/asset/heartbeat")
                    else:
                        # fallback으로 /api/asset/update 사용
                        response = requests.post(f"{server_url}/api/asset/update", json=heartbeat_data, timeout=3)
                        if response.status_code == 200:
                            logger.info(f"💓 Heartbeat sent via /api/asset/update")
                except:
                    pass  # 조용히 실패

            except Exception as e:
                logger.debug(f"Heartbeat error (ignored): {e}")

        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

            # app.exe 프로세스 확인
            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
                
                # 서버 URL
                server_url = "http://192.168.0.240:8800"
                
                # 시스템 정보 수집
                hostname = platform.node()
                
                # 네트워크 정보 수집 (UI 상태 표시와 동일한 로직 사용)
                try:
                    # 네트워크 상태 UI에서 사용하는 함수들을 직접 호출
                    # IP 주소 가져오기
                    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 주소 획득
                    mac_address = ':'.join(['{:02x}'.format((uuid.getnode() >> elements) & 0xff) 
                                          for elements in range(5, -1, -1)])
                    
                    # 연결 유형 결정 (UI와 동일한 로직)
                    if "WiFi" in network_info:
                        connection_type = "무선"
                        # SSID 추출
                        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}")
                    # fallback
                    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('/')
                    
                    # CPU 정보 정확히 가져오기
                    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 = ""
                                            try:
                                                version = winreg.QueryValueEx(subkey, "DisplayVersion")[0]
                                            except:
                                                pass

                                            # 유효한 프로그램 이름만 추가 (중복 제거)
                                            if display_name and display_name not in seen_software:
                                                software_list.append({
                                                    "name": display_name,
                                                    "version": version
                                                })
                                                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 포트 정보 수집
                    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}")
                    
                    # 네트워크 인터페이스 정보 (UI 기반으로 단순화)
                    network_interfaces = [{
                        "name": network_info,  # UI에 표시되는 정보 그대로 사용
                        "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(),
                        "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)}")
                    
                    # 서버 연결 가능 여부 체크
                    def check_server_connectivity():
                        """서버 연결 가능 여부 확인"""
                        try:
                            import socket
                            # 포트 8800으로 TCP 연결 시도
                            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                            sock.settimeout(3)  # 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)  # 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):
        """CPU 정보를 정확하게 가져오기"""
        try:
            # Windows에서 WMI를 통해 CPU 정보 가져오기
            import subprocess
            result = subprocess.run([
                'wmic', 'cpu', 'get', 'name', '/format:value'
            ], capture_output=True, text=True)
            
            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
            
            # WMI가 실패하면 레지스트리에서 가져오기
            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
                
            # cpuid 명령어 시도 (powershell)
            try:
                result = subprocess.run([
                    'powershell', '-Command',
                    'Get-WmiObject -Class Win32_Processor | Select-Object -ExpandProperty Name'
                ], capture_output=True, text=True)
                
                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}")
        
        # 모든 방법이 실패하면 기본 platform 정보 사용
        return platform.processor() or "Unknown CPU"
    
    def get_installed_software(self):
        """설치된 소프트웨어 목록 가져오기 (Windows 설치/제거 프로그램 수준)"""
        software_list = []
        
        try:
            # Windows 레지스트리에서 설치된 프로그램 정보 가져오기
            import winreg
            
            # 64비트 프로그램들
            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")
                                
                                # 1. SystemComponent 확인 (가장 중요한 필터)
                                try:
                                    system_component, _ = winreg.QueryValueEx(subkey, "SystemComponent")
                                    if system_component == 1:  # SystemComponent가 1이면 제어판에 표시되지 않음
                                        winreg.CloseKey(subkey)
                                        continue
                                except FileNotFoundError:
                                    pass  # SystemComponent가 없으면 일반 프로그램
                                
                                # 2. WindowsInstaller 확인
                                try:
                                    windows_installer, _ = winreg.QueryValueEx(subkey, "WindowsInstaller")
                                    if windows_installer == 1:
                                        # WindowsInstaller가 1인 경우 추가 필터링
                                        try:
                                            no_remove, _ = winreg.QueryValueEx(subkey, "NoRemove")
                                            if no_remove == 1:  # 제거할 수 없는 항목
                                                winreg.CloseKey(subkey)
                                                continue
                                        except FileNotFoundError:
                                            pass
                                except FileNotFoundError:
                                    pass
                                
                                # 3. ParentKeyName 확인 (업데이트나 패치)
                                try:
                                    parent_key, _ = winreg.QueryValueEx(subkey, "ParentKeyName")
                                    if parent_key:  # ParentKeyName이 있으면 업데이트/패치
                                        winreg.CloseKey(subkey)
                                        continue
                                except FileNotFoundError:
                                    pass
                                
                                # 3.5. ReleaseType 확인 (업데이트/패치 타입)
                                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
                                
                                # 4. 시스템 업데이트, 보안 패치, 개발도구 컴포넌트 제외
                                display_lower = display_name.lower()
                                if any(skip in display_lower for skip in [
                                    # Windows 업데이트 관련
                                    'security update', 'hotfix', 'update for', 'kb', 'service pack',
                                    '보안 업데이트', '핫픽스', '서비스 팩',
                                    
                                    # Microsoft 런타임 및 재배포 가능 패키지
                                    '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',
                                    
                                    # Office 공유 구성 요소
                                    'microsoft office shared', 'office shared',
                                    'microsoft report viewer', 'report viewer',
                                    'microsoft office click-to-run',
                                    
                                    # Windows 기본 구성 요소
                                    'windows driver package', 'device driver',
                                    'microsoft silverlight', 'windows live',
                                    'microsoft games for windows',
                                    
                                    # 언어팩 및 기타
                                    'language pack', 'mui', 'lcid',
                                    'windows installer', 'msi development tools',
                                    
                                    # .NET Framework 세부
                                    '.net framework', 'dotnet',
                                    
                                    # Visual Studio 구성 요소
                                    'microsoft visual studio', 'vs_', 'buildtools',
                                    
                                    # Windows 앱 스토어 앱들 (일부)
                                    'ms-resource:', 'packagefamilyname'
                                ]):
                                    winreg.CloseKey(subkey)
                                    continue
                                
                                # 5. Microsoft 시스템 구성 요소 추가 필터링
                                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
                                
                                # 5. 빈 DisplayName 제외
                                if not display_name.strip():
                                    winreg.CloseKey(subkey)
                                    continue
                                
                                # 6. GUID만 있는 이름 제외 (예: {GUID} 형태)
                                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
                                
                                # 1. InstallDate 필드 확인
                                try:
                                    install_date_raw, _ = winreg.QueryValueEx(subkey, "InstallDate")
                                    if install_date_raw:
                                        # YYYYMMDD 형식을 YYYY-MM-DD로 변환
                                        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
                                
                                # 2. InstallLocation의 폴더 생성 날짜 확인
                                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
                                
                                # 3. 레지스트리 키 자체의 마지막 수정 시간 사용
                                if not install_date or install_date == "Unknown":
                                    try:
                                        # Windows 레지스트리 키의 마지막 수정 시간
                                        # 이 방법은 정확도가 떨어질 수 있지만 대체 방법으로 사용
                                        key_info = winreg.QueryInfoKey(subkey)
                                        if key_info and len(key_info) > 2:
                                            # FILETIME을 datetime으로 변환
                                            filetime = key_info[2]
                                            if filetime:
                                                # Windows FILETIME은 1601년 1월 1일부터의 100나노초 단위
                                                # Python datetime은 1970년 1월 1일부터의 초 단위
                                                # 변환: (filetime / 10000000) - 11644473600
                                                timestamp = (filetime / 10000000.0) - 11644473600
                                                if timestamp > 0:
                                                    install_date = datetime.datetime.fromtimestamp(timestamp).strftime('%Y-%m-%d')
                                    except Exception:
                                        pass
                                
                                # 4. UninstallString의 실행 파일 생성 날짜 확인 (마지막 대안)
                                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
                                
                                # 최종적으로 날짜가 없으면 Unknown
                                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}")
        
        # PowerShell이나 기본 소프트웨어는 추가하지 않음 (레지스트리만 사용)
        # appwiz.cpl과 동일한 수준을 유지
        
        # 중복 제거 및 정렬
        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):
        """네트워크 상태 UI 투명도 조절"""
        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

                # PyQt5 창에 실시간 적용
                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}")

                # tkinter 창에 실시간 적용
                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}")
        
        # 원형 GUI를 위한 투명 배경 설정
        # 저장된 원형 UI 투명도 값 적용 (기본값: 0.9)
        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}")

        # 원형 GUI를 위한 투명 배경 설정
        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())

        # 캔버스 생성 (overlay 위에 생성)
        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

        # 1. 메인 배경 원 (어두운 회색)
        self.canvas.create_oval(
            center_x - radius, center_y - radius,
            center_x + radius, center_y + radius,
            fill='#1a1a1a', outline='', tags='background'
        )

        # 2. 내부 원 (조금 더 밝은 회색)
        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'
        )

        # 3. 중앙 원 (버튼 영역)
        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'
        )

        # 4. 심플한 외곽 테두리 (프로그램용 색상)
        self.canvas.create_oval(
            center_x - radius, center_y - radius,
            center_x + radius, center_y + radius,
            fill='', outline='#9b59b6', width=2,
            tags='background'
        )

        # 5. 내부 테두리 (얇은 라인)
        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'
        )

        # 6. 중앙 포인트 (작은 점)
        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:  # 최대 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}"
                )

                # 버튼 정보 저장
                self.buttons.append({
                    "id": btn,
                    "text_id": text,
                    "x": btn_x,
                    "y": btn_y,
                    "size": button_size,
                    "text": program['text'],
                    "path": program['path'],
                    "command": lambda path=program['path'], name=program['text']: self.execute_program(path, name),
                    "color": button_color,
                    "tag": f"button_{i}"
                })

                # 호버 이벤트 바인딩 (자동조치와 동일한 방식)
                self.canvas.tag_bind(f"button_{i}", '<Enter>',
                    (lambda tag: lambda e: self.on_button_hover(tag, True))(f"button_{i}"))
                self.canvas.tag_bind(f"button_{i}", '<Leave>',
                    (lambda tag: lambda e: self.on_button_hover(tag, False))(f"button_{i}"))
                self.canvas.tag_bind(f"button_{i}", '<Button-1>',
                    (lambda path, name: lambda e: self.execute_program(path, name))(program['path'], program['text']))

                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

        # 버튼 클릭 확인은 tag_bind로 처리됨
        # 빈 공간 클릭 시 메뉴 닫기 (원형 내부의 빈 공간 클릭)
        for btn in self.buttons:
            btn_dist = math.sqrt((x - btn["x"])**2 + (y - btn["y"])**2)
            if btn_dist <= btn["size"] / 2:
                # 버튼 영역은 tag_bind로 처리
                return

        # 원형 메뉴 전체 영역 확인 (반지름 160) - 빈 공간 클릭
        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':  # Windows
                subprocess.Popen([program_path], shell=True)
            else:  # Linux/Mac
                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  # 6개 버튼을 위해 크기 확대
        
        # 멀티 모니터 지원을 위한 가상 화면 정보 가져오기
        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}")
        
        # 원형 GUI를 위한 투명 배경 설정 (서브메뉴)
        # 저장된 원형 UI 투명도 값 적용 (기본값: 0.9)
        self.overlay.attributes('-alpha', 0.9)  # 90% 투명도
        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):
        """서브메뉴 버튼 생성 (3개)"""
        logger.info("Creating submenu buttons...")
        
        radius = 110  # 중심에서 버튼까지의 거리 (확대)
        button_size = 70  # 버튼 크기 (확대)
        
        # 서브메뉴 버튼 정보 (6개)
        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):
            # 각도 계산 (위쪽부터 시작, 6개 버튼이므로 60도 간격)
            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.canvas.tag_bind(f"button_{i}", '<Enter>', lambda e, tag=f"button_{i}": self.on_button_hover(tag, True))
            self.canvas.tag_bind(f"button_{i}", '<Leave>', lambda e, tag=f"button_{i}": self.on_button_hover(tag, False))
            # 클릭 이벤트 바인딩 추가
            self.canvas.tag_bind(f"button_{i}", '<Button-1>', 
                (lambda cmd: lambda e: self.on_button_click(cmd))(btn_info["command"]))
        
        logger.info(f"Created {len(self.buttons)} submenu buttons")
    
    def open_scanner(self):
        """스캐너 COM 포트 스캔"""
        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
            
            # COM 포트 스캔 실행
            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):
        """MES 통신 테스트 (환경에 따른 서버 주소)"""
        # 사용 환경에 따른 서버 주소 설정
        environment = load_data('environment', 'office')
        if environment == 'office':
            target_ip = "192.168.0.250"
            test_port = 80  # 사무용은 HTTP 포트로 핑 테스트
        else:  # field
            target_ip = "10.10.10.10"
            test_port = 445  # 현장용은 SMB 포트로 테스트

        logger.info(f"Testing MES connection to {target_ip}:{test_port} (environment: {environment})...")
        self.close_menu()

        try:
            import socket
            import time

            timeout = 3  # 3초 타임아웃
            
            start_time = time.time()
            
            # TCP 소켓 테스트
            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):
        """GDRIVE 통신 테스트 (192.168.2.4)"""
        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  # SMB 포트로 테스트 (일반적인 파일 서버)
            timeout = 3  # 3초 타임아웃
            
            start_time = time.time()
            
            # TCP 소켓 테스트
            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:
                    # SMB가 안되면 일반 네트워크 연결로 재시도
                    try:
                        # 일반 네트워크 포트로 연결 시도 (예: HTTP 80포트)
                        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:
                            # HTTPS 443 포트 시도
                            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 = []
            
            # Windows에서 설치된 프로그램 목록 가져오기
            if platform.system() == "Windows":
                try:
                    # PowerShell을 사용하여 레지스트리에서 직접 읽기 (appwiz.cpl과 동일한 목록)
                    logger.info("Getting software list from registry...")
                    ps_command = """
                    $programs = @()
                    
                    # 64비트 프로그램
                    $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
                    
                    # 32비트 프로그램 (64비트 시스템에서)
                    $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
                    
                    # 중복 제거 및 정렬 (DisplayName으로)
                    $uniquePrograms = @{}
                    $programs | ForEach-Object {
                        if ($_.DisplayName -and -not $uniquePrograms.ContainsKey($_.DisplayName)) {
                            $uniquePrograms[$_.DisplayName] = $_
                        }
                    }
                    
                    $uniquePrograms.Values | Sort-Object DisplayName | ForEach-Object {
                        # CSV 형식으로 출력 (구분자: |) - 날짜 필드 제거
                        Write-Output "$($_.DisplayName)|$($_.DisplayVersion)|$($_.Publisher)"
                    }
                    """
                    
                    result = subprocess.run(
                        ["powershell", "-ExecutionPolicy", "Bypass", "-Command", ps_command],
                        capture_output=True,
                        text=True,
                        encoding='cp949'  # Windows 한글 인코딩
                    )
                    
                    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
            
            # 1. 메인 배경 원 (어두운 회색)
            self.canvas.create_oval(
                center_x - radius, center_y - radius,
                center_x + radius, center_y + radius,
                fill='#1a1a1a', outline='', tags='background'
            )
            
            # 2. 내부 원 (조금 더 밝은 회색)
            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'
            )
            
            # 3. 중앙 원 (버튼 영역)
            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'
            )
            
            # 4. 심플한 외곽 테두리 (단일 네온 컬러)
            self.canvas.create_oval(
                center_x - radius, center_y - radius,
                center_x + radius, center_y + radius,
                fill='', outline='#4a9eff', width=2,
                tags='background'
            )
            
            # 5. 내부 테두리 (얇은 라인)
            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'
            )
            
            # 6. 중앙 포인트 (작은 점)
            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):
        """두 색상을 블렌딩"""
        # 간단한 색상 블렌딩 (hex to hex)
        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.canvas.tag_bind(f"button_{i}", '<Enter>', 
                (lambda tag: lambda e: self.on_button_hover(tag, True))(f"button_{i}"))
            self.canvas.tag_bind(f"button_{i}", '<Leave>', 
                (lambda tag: lambda e: self.on_button_hover(tag, False))(f"button_{i}"))
            self.canvas.tag_bind(f"button_{i}", '<Button-1>', 
                (lambda cmd: lambda e: self.on_button_click(cmd))(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")
    
    # Fix 기능들
    def network_fix(self):
        """네트워크 Fix - 수동으로 네트워크 복구 및 MES 통신 테스트"""
        logger.info("Network Fix initiated")
        self.close_menu()
        
        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❌ 네트워크 복구 실패")
                
                # MES 통신 테스트
                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):
        """브라우저 Fix - 크롬, 엣지 브라우저 캐시/쿠키 삭제"""
        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)
                
                # Chrome 캐시/쿠키 삭제
                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}")
                
                # Edge 캐시/쿠키 삭제
                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):
        """프린터 Fix - 레지스트리 수정 및 스풀러 재시작"""
        logger.info("Printer Fix initiated")
        self.close_menu()
        
        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)
                
                # 1. 레지스트리 수정
                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)
                    if result.returncode == 0:
                        log_progress("✅ 레지스트리 설정 수정 완료")
                    else:
                        log_progress(f"⚠️  레지스트리 수정 경고: {result.stderr.strip()}")
                except Exception as e:
                    log_progress(f"❌ 레지스트리 수정 실패: {e}")
                
                # 2. 스풀러 서비스 중지
                log_progress("\n🔄 Print Spooler 서비스 중지 중...")
                try:
                    result = subprocess.run(['net', 'stop', 'spooler'], 
                                          capture_output=True, text=True, timeout=30)
                    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)
                
                # 3. 스풀러 서비스 시작
                log_progress("\n🚀 Print Spooler 서비스 시작 중...")
                try:
                    result = subprocess.run(['net', 'start', 'spooler'], 
                                          capture_output=True, text=True, timeout=30)
                    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}")
                
                # 4. 추가 프린터 장애 해결 작업
                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):
        """MES Fix - pf_profile.ini 파일의 IP 설정 수정"""
        logger.info("MES Fix initiated")
        self.close_menu()
        
        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("500x350")
                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=18, 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)
                
                # 사용 환경 확인
                env_setting = load_data('environment', 'office')
                log_progress(f"현재 사용 환경: {env_setting}")
                
                # IP 설정 결정
                if env_setting == 'office':
                    target_ip = "192.168.0.250,14333"
                    env_name = "사무용"
                else:  # field
                    target_ip = "10.10.10.10,14333"
                    env_name = "현장용"
                
                log_progress(f"설정할 IP: {target_ip} ({env_name})")
                
                # pf_profile.ini 파일 경로
                profile_path = "C:\\geopop\\commlib\\pf_profile.ini"
                
                log_progress(f"\n📁 파일 경로: {profile_path}")
                
                # 파일 존재 확인
                if not os.path.exists(profile_path):
                    log_progress("❌ pf_profile.ini 파일을 찾을 수 없습니다!")
                    log_progress("   파일 경로를 확인해주세요.")
                    
                    # 장애 시 추천 사항
                    log_progress("\n📋 파일이 없을 때 해결방법:")
                    log_progress("1. MES 프로그램이 정상 설치되었는지 확인")
                    log_progress("2. C:\\geopop\\commlib\\ 폴더 존재 여부 확인")
                    log_progress("3. MES 프로그램 재설치 고려")
                    return
                
                log_progress("✅ pf_profile.ini 파일 발견")
                
                # 파일 백업
                backup_path = profile_path + ".backup"
                try:
                    import shutil
                    shutil.copy2(profile_path, backup_path)
                    log_progress(f"✅ 백업 파일 생성: {backup_path}")
                except Exception as e:
                    log_progress(f"⚠️  백업 생성 실패: {e}")
                
                # 파일 읽기 및 수정
                log_progress("\n🔧 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(profile_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
                    
                    # [SERVER] 섹션 찾기 및 IP 수정
                    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= 설정을 찾을 수 없습니다!")
                        log_progress("\n📋 IP 설정이 없을 때 해결방법:")
                        log_progress("1. [SERVER] 섹션 존재 여부 확인")
                        log_progress("2. 파일 형식이 올바른지 확인")
                        log_progress("3. MES 설정 파일 복원 고려")
                        return
                    
                    # 파일 저장 (원본 인코딩 유지)
                    with open(profile_path, 'w', encoding=used_encoding) as f:
                        f.writelines(lines)
                    
                    log_progress("✅ 파일 저장 완료")
                    
                except Exception as e:
                    log_progress(f"❌ 파일 수정 실패: {e}")
                    log_progress("\n📋 파일 수정 실패 시 해결방법:")
                    log_progress("1. 관리자 권한으로 프로그램 실행")
                    log_progress("2. 파일이 사용 중인지 확인 후 종료")
                    log_progress("3. 백업 파일에서 복원")
                    return
                
                log_progress("\n" + "=" * 50)
                log_progress("🎉 MES Fix 완료!")
                log_progress(f"ℹ️  MES 서버 IP가 {env_name} 환경에 맞게 설정되었습니다.")
                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:
            # 원형 UI 투명도 고정값 적용
            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)  # #4a9eff 기준
            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):
        """MES 통신 테스트"""
        try:
            # 현재 환경 설정에 따른 MES 서버 정보
            env_setting = load_data('environment', 'office')
            
            if env_setting == 'office':
                mes_server = "192.168.0.250"
                mes_port = 14333
            else:  # field
                mes_server = "10.10.10.10"
                mes_port = 14333
            
            # 간단한 TCP 연결 테스트
            import socket
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(5)  # 5초 타임아웃
            
            try:
                result = sock.connect_ex((mes_server, mes_port))
                sock.close()
                return result == 0  # 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:
        # 먼저 pynput 시도
        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:
        # pynput이 없으면 mouse/keyboard 라이브러리 사용
        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")
            # mouse/keyboard 라이브러리도 없으면 기본 tkinter 이벤트 사용
            use_pynput = False
            use_basic = True
    
    radial_menu = RadialMenu()
    hotkey_type = load_data('hotkey_type', 'mouse_wheel')
    
    def show_radial_menu():
        """라디얼 메뉴 표시"""
        if use_basic:
            # 기본 tkinter 방식 - 화면 중앙에 표시
            import tkinter as tk
            root = tk.Tk()
            x = root.winfo_screenwidth() // 2
            y = root.winfo_screenheight() // 2
            root.destroy()
        elif use_pynput:
            # pynput 사용 시 현재 마우스 위치 가져오기
            controller = pynput_mouse.Controller()
            x, y = controller.position
        else:
            # mouse 라이브러리 사용
            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:
        # 기본 tkinter 이벤트 - 마우스 휠 클릭만 지원
        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:
        # 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']:
            # F1~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':
            # 상태창 클릭은 StatusWindow 클래스에서 처리
            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:
        # 기존 mouse/keyboard 라이브러리 사용
        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):
    """특정 RadialMenu 인스턴스에 대해 입력 리스너 설정"""
    logger.info(f"Setting up input listener for hotkey type: {hotkey_type}")
    
    # 먼저 기존 리스너 정리
    if hasattr(radial_menu, 'cleanup_listeners'):
        radial_menu.cleanup_listeners()
    
    try:
        # 먼저 pynput 시도
        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:
        # pynput이 없으면 mouse/keyboard 라이브러리 사용
        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")
            # PyQt 창이 있는 경우 클릭 리스너 재설정
            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:
                    # PyQt 창의 현재 위치와 크기 가져오기
                    x = status_window.qt_widget.x()
                    y = status_window.qt_widget.y() 
                    width = status_window.qt_widget.width()
                    height = status_window.qt_widget.height()
                    # PyQt 클릭 리스너 재설정
                    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:
        # 기존 mouse/keyboard 라이브러리 사용
        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.canvas.tag_bind(f"button_{i}", '<Enter>', 
                (lambda tag: lambda e: self.on_button_hover(tag, True))(f"button_{i}"))
            self.canvas.tag_bind(f"button_{i}", '<Leave>', 
                (lambda tag: lambda e: self.on_button_hover(tag, False))(f"button_{i}"))
            self.canvas.tag_bind(f"button_{i}", '<Button-1>', 
                (lambda cmd: lambda e: self.on_button_click(cmd))(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")
    
    # Fix 기능들
    def network_fix(self):
        """네트워크 Fix - 수동으로 네트워크 복구 및 MES 통신 테스트"""
        logger.info("Network Fix initiated")
        self.close_menu()
        
        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❌ 네트워크 복구 실패")
                
                # MES 통신 테스트
                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):
        """브라우저 Fix - 크롬, 엣지 브라우저 캐시/쿠키 삭제"""
        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)
                
                # Chrome 캐시/쿠키 삭제
                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}")
                
                # Edge 캐시/쿠키 삭제
                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):
        """프린터 Fix - 레지스트리 수정 및 스풀러 재시작"""
        logger.info("Printer Fix initiated")
        self.close_menu()
        
        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)
                
                # 1. 레지스트리 수정
                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)
                    if result.returncode == 0:
                        log_progress("✅ 레지스트리 설정 수정 완료")
                    else:
                        log_progress(f"⚠️  레지스트리 수정 경고: {result.stderr.strip()}")
                except Exception as e:
                    log_progress(f"❌ 레지스트리 수정 실패: {e}")
                
                # 2. 스풀러 서비스 중지
                log_progress("\n🔄 Print Spooler 서비스 중지 중...")
                try:
                    result = subprocess.run(['net', 'stop', 'spooler'], 
                                          capture_output=True, text=True, timeout=30)
                    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)
                
                # 3. 스풀러 서비스 시작
                log_progress("\n🚀 Print Spooler 서비스 시작 중...")
                try:
                    result = subprocess.run(['net', 'start', 'spooler'], 
                                          capture_output=True, text=True, timeout=30)
                    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}")
                
                # 4. 추가 프린터 장애 해결 작업
                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):
        """MES Fix - pf_profile.ini 파일의 IP 설정 수정"""
        logger.info("MES Fix initiated")
        self.close_menu()
        
        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("500x350")
                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=18, 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)
                
                # 사용 환경 확인
                env_setting = load_data('environment', 'office')
                log_progress(f"현재 사용 환경: {env_setting}")
                
                # IP 설정 결정
                if env_setting == 'office':
                    target_ip = "192.168.0.250,14333"
                    env_name = "사무용"
                else:  # field
                    target_ip = "10.10.10.10,14333"
                    env_name = "현장용"
                
                log_progress(f"설정할 IP: {target_ip} ({env_name})")
                
                # pf_profile.ini 파일 경로
                profile_path = "C:\\geopop\\commlib\\pf_profile.ini"
                
                log_progress(f"\n📁 파일 경로: {profile_path}")
                
                # 파일 존재 확인
                if not os.path.exists(profile_path):
                    log_progress("❌ pf_profile.ini 파일을 찾을 수 없습니다!")
                    log_progress("   파일 경로를 확인해주세요.")
                    
                    # 장애 시 추천 사항
                    log_progress("\n📋 파일이 없을 때 해결방법:")
                    log_progress("1. MES 프로그램이 정상 설치되었는지 확인")
                    log_progress("2. C:\\geopop\\commlib\\ 폴더 존재 여부 확인")
                    log_progress("3. MES 프로그램 재설치 고려")
                    return
                
                log_progress("✅ pf_profile.ini 파일 발견")
                
                # 파일 백업
                backup_path = profile_path + ".backup"
                try:
                    import shutil
                    shutil.copy2(profile_path, backup_path)
                    log_progress(f"✅ 백업 파일 생성: {backup_path}")
                except Exception as e:
                    log_progress(f"⚠️  백업 생성 실패: {e}")
                
                # 파일 읽기 및 수정
                log_progress("\n🔧 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(profile_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
                    
                    # [SERVER] 섹션 찾기 및 IP 수정
                    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= 설정을 찾을 수 없습니다!")
                        log_progress("\n📋 IP 설정이 없을 때 해결방법:")
                        log_progress("1. [SERVER] 섹션 존재 여부 확인")
                        log_progress("2. 파일 형식이 올바른지 확인")
                        log_progress("3. MES 설정 파일 복원 고려")
                        return
                    
                    # 파일 저장 (원본 인코딩 유지)
                    with open(profile_path, 'w', encoding=used_encoding) as f:
                        f.writelines(lines)
                    
                    log_progress("✅ 파일 저장 완료")
                    
                except Exception as e:
                    log_progress(f"❌ 파일 수정 실패: {e}")
                    log_progress("\n📋 파일 수정 실패 시 해결방법:")
                    log_progress("1. 관리자 권한으로 프로그램 실행")
                    log_progress("2. 파일이 사용 중인지 확인 후 종료")
                    log_progress("3. 백업 파일에서 복원")
                    return
                
                log_progress("\n" + "=" * 50)
                log_progress("🎉 MES Fix 완료!")
                log_progress(f"ℹ️  MES 서버 IP가 {env_name} 환경에 맞게 설정되었습니다.")
                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:
            # 원형 UI 투명도 고정값 적용
            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):
        """MES 통신 테스트"""
        try:
            # 현재 환경 설정에 따른 MES 서버 정보
            env_setting = load_data('environment', 'office')
            
            if env_setting == 'office':
                mes_server = "192.168.0.250"
                mes_port = 14333
            else:  # field
                mes_server = "10.10.10.10"
                mes_port = 14333
            
            # 간단한 TCP 연결 테스트
            import socket
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(5)  # 5초 타임아웃
            
            try:
                result = sock.connect_ex((mes_server, mes_port))
                sock.close()
                return result == 0  # 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():
    """모든 UI 리소스 정리 (스레드 재시작 시 사용) - 강화된 버전"""
    global radial_menu, status_window

    logger.info("Starting comprehensive UI resource cleanup...")
    
    # 1. RadialMenu 정리 (먼저 처리)
    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()
            
            # StatusWindow 정리
            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
            
            # SettingsWindow 정리
            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}")
    
    # 2. 독립적인 StatusWindow 정리
    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}")
    
    # 3. 모든 tkinter 최상위 창 강제 정리
    try:
        import tkinter as tk
        logger.info("Cleaning up all Tkinter windows...")
        
        # 기본 루트가 있으면 모든 자식 창 정리
        if hasattr(tk, '_default_root') and tk._default_root:
            children = list(tk._default_root.winfo_children())
            for child in children:
                if isinstance(child, tk.Toplevel):
                    try:
                        child.destroy()
                        logger.info(f"Destroyed Toplevel window: {child}")
                    except Exception as e:
                        logger.error(f"Error destroying Toplevel: {e}")
        
        # 숨어있는 창들도 찾아서 정리
        try:
            all_windows = tk._default_root.winfo_children() if tk._default_root else []
            for window in all_windows:
                try:
                    if hasattr(window, 'destroy'):
                        window.destroy()
                except:
                    pass
        except:
            pass
            
    except Exception as e:
        logger.error(f"Error cleaning up tkinter windows: {e}")
    
    # 4. PyQt 애플리케이션 정리
    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:
        # PyQt5가 설치되지 않은 경우
        pass
    except Exception as e:
        logger.error(f"Error cleaning up PyQt: {e}")
    
    # 5. PySimpleGUI 정리
    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}")
    
    # 6. 전역 리스너 정리 (확실하게)
    try:
        # mouse 라이브러리 후크 정리
        try:
            import mouse
            mouse.unhook_all()
            logger.info("Mouse hooks cleaned up")
        except:
            pass
            
        # keyboard 라이브러리 후크 정리
        try:
            import keyboard
            keyboard.unhook_all()
            logger.info("Keyboard hooks cleaned up")
        except:
            pass
            
        # pynput 리스너 정리는 이미 RadialMenu에서 처리됨
            
    except Exception as e:
        logger.error(f"Error cleaning up global listeners: {e}")
    
    # 7. 살아있는 스레드 강제 종료 (daemon이 아닌 것들)
    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:
                    # join으로 종료 대기 (최대 1초)
                    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}")

    # 8. 전역 변수 정리
    globals()['radial_menu'] = None
    globals()['status_window'] = None

    # 9. 강제 가비지 컬렉션 (여러 번 실행)
    import gc
    for i in range(3):  # 3회 반복 실행
        collected = gc.collect()
        logger.info(f"Garbage collection pass {i+1}: collected {collected} objects")

    # 10. 최종 정리 - Python 인터프리터 플러시
    try:
        sys.stdout.flush()
        sys.stderr.flush()
    except:
        pass

    logger.info("Comprehensive UI cleanup completed successfully")

def main():
    """메인 함수"""
    # 종료 핸들러 등록 (exe에서 백그라운드 프로세스 방지)
    atexit.register(atexit_handler)
    signal.signal(signal.SIGTERM, force_exit_handler)
    signal.signal(signal.SIGINT, force_exit_handler)
    if hasattr(signal, 'SIGBREAK'):  # Windows
        signal.signal(signal.SIGBREAK, force_exit_handler)

    # 종료 신호 확인
    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...")

    # PyQt5 애플리케이션 생성
    from PyQt5.QtWidgets import QApplication
    import sys
    app = QApplication.instance() or QApplication(sys.argv)

    # tkinter 루트 창도 생성 (원형 메뉴 호환성을 위해)
    root = tk.Tk()
    root.withdraw()  # 메인 윈도우 숨기기

    # 애플리케이션 종료 시 정리 핸들러 등록
    app.aboutToQuit.connect(cleanup_all_ui)
    root.protocol("WM_DELETE_WINDOW", lambda: (cleanup_all_ui(), root.quit()))
    
    # set_root 함수가 있으면 호출 (app.py에서 제공)
    if 'set_root' in globals():
        set_root(root)
    
    # 버전 정보 출력 (main.py 자체 버전 사용)
    logger.info(f"GeoMedical Helper v{MAIN_VERSION} Started")
    print(f"GeoMedical Helper v{MAIN_VERSION} Started")
    
    try:
        # mouse 라이브러리 확인
        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)  # 정리가 완료될 때까지 대기
        
        # RadialMenu 인스턴스 생성
        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, '마우스 휠 버튼을 클릭하면 퀵 메뉴가 나타납니다.')
        
        # 안내 메시지 - 제거됨
        
        # 메인 루프 실행 (tkinter 우선, PyQt5 백그라운드)
        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:
        # 종료 시 모든 UI 정리
        logger.info("Main function ending, cleaning up UI...")
        cleanup_all_ui()


# psutil이 없는 경우를 위한 대체 구현
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
            
            # 1. 메인 배경 원 (어두운 회색)
            self.canvas.create_oval(
                center_x - radius, center_y - radius,
                center_x + radius, center_y + radius,
                fill='#1a1a1a', outline='', tags='background'
            )
            
            # 2. 내부 원 (조금 더 밝은 회색)
            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'
            )
            
            # 3. 중앙 원 (버튼 영역)
            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'
            )
            
            # 4. 심플한 외곽 테두리 (단일 네온 컬러)
            self.canvas.create_oval(
                center_x - radius, center_y - radius,
                center_x + radius, center_y + radius,
                fill='', outline='#4a9eff', width=2,
                tags='background'
            )
            
            # 5. 내부 테두리 (얇은 라인)
            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'
            )
            
            # 6. 중앙 포인트 (작은 점)
            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):
        """두 색상을 블렌딩"""
        # 간단한 색상 블렌딩 (hex to hex)
        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.canvas.tag_bind(f"button_{i}", '<Enter>', 
                (lambda tag: lambda e: self.on_button_hover(tag, True))(f"button_{i}"))
            self.canvas.tag_bind(f"button_{i}", '<Leave>', 
                (lambda tag: lambda e: self.on_button_hover(tag, False))(f"button_{i}"))
            self.canvas.tag_bind(f"button_{i}", '<Button-1>', 
                (lambda cmd: lambda e: self.on_button_click(cmd))(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")
    
    # Fix 기능들
    def network_fix(self):
        """네트워크 Fix - 수동으로 네트워크 복구 및 MES 통신 테스트"""
        logger.info("Network Fix initiated")
        self.close_menu()
        
        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❌ 네트워크 복구 실패")
                
                # MES 통신 테스트
                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):
        """브라우저 Fix - 크롬, 엣지 브라우저 캐시/쿠키 삭제"""
        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)
                
                # Chrome 캐시/쿠키 삭제
                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}")
                
                # Edge 캐시/쿠키 삭제
                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):
        """프린터 Fix - 레지스트리 수정 및 스풀러 재시작"""
        logger.info("Printer Fix initiated")
        self.close_menu()
        
        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)
                
                # 1. 레지스트리 수정
                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)
                    if result.returncode == 0:
                        log_progress("✅ 레지스트리 설정 수정 완료")
                    else:
                        log_progress(f"⚠️  레지스트리 수정 경고: {result.stderr.strip()}")
                except Exception as e:
                    log_progress(f"❌ 레지스트리 수정 실패: {e}")
                
                # 2. 스풀러 서비스 중지
                log_progress("\n🔄 Print Spooler 서비스 중지 중...")
                try:
                    result = subprocess.run(['net', 'stop', 'spooler'], 
                                          capture_output=True, text=True, timeout=30)
                    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)
                
                # 3. 스풀러 서비스 시작
                log_progress("\n🚀 Print Spooler 서비스 시작 중...")
                try:
                    result = subprocess.run(['net', 'start', 'spooler'], 
                                          capture_output=True, text=True, timeout=30)
                    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}")
                
                # 4. 추가 프린터 장애 해결 작업
                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):
        """MES Fix - pf_profile.ini 파일의 IP 설정 수정"""
        logger.info("MES Fix initiated")
        self.close_menu()
        
        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("500x350")
                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=18, 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)
                
                # 사용 환경 확인
                env_setting = load_data('environment', 'office')
                log_progress(f"현재 사용 환경: {env_setting}")
                
                # IP 설정 결정
                if env_setting == 'office':
                    target_ip = "192.168.0.250,14333"
                    env_name = "사무용"
                else:  # field
                    target_ip = "10.10.10.10,14333"
                    env_name = "현장용"
                
                log_progress(f"설정할 IP: {target_ip} ({env_name})")
                
                # pf_profile.ini 파일 경로
                profile_path = "C:\\geopop\\commlib\\pf_profile.ini"
                
                log_progress(f"\n📁 파일 경로: {profile_path}")
                
                # 파일 존재 확인
                if not os.path.exists(profile_path):
                    log_progress("❌ pf_profile.ini 파일을 찾을 수 없습니다!")
                    log_progress("   파일 경로를 확인해주세요.")
                    
                    # 장애 시 추천 사항
                    log_progress("\n📋 파일이 없을 때 해결방법:")
                    log_progress("1. MES 프로그램이 정상 설치되었는지 확인")
                    log_progress("2. C:\\geopop\\commlib\\ 폴더 존재 여부 확인")
                    log_progress("3. MES 프로그램 재설치 고려")
                    return
                
                log_progress("✅ pf_profile.ini 파일 발견")
                
                # 파일 백업
                backup_path = profile_path + ".backup"
                try:
                    import shutil
                    shutil.copy2(profile_path, backup_path)
                    log_progress(f"✅ 백업 파일 생성: {backup_path}")
                except Exception as e:
                    log_progress(f"⚠️  백업 생성 실패: {e}")
                
                # 파일 읽기 및 수정
                log_progress("\n🔧 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(profile_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
                    
                    # [SERVER] 섹션 찾기 및 IP 수정
                    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= 설정을 찾을 수 없습니다!")
                        log_progress("\n📋 IP 설정이 없을 때 해결방법:")
                        log_progress("1. [SERVER] 섹션 존재 여부 확인")
                        log_progress("2. 파일 형식이 올바른지 확인")
                        log_progress("3. MES 설정 파일 복원 고려")
                        return
                    
                    # 파일 저장 (원본 인코딩 유지)
                    with open(profile_path, 'w', encoding=used_encoding) as f:
                        f.writelines(lines)
                    
                    log_progress("✅ 파일 저장 완료")
                    
                except Exception as e:
                    log_progress(f"❌ 파일 수정 실패: {e}")
                    log_progress("\n📋 파일 수정 실패 시 해결방법:")
                    log_progress("1. 관리자 권한으로 프로그램 실행")
                    log_progress("2. 파일이 사용 중인지 확인 후 종료")
                    log_progress("3. 백업 파일에서 복원")
                    return
                
                log_progress("\n" + "=" * 50)
                log_progress("🎉 MES Fix 완료!")
                log_progress(f"ℹ️  MES 서버 IP가 {env_name} 환경에 맞게 설정되었습니다.")
                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:
            # 원형 UI 투명도 고정값 적용
            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):
        """MES 통신 테스트"""
        try:
            # 현재 환경 설정에 따른 MES 서버 정보
            env_setting = load_data('environment', 'office')
            
            if env_setting == 'office':
                mes_server = "192.168.0.250"
                mes_port = 14333
            else:  # field
                mes_server = "10.10.10.10"
                mes_port = 14333
            
            # 간단한 TCP 연결 테스트
            import socket
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(5)  # 5초 타임아웃
            
            try:
                result = sock.connect_ex((mes_server, mes_port))
                sock.close()
                return result == 0  # 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()