#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
RTD1861B UART0 检测脚本
用于自动识别当前连接的串口是否为 BootROM/UART0 升级接口
"""

import sys
import os
import glob
import time
import termios
import fcntl
import select


def list_serial_ports():
    """列出系统下可用的串口设备"""
    patterns = ['/dev/cu.*', '/dev/ttyUSB*', '/dev/ttyACM*']
    ports = []
    for p in patterns:
        ports.extend(glob.glob(p))
    # 过滤掉蓝牙等虚拟串口
    exclude = ['Bluetooth', 'Debug', 'MALS', 'SerialBluetooth']
    filtered = [p for p in ports if not any(e in p for e in exclude)]
    return sorted(filtered)


def select_port(ports):
    if not ports:
        print("[错误] 未找到任何串口设备，请检查 USB 转串口线是否插好。")
        sys.exit(1)

    print("\n发现以下串口设备：")
    for i, p in enumerate(ports, 1):
        print(f"  {i}. {p}")

    if len(ports) == 1:
        print(f"\n自动选择唯一串口: {ports[0]}")
        return ports[0]

    while True:
        choice = input(f"\n请选择串口编号 (1-{len(ports)}): ").strip()
        if choice.isdigit():
            idx = int(choice) - 1
            if 0 <= idx < len(ports):
                return ports[idx]
        print("无效输入，请重新选择。")


def open_serial(port, baudrate):
    """使用 termios 底层打开并配置串口"""
    try:
        fd = os.open(port, os.O_RDWR | os.O_NOCTTY)
    except PermissionError:
        print(f"[错误] 没有权限打开 {port}，请尝试: sudo python3 {sys.argv[0]}")
        sys.exit(1)
    except OSError as e:
        print(f"[错误] 无法打开串口 {port}: {e}")
        sys.exit(1)

    # 保存原配置以便退出时恢复
    old_attr = termios.tcgetattr(fd)

    # 新配置：原始模式，8N1
    new_attr = termios.tcgetattr(fd)
    # macOS termios 使用数字索引
    new_attr[0] = 0  # iflag
    new_attr[1] = 0  # oflag
    new_attr[2] = termios.CS8 | termios.CREAD | termios.CLOCAL  # cflag
    new_attr[3] = 0  # lflag
    new_attr[6][termios.VMIN] = 0
    new_attr[6][termios.VTIME] = 1  # 100ms read timeout

    # 波特率映射（动态构建，兼容不同系统）
    baud_map = {}
    for rate, attr_name in [
        (9600, 'B9600'),
        (19200, 'B19200'),
        (38400, 'B38400'),
        (57600, 'B57600'),
        (115200, 'B115200'),
        (230400, 'B230400'),
        (460800, 'B460800'),
        (921600, 'B921600'),
    ]:
        if hasattr(termios, attr_name):
            baud_map[rate] = getattr(termios, attr_name)

    if baudrate not in baud_map:
        supported = ', '.join(str(r) for r in sorted(baud_map.keys()))
        print(f"[错误] 不支持的波特率: {baudrate}")
        print(f"       本系统支持的波特率: {supported}")
        os.close(fd)
        sys.exit(1)

    new_attr[4] = baud_map[baudrate]  # ispeed
    new_attr[5] = baud_map[baudrate]  # ospeed

    termios.tcsetattr(fd, termios.TCSANOW, new_attr)

    # 清除非阻塞标志
    fl = fcntl.fcntl(fd, fcntl.F_GETFL)
    fcntl.fcntl(fd, fcntl.F_SETFL, fl & ~os.O_NONBLOCK)

    return fd, old_attr


def close_serial(fd, old_attr):
    termios.tcsetattr(fd, termios.TCSADRAIN, old_attr)
    os.close(fd)


def read_chunk(fd, timeout=0.3):
    """非阻塞读取串口数据"""
    data = b''
    start = time.time()
    while time.time() - start < timeout:
        ready, _, _ = select.select([fd], [], [], 0.05)
        if ready:
            try:
                chunk = os.read(fd, 4096)
                if chunk:
                    data += chunk
            except OSError:
                break
        elif data:
            break
    return data


def detect_uart0(port, baudrate):
    """核心检测逻辑：上电瞬间发送 Esc 并捕获 BootROM 输出"""
    print(f"\n[*] 正在打开串口: {port} @ {baudrate} bps")
    fd, old_attr = open_serial(port, baudrate)

    print("\n" + "=" * 62)
    print("【请按以下步骤操作】")
    print("1. 先将车机彻底断电（拔掉电源线或关闭电源开关）")
    print("2. 在本窗口按回车键")
    print("3. 按回车后，请立即给车机上电")
    print("=" * 62)

    input("\n确认车机已断电？按回车开始检测...")

    print("\n[*] 检测中，请在 3 秒内给车机上电...")
    print("-" * 62)

    # 清空残留数据
    read_chunk(fd, 0.2)

    # 特征定义
    uart0_keywords = [b'Realtek>', b'd/g/r>', b'Recovery', b'hwsetting', b'certificate']
    debug_keywords = [b'Linux version', b'Starting kernel', b'FreeRTOS', b'xiexw:', b'Project:', b'Build:']

    all_data = b''
    found_uart0 = False
    found_debug = False

    start_time = time.time()
    while time.time() - start_time < 12:
        # 持续发送 Esc 键，模拟按住不放
        os.write(fd, b'\x1b')

        chunk = read_chunk(fd, 0.25)
        if chunk:
            all_data += chunk
            # 实时打印输出（兼容中文和乱码）
            try:
                print(chunk.decode('utf-8', errors='replace'), end='', flush=True)
            except Exception:
                pass

            lower = all_data.lower()
            if any(k.lower() in lower for k in uart0_keywords):
                found_uart0 = True
                break
            if any(k.lower() in lower for k in debug_keywords):
                found_debug = True
                # 不立即 break，继续看有没有 uart0 特征出现

        time.sleep(0.15)

    print("\n" + "-" * 62)
    print("\n【检测结果】")

    if found_uart0:
        print("✅ 检测到 UART0（升级串口）特征！")
        print("   出现了 'Realtek>' 或 'd/g/r>' 等 BootROM 提示符。")
        print("   可以在此提示符下输入: boot ru")
        print("\n   注意：若已显示 Realtek>，请直接输入指令，")
        print("         不需要再按 Esc。")
    elif found_debug:
        print("⚠️  检测到调试串口特征（系统启动日志 / FreeRTOS / Shell）")
        print("   当前连接的不是 UART0，而是系统调试串口。")
        print("   按 Esc 不会进入升级模式，系统会直接启动 Android。")
        print("\n   【建议】")
        print("   请在主板上找到标有 'UART0'、'CON1' 或 'UPGRADE' 的接口，")
        print("   通常是 4pin 的 2.54mm 排针，重新接线后再试。")
    else:
        print("❓ 未检测到明确的串口类型特征。")
        print("\n   可能原因：")
        print("   - 上电太晚，错过了 BootROM 的检测窗口（只有 1~2 秒）")
        print("   - 波特率不匹配（可尝试重新运行脚本，选择 921600）")
        print("   - 当前连接的根本不是 SoC 串口")
        print("\n   【建议】重新断电，按回车后立即上电。")

    close_serial(fd, old_attr)
    print("\n" + "=" * 62)
    print("检测结束。")


def main():
    print("=" * 62)
    print("  RTD1861B UART0 升级串口自动检测工具")
    print("=" * 62)

    ports = list_serial_ports()
    port = select_port(ports)

    baud_input = input("\n请输入波特率（默认 115200，可尝试 921600）: ").strip()
    baudrate = int(baud_input) if baud_input.isdigit() else 115200

    try:
        detect_uart0(port, baudrate)
    except KeyboardInterrupt:
        print("\n\n[!] 用户取消检测")
        sys.exit(0)


if __name__ == '__main__':
    main()
