#
# CamiTK python module test directly from python (outside the CamiTK python interpreter)
#
# To test manually:
# - cd build
# - PYTEST_SRC=../sdk/libraries/python/testing; PYTEST_VENV=sdk/libraries/python/testing/.venv;
# - get the output of pytest_discover_tests:
#   ${PYTEST_VENV}/bin/python ${PYTEST_SRC}/pytest_discover_tests.py ${PYTEST_SRC}/camitk_python_test.py
# - to run a test called test_foo (name in ptest) / camitk-python-test-foo (name in ctest) 
#   - directly in pytest:
#     PYTHONPATH=lib ${PYTEST_VENV}/bin/python -m pytest -vv ${PYTEST_SRC}/camitk_python_test.py -k test_foo
#     (note PYTHONPATH is required so that the CamiTK python module can be found when "import camitk" is called)
#   - from ctest:
#     ctest -VV -R camitk-python-test-foo
#
import pytest
import re
import sys

if sys.platform == "win32":
    # Preload camitkcore DLL on windows (this is required for python to be able to import camitk.pyd)
    # This is required when the camitk python module is loaded (using "import camitk") 
    # outside the embedded python interpreter
    import ctypes
    import os  
    dll_dir = os.environ['PYTHONPATH'] # assume python path contains both camitkcore DLL and camitk python pyd file
    assert os.path.exists(dll_dir), f"Pythonpath does not exist: {dll_dir}"
    dll_path = dll_dir + r"\camitkcore.dll"
    if not os.path.exists(dll_path):
        dll_path =  dll_dir + r"\camitkcore-debug.dll"
    assert os.path.exists(dll_path), f"Cannot find camitkcore (Debug nor Release): {dll_path} in {dll_dir}"  
    ctypes.CDLL(dll_path, winmode=0)

import camitk

def test_debug_info():
    camitk.startApplication()
    
    debug_info = camitk.getDebugInfo()
    print(debug_info)
    pattern = 'Python version: ([0-9.]+)\\n'
    matches = re.findall(pattern, debug_info)
    assert len(matches) == 1, f"Cannot find any python version in\n{debug_info}"
    version = matches[0]

    current_version = ".".join(map(str, sys.version_info[0:3]))    
    assert version == current_version

    python_intrepreter_version = camitk.getPythonVersion()
    assert python_intrepreter_version == '' # as the CamiTK interpreter is not initialized

def test_import_numpy():
    import traceback
    print("Trying to import numpy...")
    try:
        import numpy
        assert numpy.__version__ != ""
    except ImportError as e:
        print("Error importing numpy:", e)
        traceback.print_exc()
        pytest.fail("Error importing numpy:")

def test_get_date():
    from datetime import datetime
    assert datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f') != ""

def test_numpy_random_numbers():
    import numpy as np
    assert len(np.random.rand(10).tolist()) == 10
    
def test_file_output(tmp_path):
    # see https://docs.pytest.org/en/stable/how-to/tmp_path.html
    from datetime import datetime
    import numpy as np
    written_string = "test_file_output on " + datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f') + ' ' + str(np.random.rand(10).tolist())
    d = tmp_path / "camitk_python_test"
    d.mkdir()
    p = d / "test.txt"
    p.write_text(written_string, encoding="utf-8")
    assert p.read_text(encoding="utf-8") == written_string
    assert len(list(tmp_path.iterdir())) == 1

### CAMITK BINDING

def test_print(capsys):
    # see https://docs.pytest.org/en/stable/how-to/capture-stdout-stderr.html
    camitk.startApplication()
    print("Open components: " + str(len(camitk.Application.getTopLevelComponents())))
    captured = capsys.readouterr()
    assert "Open components: 0" in captured.out

def test_action():
    camitk.startApplication()
    
    # get a known action
    action = camitk.Application.getAction("Resample")
    assert str(type(action)) == "<class 'camitk.Action'>"
    assert action.getName() == "Resample"

    # test parameter values
    assert action.getParameterValue("New Image X Dimension") == 256
    action.setParameterValue("New Image X Dimension", -1)
    assert action.getParameterValue("New Image X Dimension") == -1

    # test camitk property
    prop = action.getProperty("New Image X Dimension")
    assert prop.getName() == "New Image X Dimension"
    assert prop.getDescription() == "The new image width (in voxels)."
    assert prop.getUnit() == ""
    assert prop.getReadOnly() == False
    assert prop.getAttribute("minimum") == 1
    assert prop.getAttribute("maximum") == 2**31 - 1 # set as std::numeric_limits<int>::max() in the Resample action constructor
    assert prop.getAttribute("singleStep") == 1
    prop.setAttribute("minimum", -42)
    assert prop.getAttribute("minimum") == -42
    prop.setAttribute("maximum", 0)
    assert prop.getAttribute("maximum") == 0
    prop.setAttribute("singleStep", 5)
    assert prop.getAttribute("singleStep") == 5
    prop.setReadOnly(True)
    assert prop.getReadOnly() == True

# Remove open component test for now
# Testing the CamiTK python module from outside the inner interpreter is not necessarily a good idea

# def test_application_open():
#     camitk.startApplication()
#     assert len(camitk.Application.getTopLevelComponents()) == 0
    
#     # camitk.Application.open(camitk.Core.get_test_data_dir() + "/brain.mha")
#     camitk.Application.open(camitk.Core.getTestDataDir() + "/amos_0336.camitk")
#     # print("Number of top-level components:", len(camitk.Application.getTopLevelComponents()))
#     assert len(camitk.Application.getTopLevelComponents()) == 16
#     assert camitk.Application.getTopLevelComponents()[0].getName() == "amos_0336"


# def test_imagecomponent():
#     import numpy
#     camitk.startApplication()
#     camitk.Application.open(camitk.Core.getTestDataDir() + "/FullHead.mhd")
#     image = camitk.Application.getTopLevelComponents()[0]
#     image_data = image.getImageDataAsNumpy()
#     assert image_data.shape == (94, 256, 256) # as numpy is z-first
#     print(image.getSpacing())
#     spacing = image.getSpacing()
#     expected = numpy.array([0.9375, 0.9375, 1.5], dtype='f')
#     assert numpy.all(expected == spacing)

