import os
import subprocess
import time
import argparse

parser = argparse.ArgumentParser(description="Run OPC UA examples with or without valgrind")
parser.add_argument("--no-valgrind", action="store_true",
                    help="Run examples without valgrind (default: on)")
args_cli = parser.parse_args()

example_args = {
        "client_encryption":"opc.tcp://localhost:4840 client_cert.der client_key.der server_cert.der",
        "access_control_client_encrypt":"opc.tcp://localhost:4840 client_cert.der client_key.der server_cert.der",
        "client_event_filter":"opc.tcp://localhost:4840",
        "client_connect":"-username user1 -password password -cert client_cert.der -key client_key.der opc.tcp://localhost:4840",
        "pubsub_nodeset_rt_publisher":"-i lo",
        "pubsub_nodeset_rt_subscriber":"-i lo",
        "pubsub_TSN_loopback":"-i lo",
        "pubsub_TSN_loopback_single_thread":"-i lo",
        "pubsub_TSN_publisher":"-i lo",
        "pubsub_TSN_publisher_multiple_thread":"-i lo",
        "server_encryption":"server_cert.der server_key.der client_cert.der",
        "server_encryption_filestore":"server_cert.der server_key.der",
        "server_loglevel":"--loglevel=1",
        "ci_server":"4840 server_cert.der server_key.der client_cert.der",
        "server_json_config":"server_json_config.json5"
        }

server_needed_examples = {
        "access_control_client":"ci_server 4840 server_cert.der server_key.der client_cert.der",
        "access_control_client_encrypt":"ci_server 4840 server_cert.der server_key.der client_cert.der",
        "client":"ci_server 4840 server_cert.der server_key.der client_cert.der",
        "client_async":"ci_server 4840 server_cert.der server_key.der client_cert.der",
        "client_connect":"ci_server 4840 server_cert.der server_key.der client_cert.der",
        "client_connect_loop":"ci_server 4840 server_cert.der server_key.der client_cert.der",
        "client_encryption":"server_encryption server_cert.der server_key.der client_cert.der",
        "client_event_filter":"ci_server 4840 server_cert.der server_key.der client_cert.der",
        "client_historical":"tutorial_server_historicaldata",
        "client_method_async":"ci_server 4840 server_cert.der server_key.der client_cert.der",
        "client_subscription_loop":"ci_server 4840 server_cert.der server_key.der client_cert.der",
        "custom_datatype_client":"custom_datatype_server",
        "discovery_client_find_servers":"discovery_server_lds",
        "discovery_server_register":"discovery_server_lds",
        "discovery_server_multicast":"discovery_server_multicast",
        "pubsub_publish_encrypted":"pubsub_subscribe_encrypted",
        "pubsub_publish_encrypted_sks":"server_pubsub_central_sks server_cert.der server_key.der --enableUnencrypted --enableAnonymous",
        "pubsub_subscribe_encrypted":"pubsub_publish_encrypted",
        "pubsub_subscribe_encrypted_sks":"server_pubsub_central_sks server_cert.der server_key.der --enableUnencrypted --enableAnonymous",
        "pubsub_subscribe_standalone_dataset":"tutorial_pubsub_publish",
        "server_pubsub_subscribe_custom_monitoring":"server_pubsub_publish_on_demand",
        "server_pubsub_publish_rt_offsets":"server_pubsub_publish_rt_offsets",
        "server_pubsub_publish_rt_state_machine":"server_pubsub_publish_rt_state_machine",
        "server_pubsub_subscribe_rt_offsets":"server_pubsub_subscribe_rt_offsets",
        "server_pubsub_subscribe_rt_state_machine":"server_pubsub_subscribe_rt_state_machine",
        "tutorial_client_events":"tutorial_server_events",
        "tutorial_client_firststeps":"tutorial_server_firststeps",
        "tutorial_pubsub_connection":"tutorial_pubsub_subscribe",
        "tutorial_pubsub_publish":"tutorial_pubsub_subscribe",
        "tutorial_pubsub_subscribe":"tutorial_pubsub_publish",
        "tutorial_server_reverseconnect":"ci_server 4841 client_cert.der client_key.der server_cert.der"
}

