"""How long does an app take from the double-click to its window? (Windows only)

    python start_time.py dist/hello.exe hello
    python start_time.py dist/pond/pond.exe pond 5

It starts the program, waits until a window with that title is on the screen, and
prints the seconds. Then it closes the window. It does this 3 times, or as many
times as the last number says. The first start of a new .exe is often slower:
Windows Security reads the whole file once before it lets it run.

To time the same program with plain Python, give the script after "python":
    python start_time.py python ../hello.py hello
"""
import ctypes
import os
import subprocess
import sys
import time

WM_CLOSE = 0x0010                 # the message Windows sends when you click the X
LONGEST = 180                     # give up after this many seconds


def window_of(title):
    """The window with exactly this title, or 0 when there is none."""
    return ctypes.windll.user32.FindWindowW(None, title)


def one_start(command, title):
    started = time.perf_counter()
    program = subprocess.Popen(command)
    while not window_of(title):
        if program.poll() is not None:
            return None, "the program ended before it opened a window"
        if time.perf_counter() - started > LONGEST:
            program.kill()
            return None, "no window in " + str(LONGEST) + " seconds"
        time.sleep(0.01)
    seconds = time.perf_counter() - started
    time.sleep(1.0)               # let it draw, then close it the way a person does
    ctypes.windll.user32.PostMessageW(window_of(title), WM_CLOSE, 0, 0)
    try:
        code = program.wait(timeout=30)
    except subprocess.TimeoutExpired:
        program.kill()
        return seconds, "it did not close in 30 seconds, so it was stopped"
    return seconds, "closed with exit code " + str(code)


def main():
    words = sys.argv[1:]
    if len(words) < 2 or sys.platform != "win32":
        print(__doc__)
        return 2
    times = 3
    if words[-1].isdigit():
        times = int(words.pop())
    title = words.pop()
    command = words               # one word for an .exe, two for "python file.py"
    if os.path.exists(command[0]):
        command[0] = os.path.abspath(command[0])      # Windows wants an .exe's full path
    if window_of(title):
        print("A window named", repr(title), "is open already. Close it first.")
        return 1
    all_seconds = []
    for number in range(times):
        seconds, how = one_start(command, title)
        if seconds is None:
            print("start", number + 1, "FAILED:", how)
            return 1
        all_seconds.append(seconds)
        print(f"start {number + 1}: {seconds:5.2f} s to the window   ({how})")
        time.sleep(1.0)
    later = sorted(all_seconds[1:])
    if later:
        print(f"first start {all_seconds[0]:.2f} s, later starts {later[len(later) // 2]:.2f} s "
              f"(the middle of {len(later)})")
    return 0


if __name__ == "__main__":
    sys.exit(main())
