# Copyright (c) 2026 동일비전(Dongil Vision Korea). All Rights Reserved.
"""
╔══════════════════════════════════════════════════════════════════════════════╗
║  ALASKA v2.3 - SmLockFreeEvent                                               ║
║  Ultra-low Latency Process Notification via Spin-lock Shared Memory Bit      ║
╠══════════════════════════════════════════════════════════════════════════════╣
║  Version : 2.3.0                                                             ║
║  Date    : 2026-02-28                                                        ║
╠══════════════════════════════════════════════════════════════════════════════╣
║  Summary:                                                                    ║
║    OS 커널 컨텍스트 스위칭 없이, 유저 레벨에서                               ║
║    1μs 이내로 다른 프로세스에 신호를 전달하는 고속 동기화 이벤트             ║
╚══════════════════════════════════════════════════════════════════════════════╝
"""

import time
from .sm_value import SmValue


class SmLockFreeEvent:
    """공유 메모리 기반 초저지연 동기화 이벤트

    multiprocessing.Event는 내부적으로 OS 커널 락을 사용하여 수 ms의 지연이 발생할 수 있습니다.
    SmLockFreeEvent는 공유 메모리의 특정 바이트를 스핀락(Spin-lock) 방식으로 감시하여
    1μs 이내의 통지 속도를 보장합니다.

    주의 (RC-09 시험 결과):
        Producer가 set() 후 즉시 clear() 하면 waiter가 91.9% 놓칩니다.
        바이너리 플래그 특성상 set→clear 사이에 waiter가 깨어나지 못하면 손실됩니다.

        권장 패턴:
            - Producer는 set()만 호출
            - Consumer가 wait() 후 clear() (drain 패턴)
            - 또는 wait_drain() 편의 메서드 사용

        실운영 참고:
            ALASKA 시그널 시스템은 SmKernelEvent + Double-Check 패턴을 사용합니다.
            SmLockFreeEvent는 극저지연(μs) 필요 시에만 사용하세요.
    """

    def __init__(self, name: str, create: bool = False, initial: bool = False):
        """SmLockFreeEvent 초기화.
        
        내부적으로 SmValue('B') (1바이트 unsigned char)를 사용합니다.
        """
        self._name = name
        self._val = SmValue(name=name + "_evt", typecode='B', create=create, 
                            initial=1 if initial else 0)

    def set(self):
        """이벤트를 '설정' 상태(1)로 변경"""
        self._val.value = 1

    def clear(self):
        """이벤트를 '해제' 상태(0)로 변경"""
        self._val.value = 0

    def is_set(self) -> bool:
        """현재 이벤트가 설정되어 있는지 확인 (Lock-free)"""
        return self._val.value == 1

    def wait(self, timeout: float = None, spin: int = 1000) -> bool:
        """이벤트가 설정될 때까지 대기.
        
        Args:
            timeout: 최대 대기 시간 (초). None이면 무한 대기.
            spin: OS sleep을 수행하기 전 스핀락(Busy-wait) 횟수.
                  기본 1000회 (약 수십 μs).
                  -1이면 무한 스핀 (CPU 사용율 100%, 지연 시간 최소화).
        
        Returns:
            bool: 이벤트가 설정되면 True, 타임아웃이면 False.
        """
        start = time.perf_counter()
        count = 0
        
        while self._val.value == 0:
            # 1. 스핀락 시도
            if spin == -1:
                pass # 무한 스핀
            elif count < spin:
                count += 1
                continue
            else:
                # 2. 스핀 완료 후 가벼운 양보 (Yield)
                time.sleep(0) # 0초 수면으로 OS 스케줄러에게 양보
            
            # 3. 타임아웃 체크
            if timeout is not None:
                if (time.perf_counter() - start) > timeout:
                    return False
        
        return True

    def wait_drain(self, timeout: float = None, spin: int = 1000) -> bool:
        """이벤트 대기 후 자동 clear (drain 패턴).

        Producer는 set()만 호출하고, Consumer는 이 메서드로 wait+clear를
        원자적으로 수행합니다. set→clear 사이 91% 손실(RC-09) 방지.

        Args:
            timeout: 최대 대기 시간 (초). None이면 무한 대기.
            spin: OS sleep 전 스핀 횟수.

        Returns:
            bool: 이벤트가 설정되면 True, 타임아웃이면 False.
        """
        result = self.wait(timeout=timeout, spin=spin)
        if result:
            self.clear()
        return result

    def poll(self) -> bool:
        """현재 값을 즉시 읽어 반환 (Wait 없이 체크)"""
        return self.is_set()

    def close(self):
        """공유 메모리 리소스 해제"""
        self._val.close()

    def __repr__(self):
        return f"SmLockFreeEvent({self._name}, set={self.is_set()})"
