#!/usr/bin/env python3
import argparse
import errno
import socket
import sys
import time

# await_port
#
# Functionality:
# 1. Wait for a process to be listening on a port - await_port will poll the address with a bind request and close it
#    until another process takes the address.
# 2. Wait for a port to be free - await_port will poll the address with a bind request and close it until the port is
#    free.

parser = argparse.ArgumentParser(description='Wait for a port either to be bound to or to be free.')
parser.add_argument('port', type=int, help='port to wait for')
parser.add_argument('--sleep-increment', type=int, default=2.0, help='sleep duration in between checks of the port')
parser.add_argument('--timeout', type=int, default=60.0, help='How long should we wait before timing out?')
parser.add_argument('--await-free', default=False, action='store_true',
                    help='Wait for the port to be active or free?')

args = parser.parse_args()

start = time.time()

port_in_use = None

desired_state = 'free' if args.await_free else 'in use'


def condition_reached(port_in_use):
    return not port_in_use if args.await_free else port_in_use


while True:
    # halt if we've reached the timeout
    elapsed = time.time() - start
    if elapsed > args.timeout:
        print(f"Timed out while waiting for port {args.port}")
        sys.exit(1)

    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        try:
            s.bind(('localhost', args.port))
            port_in_use = False
        except socket.error as e:
            if e.errno != errno.EADDRINUSE:
                raise e
            port_in_use = True

    if condition_reached(port_in_use):
        break

    print(f"Waiting for port {args.port} to be {desired_state}... sleeping for {args.sleep_increment} seconds.")
    time.sleep(args.sleep_increment)

print(f"Port {args.port} is {desired_state}!")
