#!/usr/bin/env python3
#
# Copyright (C) 2023 Intel Corporation.  All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#

import argparse
import time
from pathlib import Path
import subprocess, shlex

SCRIPT_DIR = Path(__file__).parent.resolve()
REPO_ROOT_DIR = SCRIPT_DIR.parent
SAMPLE_CODE_FILE = REPO_ROOT_DIR / 'product-mini/app-samples/hello-world/main.c'
WASM_OUT_FILE = SCRIPT_DIR / 'out.wasm'

parser = argparse.ArgumentParser(
    description="Validate the customized lldb with sample code"
)
parser.add_argument(
    "-l", "--lldb", dest='lldb', default='lldb', help="path to lldb executable"
)
parser.add_argument(
    "-w", "--wamr", dest='wamr', default='iwasm', help="path to iwasm executable"
)
parser.add_argument(
    "-p", "--port", dest='port', default='1234', help="debug server listen port"
)
parser.add_argument(
    "-v", "--verbose", dest='verbose', action='store_true', default=False, help="display lldb stdout"
)

options = parser.parse_args()

lldb_command_epilogue = '-o q'

test_cases = {
    'run_to_exit': '-o c',
    'func_breakpoint': '-o "b main" -o c -o c',
    'line_breakpoint': '-o "b main.c:12" -o c -o c',
    'break_on_unknown_func': '-o "b not_a_func" -o c',
    'watch_point': '-o "b main" -o c -o "watchpoint set variable buf" -o c -o "fr v buf" -o c',
}

# Step1: Build wasm module with debug information
build_cmd = f'/opt/wasi-sdk/bin/clang -g -O0 -o {WASM_OUT_FILE} {SAMPLE_CODE_FILE}'
try:
    print(f'building wasm module ...', end='', flush=True)
    subprocess.check_call(shlex.split(build_cmd))
    print(f'\t OK')
except subprocess.CalledProcessError:
    print("Failed to build wasm module with debug information")
    exit(1)

def print_process_output(p):
    try:
        outs, errs = p.communicate(timeout=2)
        print("stdout:")
        print(outs)
        print("stderr:")
        print(errs)
    except subprocess.TimeoutExpired:
        print("Failed to get process output")

# Step2: Launch WAMR in debug mode and validate lldb commands

iteration = 0
for case, cmd in test_cases.items():
    lldb_command_prologue = f'{options.lldb} -o "process connect -p wasm connect://127.0.0.1:{int(options.port) + iteration}"'
    wamr_cmd = f'{options.wamr} -g=127.0.0.1:{int(options.port) + iteration} {WASM_OUT_FILE}'
    iteration += 1

    has_error = False
    print(f'validating case [{case}] ...', end='', flush=True)
    lldb_cmd = f'{lldb_command_prologue} {cmd} {lldb_command_epilogue}'

    wamr_process = subprocess.Popen(shlex.split(
        wamr_cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)

    time.sleep(0.1)
    if (wamr_process.poll() != None):
        print("\nWAMR doesn't wait for lldb connection")
        print_process_output(wamr_process)
        exit(1)

    lldb_process = subprocess.Popen(shlex.split(
        lldb_cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)

    if (options.verbose):
        while (lldb_process.poll() is None):
            print(lldb_process.stdout.read(), end='', flush=True)

    try:
        if (lldb_process.wait(5) != 0):
            print(f"\nFailed to validate case [{case}]")
            print_process_output(lldb_process)
            has_error = True

        if wamr_process.wait(2) != 0:
            print("\nWAMR process doesn't exit normally")
            print_process_output(wamr_process)
            has_error = True

    except subprocess.TimeoutExpired:
        print(f"\nFailed to validate case [{case}]")
        print("wamr output:")
        print_process_output(wamr_process)
        print("lldb output:")
        print_process_output(lldb_process)
        has_error = True
    finally:
        if (lldb_process.poll() == None):
            print(f'\nterminating lldb process [{lldb_process.pid}]')
            lldb_process.kill()
        if (wamr_process.poll() == None):
            print(f'terminating wamr process [{wamr_process.pid}]')
            wamr_process.kill()

        if (has_error):
            exit(1)

    print(f'\t OK')

    # wait 100ms to ensure the socket is closed
    time.sleep(0.1)

print('Validate lldb success')
exit(0)