# 91cd85d1c6c7aad6b978e565111dc5977b19987ea75b4e7e7b2b8b0b26a73139  davis.aedat4
# daf5fba83978a9702efc8b3ae13bcd799d90d7e1e84b5038fb93dc915f8c1b00  dvxplorer.aedat4
# ac17c496518c6c55f8bb2624710f9411e3d7dae5184e5cd214fc769bfdc843c8  plane.aedat4
# 9358014b2adc1c556e35cc6d6f311268c9f690996332ddb6b2d62f1cdef3c687  plane_noise.aedat4
import platform
import unittest
import os
import shutil

NUM_RUNS_FOR_PERFORMANCE_TESTS = 10
NUM_PRIOR_RESULTS_FOR_PERFORMANCE_TESTS = 25
NUM_REQUIRED_PRIOR_RESULTS_FOR_STATS_TEST = 0.75 * NUM_PRIOR_RESULTS_FOR_PERFORMANCE_TESTS
STATS_TOLERANCE_STD_DEV = 1

test_data_root = {
    'Linux': '/test_data/',
    'Darwin': '/Users/inivation/docker-files/test_data/',
    'Windows': 'C:\\msys64\\home\\inivation\\docker-files\\test_data\\'
}

converter_dir = {'Linux': 'converter/', 'Darwin': 'converter/', 'Windows': 'converter\\'}

dvxplorer_file = {
    "name": "dvxplorer.aedat4",
    "hash": "daf5fba83978a9702efc8b3ae13bcd799d90d7e1e84b5038fb93dc915f8c1b00",
    "size": 676593462  # uncompressed
}
dvxplorer_performance_file = {
    "name": "dvxplorer_performance.aedat4",
    "hash": "56f95fa317a0b90f9e4afbd38631981c43e89b15140397b2f918895b5a181dbb",
    "size": 2024770214  # uncompressed
}
davis_file = {
    "name": "davis.aedat4",
    "hash": "91cd85d1c6c7aad6b978e565111dc5977b19987ea75b4e7e7b2b8b0b26a73139",
    "size": 621249606  # uncompressed
}
aedat2_0_file = {
    "name": converter_dir[platform.system()] + "aedat2-0.aedat",
    "hash": "e24d29a367b3a7dde8cbaf77ac67fb25679461460becb9eae04d800d7ba894c6",
    "size": 3982769
}
aedat2_1_file = {
    "name": converter_dir[platform.system()] + "aedat2-1.aedat",
    "hash": "c5cbebe89dd93f410df46422c8d2b68296e67cde9351fd166296b5f2a54898ff",
    "size": 86571040
}
aedat2_2_file = {
    "name": converter_dir[platform.system()] + "aedat2-2.aedat",
    "hash": "9830df29a5d19f38454058767c088dcf70ab5db7a09c45c211a38ed96c464d6c",
    "size": 21673001
}
aedat2_3_file = {
    "name": converter_dir[platform.system()] + "aedat2-3.aedat",
    "hash": "e1f5c83efa98e83daa08f9608d2683c2750ad0356c3ed9f53edbd714727e8051",
    "size": 627826
}
aedat2_4_file = {
    "name": converter_dir[platform.system()] + "aedat2-4.aedat",
    "hash": "86fec69d10c671d1fb50b63d5c416380bfe154c521ddb404d3932f4cd0a548af",
    "size": 1800666
}
aedat2_5_file = {
    "name": converter_dir[platform.system()] + "aedat2-5.aedat",
    "hash": "9db0a000152fba1ba35d2b085c7e7e3224583222c80023631f3df461b20d77c8",
    "size": 30670848
}
aedat2_6_file = {
    "name": converter_dir[platform.system()] + "aedat2-6.aedat",
    "hash": "2aeb88db73f1a1b63461d64d39bb9f6e17dc99f3070aa5aac16ef4c063a29951",
    "size": 42560589
}
aedat2_7_file = {
    "name": converter_dir[platform.system()] + "aedat2-7.aedat",
    "hash": "0abbfd39d40d016a78836d097a072f776d72a522afd9d4aa5d893ee33a092a0d",
    "size": 10886628
}
aedat2_8_file = {
    "name": converter_dir[platform.system()] + "aedat2-8.aedat",
    "hash": "f50e82f5c8e5e243fde7ea567f9d1c68d2a4bbd88106771ed9c990f71456e8a5",
    "size": 1421690
}
aedat2_9_file = {
    "name": converter_dir[platform.system()] + "aedat2-9.aedat",
    "hash": "7c056aeb11e0dea474c060f2a15e6fd49f0afb31023c670d7a56b6ca32ba40ad",
    "size": 360185
}
aedat3_0_file = {
    "name": converter_dir[platform.system()] + "aedat3-0.aedat",
    "hash": "3b332a3c90c6a5ffa5f5c065cf742c3d58f4a3f2a4388c7e0f0bf000cfd219b9",
    "size": 247283692
}
calibration_file = {"name": "calibration.xml"}

test_params = {}

skipped_tests = []


def davis_input(func):
    return input_data(davis_file)(func)


def dvxplorer_input(func):
    return input_data(dvxplorer_file)(func)


def dvxplorer_performance_input(func):
    return input_data(dvxplorer_performance_file)(func)


def aedat2_0_input(func):
    return input_data(aedat2_0_file)(func)


def aedat2_1_input(func):
    return input_data(aedat2_1_file)(func)


