# python binding.

# recompile libsmoldyn for PYTHON.
set_source_files_properties(${SRC_FILES} PROPERTIES LANGUAGE CXX )
add_library(_pysmoldyn STATIC ${SRC_FILES})
target_include_directories(_pysmoldyn PRIVATE ${Python3_INCLUDE_DIRS} ${PYBIND11_SOURCE_DIR}/include)

# COMPILE_AS_PY_MODULE changes the behaviour of libSteve at a few places
# especially where scanf is used for user input.
target_compile_options(_pysmoldyn PRIVATE -DCOMPILE_AS_PY_MODULE)

# Enables PYTHON_CALLBACK extention.
target_compile_options(_pysmoldyn PRIVATE -DENABLE_PYTHON_CALLBACK)

# This is suggested by pybind11: reduces binary size and disables some warnings.
target_compile_options(_pysmoldyn PRIVATE -fvisibility=hidden)

set_target_properties(_pysmoldyn PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_link_libraries(_pysmoldyn PRIVATE ${DEP_LIBS})
target_link_libraries(_pysmoldyn PUBLIC ${PYTHON_LIBRARIES})

# A custom target to install dependencies. Useful on various CI
add_custom_target(install_deps 
    COMMAND ${Python3_EXECUTABLE} -m pip install wheel numpy setuptools --user
    COMMENT "Installing Python modules for testing"
    WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
    VERBATIM)

# FIXME: If it comes in your way, comment it out.
# FIXME: FOllowing runs on github actions and other CI (for Windows only).
# Its very hard to locate correct version of Python on every cloud based CI. 
# So I let cmake install the dependencies at build time.
if(WINDOWS_BUILD)
    add_custom_command(TARGET _pysmoldyn PRE_BUILD
        COMMAND ${Python3_EXECUTABLE} -m pip install wheel numpy setuptools --user
        COMMENT "Installing build dependencies on Windows"
        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
        VERBATIM)
endif()

pybind11_add_module(_smoldyn MODULE CallbackFunc.cpp module.cpp)
target_compile_options(_smoldyn PRIVATE 
    -DSMOLDYN_VERSION=${SMOLDYN_VERSION} 
    -DENABLE_PYTHON_CALLBACK 
    # https://pybind11.readthedocs.io/en/stable/faq.html#someclass-declared-with-greater-visibility-than-the-type-of-its-field-someclass-member-wattributes
    -fvisibility=hidden)
target_include_directories(_smoldyn PRIVATE 
    ${PYTHON_INCLUDE_DIRS}
    ${CMAKE_SOURCE_DIR}/source/external/pybind11/include)

# If generator pattern is not used here, cmake produces module in Release/Debug folders
# which will break the wheels.
set_target_properties(_smoldyn PROPERTIES
    LIBRARY_OUTPUT_DIRECTORY $<1:${CMAKE_CURRENT_SOURCE_DIR}/smoldyn/>)

# ../examples/CMakeLists.txt file.
set(PY_SMOLDYN_OUTDIR ${CMAKE_BINARY_DIR}/py)
file(MAKE_DIRECTORY ${PY_SMOLDYN_OUTDIR})

add_custom_target(copy_python_tree ALL 
    DEPENDS _smoldyn
    COMMAND ${CMAKE_COMMAND} -E copy_directory
        ${CMAKE_SOURCE_DIR}/source/python/smoldyn ${PY_SMOLDYN_OUTDIR}/smoldyn
    COMMENT "Copying python source tree to binary directory"
    VERBATIM)

target_link_libraries(_smoldyn PRIVATE _pysmoldyn)

# copy setup.py.in to setup.py
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/setup.py.in
    ${PY_SMOLDYN_OUTDIR}/setup.py)

# Wheel is the main python target. It is generated by default.
add_custom_target(wheel ALL 
    DEPENDS _smoldyn
    COMMAND ${Python3_EXECUTABLE} setup.py bdist_wheel -d ${CMAKE_BINARY_DIR} 
    WORKING_DIRECTORY ${PY_SMOLDYN_OUTDIR}
    COMMENT "Generating wheel in ${CMAKE_BINARY_DIR}"
    VERBATIM)

# This target is used by install script (useful in packaging).
add_custom_target(pyinstall
    COMMAND ${Python3_EXECUTABLE} setup.py install 
      --prefix ${CMAKE_INSTALL_PREFIX}
    WORKING_DIRECTORY ${PY_SMOLDYN_OUTDIR}
    COMMENT "RUNNING: python3 setup.py install --prefix ${CMAKE_INSTALL_PREFIX}"
    VERBATIM)

add_custom_target(pyinstall_venv
    DEPENDS _smoldyn
    COMMAND ${Python3_EXECUTABLE} setup.py install
    WORKING_DIRECTORY ${PY_SMOLDYN_OUTDIR}
    COMMENT "RUNNING: python3 setup.py install"
    VERBATIM)

add_custom_target(pyuninstall
    COMMAND ${Python3_EXECUTABLE} -m pip uninstall smoldyn
    COMMENT "RUNNING: `pip uninstall smoldyn`"
    VERBATIM)

add_custom_target(pydevel
    DEPENDS _smoldyn
    COMMAND ${Python3_EXECUTABLE} setup.py develop --user
    WORKING_DIRECTORY ${PY_SMOLDYN_OUTDIR}
    COMMENT "RUNNING: python3 setup.py develop --user"
    VERBATIM)

# Run static type checking using mypy (developer only). Works best with
# python3.7+
add_custom_target(lint 
    COMMAND mypy ${CMAKE_SOURCE_DIR}/source/python/smoldyn/*.py
    WORKING_DIRECTORY ${CMAKE_BINARY_DIR})

add_custom_target(lint_strict
    COMMAND mypy --strict ${CMAKE_SOURCE_DIR}/source/python/smoldyn/*.py
    WORKING_DIRECTORY ${CMAKE_BINARY_DIR})

#
# Install target 
#
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../../scripts/install_wheel.py.in
    ${CMAKE_BINARY_DIR}/install_wheel.py)

install(CODE "execute_process(
    COMMAND ${Python3_EXECUTABLE} install_wheel.py
    WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
    )")

###  python tests ###
enable_testing()
file(GLOB TEST_SCRIPTS "${CMAKE_SOURCE_DIR}/tests/test_*.py")
foreach(_pyscript ${TEST_SCRIPTS})
    # message(STATUS "Adding test ${_pyscript}") 
    get_filename_component(_test_name ${_pyscript} NAME_WE)
    get_filename_component(_test_dir ${_pyscript} DIRECTORY)
    add_test(NAME ${_test_name} COMMAND 
        ${Python3_EXECUTABLE} ${_pyscript}
        WORKING_DIRECTORY ${_test_dir})
    set_tests_properties(${_test_name} PROPERTIES 
        ENVIRONMENT "PYTHONPATH=${PY_SMOLDYN_OUTDIR};SMOLDYN_NO_PROMPT=1")
endforeach()
