# -*- coding: utf-8 -*-
"""
Esc 键中断监听器
任务执行期间后台监听 Esc 键，主线程在检查点轮询取消标志。
"""
import sys
import threading


class EscInterrupt:
    """后台监听 Esc 键的中断信号源。

    任务开始时调用 start() 启动监听线程；
    主线程在检查点调用 is_cancelled() 轮询；
    任务结束调用 stop() 停止监听并重置状态。
    """

    def __init__(self):
        self._cancel_event = threading.Event()
        self._thread = None
        self._running = False

    def start(self):
        """启动 Esc 监听线程（仅 Windows 有效）。"""
        if sys.platform != "win32":
            return
        self._cancel_event.clear()
        self._running = True
        self._thread = threading.Thread(target=self._listen, daemon=True)
        self._thread.start()

    def stop(self):
        """停止监听并重置取消标志。"""
        self._running = False
        self._cancel_event.clear()
        # 监听线程在 getwch() 阻塞，daemon=True 会在主线程退出时自动结束

    def is_cancelled(self) -> bool:
        """主线程检查是否收到中断信号。"""
        return self._cancel_event.is_set()

    def _listen(self):
        """后台线程：轮询 Esc 键（不消耗控制台输入事件）。

        重要：不能用 msvcrt.getwch() 阻塞读，因为 stop() 后线程仍阻塞在
        getwch() 上，会和 ansi_prompt 的 getwch() 争抢控制台输入，
        导致下一轮对话的首个字符被后台线程偷走吃掉。
        改用 GetAsyncKeyState 轮询，不触碰控制台输入缓冲区。
        """
        import ctypes
        import time

        VK_ESCAPE = 0x1B
        was_pressed = False

        while self._running:
            try:
                pressed = (ctypes.windll.user32.GetAsyncKeyState(VK_ESCAPE) & 0x8000) != 0
                if pressed and not was_pressed:
                    self._cancel_event.set()
                    self._running = False
                    return
                was_pressed = pressed
                time.sleep(0.05)
            except Exception:
                break
