from __future__ import annotations

import time

import serial
from serial import SerialException, SerialTimeoutException


PORT = "COM5"
BAUD_RATE = 115200

# Arduinoの100 msパルスとUSB通信時間を考慮した応答待ち時間
RESPONSE_TIMEOUT_SECONDS = 2.0


def send_trigger(ser: serial.Serial) -> None:
    """
    ArduinoへTRIGコマンドを送り、ACKとDONEを待つ。

    Raises:
        RuntimeError:
            ArduinoがBUSYまたはエラーを返した場合、
            あるいは応答がタイムアウトした場合。
    """
    # 前回の不要な受信データを破棄
    ser.reset_input_buffer()

    # Arduinoへ改行付きコマンドを送信
    ser.write(b"TRIG\n")
    ser.flush()

    deadline = time.monotonic() + RESPONSE_TIMEOUT_SECONDS
    ack_received = False

    while time.monotonic() < deadline:
        raw_line = ser.readline()

        # timeout時間内に1行受信できなかった場合
        if not raw_line:
            continue

        message = raw_line.decode("ascii", errors="replace").strip()

        if not message:
            continue

        print(f"Arduino: {message}")

        if message == "ACK":
            ack_received = True
            continue

        if message == "DONE":
            if not ack_received:
                raise RuntimeError("DONEを受信しましたが、ACKを受信していません。")

            return

        if message == "BUSY":
            raise RuntimeError("Arduinoは現在トリガー出力中です。")

        if message.startswith("ERR"):
            raise RuntimeError(f"Arduinoエラー: {message}")

    raise RuntimeError("Arduinoからの応答がタイムアウトしました。")


def main() -> None:
    try:
        with serial.Serial(
            port=PORT,
            baudrate=BAUD_RATE,
            bytesize=serial.EIGHTBITS,
            parity=serial.PARITY_NONE,
            stopbits=serial.STOPBITS_ONE,
            timeout=0.1,
            write_timeout=1.0,
            xonxoff=False,
            rtscts=False,
            dsrdtr=False,
        ) as ser:
            print(f"{ser.port}を開きました。")

            # 接続直後の安定待ち
            time.sleep(0.2)

            send_trigger(ser)

            print("100 msトリガーが完了しました。")

    except SerialTimeoutException as exc:
        print(f"シリアル送信タイムアウト: {exc}")

    except SerialException as exc:
        print(f"シリアルポートエラー: {exc}")

    except RuntimeError as exc:
        print(f"通信エラー: {exc}")


if __name__ == "__main__":
    main()
