#!/usr/bin/env python3

import unittest
import datetime
import sys
import os
import argparse
import shutil
from test_framework._test_params import skipped_tests


class TestRuntime(unittest.TestCase):
    from tests._test_00_01 import test_00_01_stop_runtime
    from tests._test_00_02 import test_00_02_camera_check
    from tests._test_01 import test_01_terminate
    from tests._test_02 import test_02_kill
    from tests._test_03 import test_03_controlPutGetLimitLogSize
    from tests._test_04 import test_04_InputOutput
    from tests._test_05_01 import test_05_01_accumulatorPlayFile
    from tests._test_05_02 import test_05_02_accumulatorLinear
    from tests._test_05_03 import test_05_03_accumulatorStep
    from tests._test_05_04 import test_05_04_accumulatorNone
    from tests._test_05_05 import test_05_05_accumulatorEventNumber
    from tests._test_06_01 import test_06_01_dvsnoisefilterPlayFile
    from tests._test_06_02 import test_06_02_dvsnoisefilterHotPixel
    from tests._test_07_01 import test_07_01_CropScaleEvents
    from tests._test_07_02 import test_07_02_CropScaleFrames
    from tests._test_08_01 import test_08_01_FlipRotateEventsHVD
    from tests._test_08_02 import test_08_02_FlipRotateFramesHVD
    from tests._test_09_01 import test_09_01_FrameContrastNormalisation
    from tests._test_09_02 import test_09_02_FrameContrastHistogram
    from tests._test_09_03 import test_09_03_FrameContrastClahe
    from tests._test_10 import test_10_VideoOutputFFV1
    from tests._test_11 import test_11_ExportCSV
    from tests._test_12_01 import test_12_01_UndistortEvents
    from tests._test_12_02 import test_12_02_UndistortFrames
    from tests._test_13 import test_13_decimation
    from tests._test_14_01 import test_14_01_converter_aedat2_0, \
        test_14_01_converter_aedat2_1, \
        test_14_01_converter_aedat2_3, \
        test_14_01_converter_aedat2_4, \
        test_14_01_converter_aedat2_5, \
        test_14_01_converter_aedat2_6, \
        test_14_01_converter_aedat2_7, \
        test_14_01_converter_aedat2_8, \
        test_14_01_converter_aedat2_9
    from tests._test_14_02 import test_14_02_converter_aedat3
    from tests._test_15_01 import test_15_01_perf_accumulator
    from tests._test_15_02 import test_15_02_perf_dvsnoisefilter
    from tests._test_15_03 import test_15_03_perf_crop_scale
    from tests._test_15_04 import test_15_04_perf_flip_rotate
    from tests._test_15_05 import test_15_05_perf_frame_contrast
    from tests._test_15_06 import test_15_06_perf_video_output
    from tests._test_15_07 import test_15_07_perf_csv_output
    from tests._test_15_08 import test_15_08_perf_undistort
    from tests._test_15_09 import test_15_09_perf_decimation
    from tests._test_15_10 import test_15_10_perf_knoise
    from tests._test_15_11 import test_15_11_perf_ynoise
    from tests._test_16 import test_16_controlAddMultipleModules


def parse_requested_tests(requested_tests, test_suite):
    all = [str(t) for t in test_suite._tests]
    minimal = []
    perf = []

    for t in all:
        if any(test in t for test in ["test_00", "test_01", "test_02", "test_03", "test_16"]):
            minimal.append(t)
        if "perf" in t:
            perf.append(t)

    # By default run all tests except performance tests
    if requested_tests is None or requested_tests == " " or len(requested_tests) == 0:
        requested_tests = "noperf"

    if isinstance(requested_tests, list) and len(requested_tests) == 1:
        requested_tests = requested_tests[0].split(",")

    if "minimal" in requested_tests:
        requested_tests = minimal
    elif "all" in requested_tests:
        requested_tests = all
    elif "noperf" in requested_tests:
        requested_tests = [t for t in all if t not in perf]
    elif "perf" in requested_tests:
        requested_tests = perf
    else:
        requested_tests_exact_names = []
        for t in all:
            if any(t.startswith("test_" + test) for test in requested_tests):
                requested_tests_exact_names.append(t)
        requested_tests = requested_tests_exact_names

    for available_test in all:
        test_requested = False
        for requested_test in requested_tests:
            if available_test == requested_test:
                test_requested = True
                break

        if not test_requested:
            skipped_tests.append(str(available_test).split(" ")[0])


