# Copyright (c) 2026 동일비전(Dongil Vision Korea). All Rights Reserved.
"""
╔══════════════════════════════════════════════════════════════════════════════╗
║  ALASKA v2.3 - SmKernelEvent                                                 ║
║  High-performance Windows Kernel Event (No CPU overhead wait)                 ║
╠══════════════════════════════════════════════════════════════════════════════╣
║  Version : 2.3.0                                                             ║
║  Date    : 2026-02-28                                                        ║
╠══════════════════════════════════════════════════════════════════════════════╣
║  Summary:                                                                    ║
║    Windows 커널 이벤트를 직접 호출하여 CPU 점유율 0%로 대기하면서도           ║
║    20~50μs의 정밀한 반응 속도를 제공하는 동기화 객체                         ║
╚══════════════════════════════════════════════════════════════════════════════╝
"""

import ctypes
from ctypes import wintypes
import os

# Windows Constants
INFINITE = 0xFFFFFFFF
WAIT_OBJECT_0 = 0x00000000
WAIT_TIMEOUT = 0x00000102
EVENT_ALL_ACCESS = 0x1F0003


class SmKernelEvent:
    """Windows 커널 이벤트 동기화 객체 (Shared Asset)

    SmLockFreeEvent(스핀락)보다 지연 시간은 미세하게 길지만(수십 μs),
    대기 중 CPU 점유율이 0%이므로 장기 대기나 일반적인 프로세스 동기화에 최적입니다.
    내부적으로 kernel32.dll의 CreateEventW/OpenEventW를 사용합니다.
    """

    def __init__(self, name: str, create: bool = False, initial: bool = False, manual_reset: bool = True):
        r"""SmKernelEvent 초기화.

        Args:
            name: 커널 이벤트 이름 (Global\ prefix 권장)
            create: True=생성, False=기존 이벤트 오픈
            initial: 초기 상태 (True=Set, False=Reset)
            manual_reset: True이면 수동으로 clear() 호출 전까지 Set 유지 (추천)
        """
        self._name = name
        self._is_owner = create
        self._k32 = ctypes.windll.kernel32
        
        # Windows 이름 규칙 (Global\ prefix 추가 시 시스템 전역 공유 가능)
        win_name = name if name.startswith("Global\\") else rf"Global\{name}"
        
        if create:
            # HANDLE CreateEventW(LPSECURITY_ATTRIBUTES, BOOL, BOOL, LPCWSTR)
            self._handle = self._k32.CreateEventW(None, manual_reset, initial, win_name)
            if not self._handle:
                raise OSError(f"Failed to create kernel event '{win_name}'. Error: {ctypes.get_last_error()}")
        else:
            # HANDLE OpenEventW(DWORD, BOOL, LPCWSTR)
            self._handle = self._k32.OpenEventW(EVENT_ALL_ACCESS, False, win_name)
            if not self._handle:
                raise OSError(f"Failed to open kernel event '{win_name}'. Error: {ctypes.get_last_error()}")

    def set(self):
        """이벤트를 '설정' 상태(Signaled)로 변경"""
        if not self._k32.SetEvent(self._handle):
            raise OSError(f"Failed to set event. Error: {ctypes.get_last_error()}")

    def clear(self):
        """이벤트를 '해제' 상태(Non-signaled)로 변경 (manual_reset=True일 때 필요)"""
        if not self._k32.ResetEvent(self._handle):
            raise OSError(f"Failed to reset event. Error: {ctypes.get_last_error()}")

    def wait(self, timeout: float = None) -> bool:
        """이벤트가 설정될 때까지 커널 대기 (CPU 점유율 0%)

        Args:
            timeout: 대기 시간 (초). None이면 무한 대기.

        Returns:
            bool: 이벤트가 설정되면 True, 타임아웃이면 False.
        """
        dw_millis = int(timeout * 1000) if timeout is not None else INFINITE
        
        # DWORD WaitForSingleObject(HANDLE, DWORD)
        res = self._k32.WaitForSingleObject(self._handle, dw_millis)
        
        if res == WAIT_OBJECT_0:
            return True
        elif res == WAIT_TIMEOUT:
            return False
        else:
            raise OSError(f"Wait error on event. Result: {res}, Error: {ctypes.get_last_error()}")

    def is_set(self) -> bool:
        """현재 이벤트가 설정되어 있는지 즉시 확인 (Wait 0ms)"""
        res = self._k32.WaitForSingleObject(self._handle, 0)
        return res == WAIT_OBJECT_0

    def close(self):
        """커널 핸들 해제"""
        if hasattr(self, '_handle') and self._handle:
            self._k32.CloseHandle(self._handle)
            self._handle = None

    def __del__(self):
        self.close()

    @property
    def name(self) -> str:
        return self._name

    def __repr__(self):
        status = "SET" if self.is_set() else "RESET"
        return f"SmKernelEvent({self._name}, status={status})"
