import json
import threading
from itertools import chain
from queue import Queue, Empty

try:
    import SharedData
except ImportError:
    from sys import path

    path.insert(1, "../..")
    path.insert(2, "..")
    import SharedData

# setup
config = SharedData.load_json_config()
c_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
TIMEOUT_FACTOR = config.SOCK_TIMEOUT
read_b, write_b = SharedData.rw_bytes(config.BYTE_SIZE, config.BYTE_ORDER,
                                      config.END_MARK, config.ENCODING)

# Wait for connection, or a proper IP:PORT input.
while True:
    host, port = input("Host [IP:Port] >> ").split(":")
    # host, port = ''.split(":")
    port = int(port)
    try:
        c_sock.connect((host, port))
    except TypeError:
        print(f"[C][Warn] Cannot connect to - {host}:{port}")
    else:
async def main():
    config = SharedData.load_json_config()

    work = generate_queue(config.PORT_MAX)
    delimiter = config.READ_UNTIL.encode(config.ENCODING)
    timeout = config.TIMEOUT

    started = asyncio.Event()
    fail_trigger = asyncio.Event()
    stop = asyncio.Event()

    send_q = asyncio.Queue()
    recv_q = asyncio.Queue()

    in_use = asyncio.Queue()
    failed = asyncio.Queue()

    # handler for primary connection.
    handle = handler_closure([send_q, recv_q], fail_trigger, started, stop,
                             delimiter, timeout)

    async def handler(recv, send):
        # send config to client.
        print("Sending config to client.")
        await tcp_send(SharedData.load_config_raw(), send)

        await handle(recv, send)
        send.close()

    # start server
    server_co, port_primary = await get_connection(handler)
    await server_co.start_serving()
    config.EXCLUDE.append(port_primary)

    # wait until connection is established, and handler started.
    await started.wait()

    # start workers
    await run_workers(config, work, send_q, recv_q, in_use, failed, stop)

    fail_trigger.set()
    await stop.wait()

    server_co.close()
    await server_co.wait_closed()

    last_port = config.PORT_MAX

    if not work.empty():
        last_port = await work.get()
        print(f"Task failed! Showing result before failed port {last_port}.")

    # overriding
    in_use = async_q_to_list(in_use)
    failed = async_q_to_list(failed)

    result = SharedData.get_port_result(in_use, failed, config.EXCLUDE,
                                        config.PORT_MAX)

    print("\n[Count Results]")
    print(f"Used Ports  : {result['Occupied']}")
    print(f"Closed Ports: {result['Unreachable']}")
    print(f"Excluded    : {result['Excluded']}")
    print(f"\nAll other ports from 1~{last_port} is open.")

    SharedData.dump_result(result, "tcp_scan_result")