#!/usr/bin/env python3
"""
MT5 Wake - 解决 Wine 后台运行时 OnTimer 不触发的问题
原理：每隔 N 秒向 MT5 发送 ChartNavigate 键盘快捷键，激活消息循环
同时写入 mt5_cmd.json 供 EA 读取处理

用法：
  python3 mt5_wake.py --interval 5 &
  # 持续运行，保持 MT5 的 OnTimer 激活
"""

import argparse
import json
import subprocess
import sys
import time
from pathlib import Path

try:
    import Quartz as q
    HAS_QUARTZ = True
except ImportError:
    HAS_QUARTZ = False

import os
if sys.platform == "win32":
    MT5_FILES = Path(os.environ["APPDATA"]) / "MetaQuotes" / "Terminal" / "Common" / "Files"
else:
    MT5_FILES = Path.home() / "Library/Application Support/net.metaquotes.wine.metatrader5/drive_c/Program Files/MetaTrader 5/MQL5/Files"
CMD_FILE = MT5_FILES / "mt5_cmd.json"
RSP_FILE = MT5_FILES / "mt5_rsp.json"


def get_mt5_pid() -> int:
    try:
        return int(subprocess.check_output(
            ["osascript", "-e", 'tell application "System Events" to unix id of process "MetaTrader 5"'],
            text=True,
            timeout=5,
        ).strip())
    except Exception:
        return 0


def send_arrow_keys(pid: int, keycode: int = 123, times: int = 1) -> None:
    """Send left/right arrow key to MT5 process via CGEvent."""
    if not HAS_QUARTZ:
        return
    try:
        for _ in range(times):
            kd = q.CGEventCreateKeyboardEvent(None, keycode, True)
            ku = q.CGEventCreateKeyboardEvent(None, keycode, False)
            if kd:
                q.CGEventSetIntegerValueField(kd, q.kCGEventTargetUnixProcessID, pid)
                q.CGEventSetIntegerValueField(ku, q.kCGEventTargetUnixProcessID, pid)
                q.CGEventPost(1, kd)
                time.sleep(0.05)
                q.CGEventPost(1, ku)
                time.sleep(0.1)
    except Exception as e:
        print(f"CGEvent error: {e}")


def keep_mt5_alive(pid: int, interval: float) -> None:
    """
    持续向 MT5 发送图表导航信号，保持消息循环活跃。
    这是让 Wine 环境下 OnTimer 持续触发的关键。
    """
    print(f"[MT5Wake] Activating MT5 (PID {pid}) every {interval}s")
    print(f"[MT5Wake] MT5 Files: {MT5_FILES}")
    print(f"[MT5Wake] Watch for: {CMD_FILE}")

    # 先激活 MT5 窗口
    try:
        subprocess.run(["osascript", "-e", 'tell application "MetaTrader 5" to activate'],
                      capture_output=True, timeout=5)
        time.sleep(1)
    except Exception:
        pass

    count = 0
    while True:
        count += 1
        try:
            # 激活窗口（bring to front）
            subprocess.run(
                ["osascript", "-e", 'tell application "System Events" to keystroke ""'],
                capture_output=True, timeout=3
            )

            # 发送右箭头键（ChartNavigate Right = 1 pixel, 不影响图表显示）
            # Right arrow = keycode 124
            send_arrow_keys(pid, keycode=124, times=1)

            if count % 10 == 0:
                print(f"[MT5Wake] Heartbeat #{count} at {time.strftime('%H:%M:%S')}")

        except Exception as e:
            print(f"[MT5Wake] Error: {e}")

        time.sleep(interval)


def main() -> int:
    parser = argparse.ArgumentParser(description="MT5 Wake - Keep OnTimer alive in Wine")
    parser.add_argument("--interval", type=float, default=5.0,
                        help="Seconds between wake signals (default 5s, use 1-10)")
    parser.add_argument("--once", action="store_true", help="Send one wake signal and exit")
    args = parser.parse_args()

    if not HAS_QUARTZ:
        print("[FAIL] Quartz not available. Run: pip3 install pyobjc")
        return 1

    pid = get_mt5_pid()
    if pid == 0:
        print("[FAIL] MT5 not running. Start MT5 first.")
        return 1

    print(f"[OK] MT5 found (PID {pid})")

    if args.once:
        send_arrow_keys(pid, 124, 2)
        print("[OK] Wake signal sent")
        return 0

    print("Starting MT5 Wake daemon (Ctrl+C to stop)...")
    try:
        keep_mt5_alive(pid, args.interval)
    except KeyboardInterrupt:
        print("\n[MT5Wake] Stopped.")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