def aedat2_2_input(func):
    return input_data(aedat2_2_file)(func)


def aedat2_3_input(func):
    return input_data(aedat2_3_file)(func)


def aedat2_4_input(func):
    return input_data(aedat2_4_file)(func)


def aedat2_5_input(func):
    return input_data(aedat2_5_file)(func)


def aedat2_6_input(func):
    return input_data(aedat2_6_file)(func)


def aedat2_7_input(func):
    return input_data(aedat2_7_file)(func)


def aedat2_8_input(func):
    return input_data(aedat2_8_file)(func)


def aedat2_9_input(func):
    return input_data(aedat2_9_file)(func)


def aedat3_0_input(func):
    return input_data(aedat3_0_file)(func)


def output_hash(hash):

    def output_hash_wrapper(func):
        return output_hashes([["output0_hash", hash]])(func)

    return output_hash_wrapper


def output_hashes(hashes):

    def output_hashes_wrapper(func):
        return named_parameter_list(hashes)(func)

    return output_hashes_wrapper


def output_file_size(size):

    def output_file_size_wrapper(func):
        return output_file_sizes([["output0_file_size", size]])(func)

    return output_file_size_wrapper


def output_file_sizes(sizes):

    def output_file_sizes_wrapper(func):
        return named_parameter_list(sizes)(func)

    return output_file_sizes_wrapper


def output_written_data_size(size):

    def output_written_data_wrapper(func):
        return output_file_sizes([["output0_written_data_size", size]])(func)

    return output_written_data_wrapper


def output_written_data_sizes(sizes):

    def output_written_data_sizes_wrapper(func):
        return named_parameter_list(sizes)(func)

    return output_written_data_sizes_wrapper


def init_and_deinit(name):

    def init_and_deinit_wrapper(func):

        def wrapper(*args, **kwargs):
            set_test_name(name)
            try:
                ret = func(*args, **kwargs)
                test_name = test_params.get("name", None)
                test_params.clear()
                remove_output_files(test_name)
                return ret
            except:
                test_name = test_params.get("name", None)
                test_params.clear()
                move_output_files_to_failed_tests(test_name)
                raise

        return wrapper

    return init_and_deinit_wrapper


def test_performance(func):

    @init_and_deinit(func.__name__)
    def wrapper(*args, **kwargs):
        test_params["test_performance"] = True
        test_params["num_runs"] = NUM_RUNS_FOR_PERFORMANCE_TESTS
        return func(*args, **kwargs)

    return wrapper


def disable(func):

    @init_and_deinit(func.__name__)
    def wrapper(*args, **kwargs):
        test_params["enable"] = False
        return func(*args, **kwargs)

    return wrapper


def expected_to_fail(func):

    @init_and_deinit(func.__name__)
    def wrapper(*args, **kwargs):
        test_params["expected_to_fail"] = True
        return func(*args, **kwargs)

    return wrapper


def ignore_fail(func):

    @init_and_deinit(func.__name__)
    def wrapper(*args, **kwargs):
        test_params["ignore_fail"] = True
        return func(*args, **kwargs)

    return wrapper


def camera_required(func):

    @init_and_deinit(func.__name__)
    def wrapper(*args, **kwargs):
        test_params["camera_required"] = True
        return func(*args, **kwargs)

    return wrapper


def calibration_required(func):

    @init_and_deinit(func.__name__)
    def wrapper(*args, **kwargs):
        test_params["calibration_required"] = True
        test_params["calibration_file"] = test_data_root[platform.system()] + calibration_file["name"]
        return func(*args, **kwargs)

    return wrapper


def input_data(data, name="input"):

    def input_data_wrapper(func):

        @init_and_deinit(func.__name__)
        def test_wrapper(*args, **kwargs):
            test_params["data_required"] = True
            test_params[name + "_file"] = test_data_root[platform.system()] + data["name"]
            test_params[name + "_hash"] = data["hash"]
            test_params[name + "_size"] = data["size"]
            return func(*args, **kwargs)

        return test_wrapper

    return input_data_wrapper


def named_parameter_list(param_list):

    def named_parameter_list_wrapper(func):

        @init_and_deinit(func.__name__)
        def test_wrapper(*args, **kwargs):
            for item in param_list:
                if len(item) != 2:
                    raise ValueError("Named parameters should have two elements")
                test_params[item[0]] = item[1]
            return func(*args, **kwargs)

        return test_wrapper

    return named_parameter_list_wrapper


def standard_params(func):

    @init_and_deinit(func.__name__)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)

    return wrapper


def set_test_name(name):
    test_params["name"] = name


def reset_test_params():
    test_params = {}


def remove_output_files(test_name):
    files = os.listdir(os.getenv("DV_TEST_OUTPUT_DIR"))
    for file in files:
        file_path = os.path.join(os.getenv("DV_TEST_OUTPUT_DIR"), file)
        if os.path.isfile(file_path) and test_name is not None and test_name in file:
            os.remove(file_path)


def move_output_files_to_failed_tests(test_name):
    files = os.listdir(os.getenv("DV_TEST_OUTPUT_DIR"))
    for file in files:
        file_path = os.path.join(os.getenv("DV_TEST_OUTPUT_DIR"), file)
        if os.path.isfile(file_path) and test_name is not None and test_name in file:
            shutil.move(file_path, os.getenv("DV_FAILED_TESTS_OUTPUT_DIR"))