client_needed_examples = {
        "ci_server":"client",
        "server_ctt":"client",
        "server_inheritance":"client",
        "server_instantiation":"client",
        "server_loglevel":"client",
        "server_mainloop":"client",
        "server_nodeset":"client",
        "server_nodeset_loader":"client",
        "server_nodeset_plcopen":"client",
        "server_nodeset_powerlink":"client",
        "server_settimestamp":"client",
        "server_testnodeset":"client",
        "tutorial_server_monitoreditems":"client",
        "tutorial_server_variabletype":"client",
        "tutorial_server_object":"client",
        "tutorial_server_variable":"client",
        "access_control_server":"access_control_client",
        "custom_datatype_server":"custom_datatype_client",
        "discovery_server_lds":"discovery_client_find_servers",
        "server_encryption":"client_encryption opc.tcp://localhost:4840 client_cert.der client_key.der server_cert.der",
        "server_events_random":"tutorial_client_events opc.tcp://localhost:4840",
        "server_pubsub_central_sks":"pubsub_publish_encrypted_sks",
        "tutorial_server_alarms_conditions":"tutorial_client_events opc.tcp://localhost:4840",
        "tutorial_server_datasource":"tutorial_client_events opc.tcp://localhost:4840",
        "tutorial_server_firststeps":"tutorial_client_firststeps",
        "tutorial_server_historicaldata":"client_historical",
        "tutorial_server_historicaldata_circular":"client_historical",
        "tutorial_server_method":"client_method_async",
        "tutorial_server_method_async":"client_method_async"
}

blacklist = {
        "pubsub_TSN_loopback":1,
        "pubsub_TSN_loopback_single_thread":1,
        "pubsub_TSN_publisher":1,
        "pubsub_TSN_publisher_multiple_thread":1,
        "pubsub_nodeset_rt_publisher":1,
        "pubsub_nodeset_rt_subscriber":1,
        "access_control_client_encrypt":1,
        "client_encryption":1,
        "client_historical":1,
        "client_subscription_loop":1,
        "client_method_async":1,
        "pubsub_subscribe_encrypted":1,
        "tutorial_pubsub_mqtt_subscribe":1,
        "tutorial_pubsub_mqtt_publish":1
}

# Run each example with valgrind.
# Wait 10 seconds and send the SIGINT signal.
# Wait for the process to terminate and collect the exit status.
# Abort when the exit status is non-null.

os.chdir("../build")
current_dir = os.getcwd()
print(f"Current directory: {current_dir}")
example_dir = os.path.join(current_dir, "bin", "examples")
examples = os.listdir(example_dir)

cur_dir = os.path.dirname(os.path.realpath(__file__)) # path of current file
tests_path = os.path.join(cur_dir, os.pardir, os.pardir, os.pardir, "tests")

# skipping examples that are in the blacklist
for example in examples:
    if example in blacklist:
        print(f"Skipping {example}")
        continue

    # get the arguments for the example
    args = example_args.get(example)

    if args_cli.no_valgrind:
        cmd = ["./bin/examples/" + example]
    else:
        cmd = [
            "valgrind",
            "--errors-for-leak-kinds=all",
            "--leak-check=full",
            "--error-exitcode=1337",
            f"--suppressions={tests_path}/valgrind_suppressions.supp",
            "./bin/examples/" + example
        ]

    if args:
        args_list = args.split()
        cmd += args_list
    print(f"Args for {example}: {cmd} ")

    # check if the example needs a server or client to be running
    server_needed = server_needed_examples.get(example)
    client_needed = client_needed_examples.get(example)
    
    #start the server if needed
    server_process = None
    if server_needed:
        server_cmd = server_needed.split()
        server_cmd[0] = "./bin/examples/" + server_cmd[0]
        print(f"Starting server {server_cmd} from {os.getcwd()}")
        server_process = subprocess.Popen(server_cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        time.sleep(10)
    
    # start the example
    process = subprocess.Popen(cmd)
    time.sleep(10)
    
    # start the client if needed
    client_process = None
    if client_needed:
        client_cmd = client_needed.split()
        client_cmd[0] = "./bin/examples/" + client_cmd[0]
        print(f"Starting client {client_cmd}")
        client_process = subprocess.Popen(client_cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    
    # wait 20 seconds and send the SIGINT signal
    time.sleep(20)
    print(f"Sending SIGINT to {process.pid}")
    process.send_signal(subprocess.signal.SIGINT)
    
    tries = 0
    while process.poll() is None:
        time.sleep(20)
        process.send_signal(subprocess.signal.SIGINT)
        if tries > 5:
            print(f"Process {process.pid} is still running after 5 tries. Terminating process.")
            process.kill()
        tries += 1

    # save the exit code
    exit_code = process.wait()

    # Check exit codes depending on whether valgrind is enabled
    if not args_cli.no_valgrind and exit_code == 1337:
        print(f"Processing {example} failed with valgrind issues")
        exit(exit_code)
    if not args_cli.no_valgrind and exit_code != 0:
        print(f"The application {example} returned exit code {exit_code} but valgrind has no issue")

    if args_cli.no_valgrind and exit_code != 0:
        print(f"The application {example} returned exit code {exit_code}")
        exit(exit_code)

    # terminate the server and client
    if server_process:
        print(f"Sending SIGKILL to server {server_process.pid} for application {example}")
        server_process.kill()
        server_process.wait()
    
    if client_process:
        print(f"Sending SIGKILL to client {client_process.pid} for application {example}")
        client_process.kill()
        client_process.wait()
