"""
Contains helpful utilities relating to WHSPAH.
"""

from threading import Thread
import select
import time
import os
import io

import serial.tools.list_ports
import serial

# The hardware magic string.
# I couldn't really think of a better place to put this,
# so I figured utils is the closest we have to a globals.py file.
# I don't want to create a globals.py file for this one variable.
HARDWARE_MAGIC = b'/WHSPAH\x00'

def _testPortForWHSPAH(resultList: list, port: os.PathLike, baud: int, timeout: float) -> None:
    """
    Gets called as a thread by _sanForValidPorts().
    Attempts to connect to the port specified, then waits for magic string.
    If a successful port was found, it will be closed, then appended to
    resultList.
    """

    ser = None

    try:
        # Start a timer
        st = time.perf_counter()
        # Connect to the serial port
        ser = serial.Serial(port, baud, timeout=timeout)
        ser.flushInput()
        # Select it to enforce timeouts
        select.select([ser], [], [], timeout)
        # Attempt to read magic string from the port
        while (ser.read_until(b'\x00') != HARDWARE_MAGIC) and time.perf_counter() - st < timeout:
            time.sleep(0.1)

        # If all of the above completed before the timeout, add the port to the result list
        if time.perf_counter() - st < timeout:
            resultList.append(port)
    except (serial.serialutil.SerialException, io.UnsupportedOperation):
        pass
    finally:
        if ser is not None:
            ser.close()

def scanForValidPorts(timeout: float=2.5, *, baudrate: int=9600) -> list[os.PathLike]:
    """
    Scans the system for serial devices calling out "WHSAPH".

    Args:
        timeout (float): How long (in seconds) the scan will wait for the "WHSPAH" callout.
        baudrate (int): What baudrate to use when connecting to the hardware.
                        This is only needed if you're messing with custom firmware, in all other
                        situations, this should be left as default.
    """

    res = []
    threads = []

    for port in serial.tools.list_ports.comports():
        thread = Thread(target=_testPortForWHSPAH, args=(res, port.device, baudrate, timeout))
        threads.append(thread)
        thread.start()

    for thread in threads:
        thread.join()

    return res

__all__ = ['scanForValidPorts']