def main():
    # Parse command line arguments
    parser = argparse.ArgumentParser()
    parser.add_argument('--env-vars')
    parser.add_argument('--runtime-exec')
    parser.add_argument('--modules-path')
    parser.add_argument('--control-exec')
    parser.add_argument(
        '--tests',
        help=
        "Test IDs to be run. Choices are 'minimal', 'all', 'perf', 'noperf', or numeric IDs, e.g. '--tests=04,06' or '--tests 04 06'. If nothing is provided, all will be executed.",
        type=str,
        nargs='+',
        required=False)
    parser.add_argument('--perf-data-append', help="Append to existing performance data file", type=str, required=False)
    args = parser.parse_args()

    # Make cli arguments into environment variables so they're available everywhere
    if (args.env_vars is not None) and args.env_vars:
        sep_idx = args.env_vars.index('=')
        var_name = args.env_vars[:sep_idx]
        var_content = args.env_vars[sep_idx + 1:]
        os.environ[var_name] = var_content

    if (args.runtime_exec is not None) and args.runtime_exec:
        os.environ['DV_RUNTIME_PATH'] = args.runtime_exec

    if (args.modules_path is not None) and args.modules_path:
        os.environ['DV_MODULES_PATH'] = args.modules_path

    if (args.control_exec is not None) and args.control_exec:
        os.environ['DV_CONTROL_PATH'] = args.control_exec

    if (args.perf_data_append is not None) and len(args.perf_data_append) > 0:
        os.environ['DV_PERF_DATA_APPEND'] = args.perf_data_append
    else:
        os.environ['DV_PERF_DATA_APPEND'] = "0"

    if os.environ.get("CI_JOB_NAME", None) is not None:
        os.environ['DV_RUNNER_NAME'] = os.environ["CI_JOB_NAME"]
    else:
        os.environ['DV_RUNNER_NAME'] = "test_runner"

    # Ready output dir for test-result files
    os.environ['DV_TEST_OUTPUT_DIR'] = 'test_out'
    shutil.rmtree(os.environ['DV_TEST_OUTPUT_DIR'], ignore_errors=True)
    os.makedirs(os.environ['DV_TEST_OUTPUT_DIR'])

    os.environ['DV_FAILED_TESTS_OUTPUT_DIR'] = 'failed_tests'
    shutil.rmtree(os.environ['DV_FAILED_TESTS_OUTPUT_DIR'], ignore_errors=True)
    os.makedirs(os.environ['DV_FAILED_TESTS_OUTPUT_DIR'])

    os.environ['DV_PERF_OUTPUT_DIR'] = 'perf_data'
    if not os.path.isdir(os.environ['DV_PERF_OUTPUT_DIR']):
        os.makedirs(os.environ['DV_PERF_OUTPUT_DIR'])

    # Load test suite
    suite = unittest.TestLoader().loadTestsFromTestCase(TestRuntime)

    # Disable tests that were not requested by the user
    parse_requested_tests(args.tests, suite)

    # Run test suite
    result = unittest.TextTestRunner(verbosity=2).run(suite)

    print("*********************************************************************************************************\n")
    print("COMPLETE: " + str(result.testsRun) + " tests executed on " + str(datetime.datetime.now()) + "\n")
    print("*********************************************************************************************************\n")

    if not result.errors:
        print("No errors found\n")

    for line in result.errors:
        print(line[1])
        print("-----------------------------------------------------------------------------------------------\n")

    print("*********************************************************************************************************\n")

    if not result.failures:
        print("No failures found\n")

    for line in result.failures:
        print(line[1])
        print("-----------------------------------------------------------------------------------------------\n")

    print("*********************************************************************************************************\n")

    if not result.skipped:
        print("No tests skipped\n")

    for line in result.skipped:
        print(line[1])
        print("-----------------------------------------------------------------------------------------------\n")

    print("*********************************************************************************************************\n")

    # Cleanup output directory before shutdown.
    if (not result.failures) and (not result.errors):
        shutil.rmtree(os.environ['DV_TEST_OUTPUT_DIR'], ignore_errors=True)

    if result.wasSuccessful():
        sys.exit(0)
    else:
        sys.exit(1)


if __name__ == "__main__":
    main()
