cmake_minimum_required(VERSION 3.10)

file(READ "libclingo-lpx/clingo-lpx.h" main)
string(REGEX MATCH "#define CLINGOLPX_VERSION \"([^\"]*)\"" clingov ${main})

project(CLINGOLPX VERSION "${CMAKE_MATCH_1}" LANGUAGES C CXX)
if (POLICY CMP0063)
    cmake_policy(SET CMP0063 NEW)
endif()
if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
    message(STATUS "No build type selected - using 'Release'")
    set(CMAKE_BUILD_TYPE "Release")
endif()

# Enable folders in IDEs like Visual Studio
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS ON)

list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")

include(GNUInstallDirs)
include(CMakeDependentOption)
include(CheckCXXCompilerFlag)
include(ClingoLPXUtils)

clingolpx_add_clang_c2y_suppression()

option(CLINGOLPX_MANAGE_RPATH "set rpath if not installed into system directory" ON)
option(CLINGOLPX_BUILD_TESTS "build tests" OFF)
option(CLINGOLPX_BUILD_SHARED "build clingo-lpx library shared" ON)
option(CLINGOLPX_CROSSCHECK "enable additional state checking" OFF)
option(CLINGOLPX_PROFILE "Enable profiling using gperftools" OFF)
set(CLINGOLPX_MATH_LIBRARY "auto" CACHE STRING "math library: auto, imath, flint")
option(CLINGOLPX_USE_LOCAL_CATCH
"Advanced option to build against bundled or installed catch."
ON)

set(PYCLINGOLPX_ENABLE "auto" CACHE STRING "build with python support: OFF, auto, require, pip
auto: enable python support if python is available
require: enable python support and fails if python is not available
pip: enable python support and link against the clingo python module")
set(PYCLINGOLPX_INSTALL "prefix" CACHE STRING "configure where to install python module: user, system, prefix
user: install into user prefix
system: install into python library
prefix: install into install prefix")
set(PYCLINGOLPX_SUFFIX "" CACHE STRING "suffix for python module")
set(PYCLINGOLPX_INSTALL_DIR "" CACHE STRING "path where to install python module")

mark_as_advanced(CLINGOLPX_BUILD_STATIC)
mark_as_advanced(CLINGOLPX_BUILD_SHARED)
mark_as_advanced(CLINGOLPX_CROSSCHECK)
mark_as_advanced(CLINGOLPX_PROFILE)
mark_as_advanced(PYCLINGOLPX_SUFFIX)
mark_as_advanced(PYCLINGOLPX_INSTALL_DIR)

if (MSVC)
    set(VC_RELEASE_LINK_OPTIONS /LTCG)
    SET(CMAKE_EXE_LINKER_FLAGS_RELEASE    "${CMAKE_EXE_LINKER_FLAGS_RELEASE}    ${VC_RELEASE_LINK_OPTIONS}")
    SET(CMAKE_MODULE_LINKER_FLAGS_RELEASE "${CMAKE_MODULE_LINKER_FLAGS_RELEASE} ${VC_RELEASE_LINK_OPTIONS}")
    SET(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} ${VC_RELEASE_LINK_OPTIONS}")
    SET(CMAKE_STATIC_LINKER_FLAGS_RELEASE "${CMAKE_STATIC_LINKER_FLAGS_RELEASE} ${VC_RELEASE_LINK_OPTIONS}")
endif()

if (POLICY CMP0063 AND CLINGOLPX_BUILD_SHARED)
    set(CMAKE_CXX_VISIBILITY_PRESET hidden)
    set(CMAKE_C_VISIBILITY_PRESET hidden)
endif()

if (CLINGOLPX_BUILD_TESTS)
    enable_testing()
endif()

if (CLINGOLPX_MANAGE_RPATH)
    set(CMAKE_SKIP_BUILD_RPATH FALSE)
    set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
    set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
    list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_FULL_LIBDIR}" isSystemDir)
    if ("${isSystemDir}" STREQUAL "-1")
        set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_FULL_LIBDIR}")
    endif()
endif()

if (NOT CMAKE_ARCHIVE_OUTPUT_DIRECTORY)
    set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
endif()
if (NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY)
    set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
endif()
if (NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY)
    set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
endif()
if (NOT CMAKE_IMPORT_LIBRARY_PREFIX)
    set(CMAKE_IMPORT_LIBRARY_PREFIX import_)
endif()

find_package(Threads)

try_compile(
    CLINGOLPX_HAS_SHARED_MUTEX "${CMAKE_BINARY_DIR}/try_compile"
    SOURCES "${CMAKE_SOURCE_DIR}/cmake/shared_mutex.cc"
    LINK_LIBRARIES Threads::Threads)

if (NOT CLINGOLPX_HAS_SHARED_MUTEX)
    message(WARNING "shared mutex support not available falling back to mutex")
endif()

if (CLINGOLPX_MATH_LIBRARY STREQUAL "imath" OR PYCLINGOLPX_ENABLE STREQUAL "pip")
    set(_CLINGOLPX_MATH_LIBRARY "imath")
elseif (CLINGOLPX_MATH_LIBRARY STREQUAL "auto")
    find_package(FLINT)
    if (FLINT_FOUND)
        set(_CLINGOLPX_MATH_LIBRARY "flint")
    else()
        set(_CLINGOLPX_MATH_LIBRARY "imath")
    endif()
elseif (CLINGOLPX_MATH_LIBRARY STREQUAL "flint")
    find_package(FLINT REQUIRED)
    set(_CLINGOLPX_MATH_LIBRARY "flint")
else()
    message(FATAL_ERROR "error unknown math library")
endif()

add_subdirectory(third_party)

add_library(MATH::MATH INTERFACE IMPORTED)
if (_CLINGOLPX_MATH_LIBRARY STREQUAL "flint")
    set_property(TARGET MATH::MATH PROPERTY INTERFACE_COMPILE_DEFINITIONS CLINGOLPX_USE_FLINT)
    set_property(TARGET MATH::MATH PROPERTY INTERFACE_LINK_LIBRARIES FLINT::FLINT)
elseif (_CLINGOLPX_MATH_LIBRARY STREQUAL "imath")
    set_property(TARGET MATH::MATH PROPERTY INTERFACE_COMPILE_DEFINITIONS CLINGOLPX_USE_IMATH)
    set_property(TARGET MATH::MATH PROPERTY INTERFACE_LINK_LIBRARIES libimath)
else()
endif()

if(CLINGOLPX_PROFILE)
    find_package(Gperftools REQUIRED)
endif()

if(PYCLINGOLPX_ENABLE STREQUAL "pip" AND CMAKE_VERSION VERSION_LESS "3.18")
    message(FATAL_ERROR
        "PYCLINGOLPX_ENABLE=pip requires CMake >= 3.18 "
        "(found ${CMAKE_VERSION})")
endif()

if(NOT CMAKE_VERSION VERSION_LESS "3.15.0")
    if(DEFINED PYTHON_EXECUTABLE AND NOT DEFINED Python_EXECUTABLE)
        set(Python_EXECUTABLE "${PYTHON_EXECUTABLE}")
    endif()
    if(DEFINED PYTHON_INCLUDE_DIR AND NOT DEFINED Python_INCLUDE_DIR)
        set(Python_INCLUDE_DIR "${PYTHON_INCLUDE_DIR}")
    endif()
    if(DEFINED PYTHON_LIBRARY AND NOT DEFINED Python_LIBRARY)
        set(Python_LIBRARY "${PYTHON_LIBRARY}")
    endif()
    if(POLICY CMP0094)
        cmake_policy(SET CMP0094 NEW)
    endif()
    set(_args Interpreter)
    if(PYCLINGOLPX_ENABLE)
        if(PYCLINGOLPX_ENABLE STREQUAL "pip")
            set(_args ${_args} Development.Module)
        else()
            set(_args ${_args} Development)
        endif()
        if(NOT PYCLINGOLPX_ENABLE STREQUAL "auto")
            set(_args ${_args} REQUIRED)
        endif()
    endif()
    find_package(Python "3.6" COMPONENTS ${_args})
else()
    find_package(PythonInterp)
    set(Python_EXECUTABLE "${PYTHON_EXECUTABLE}")
    set(Python_VERSION_MAJOR "${PYTHON_VERSION_MAJOR}")
    if(PYCLINGOLPX_ENABLE)
        if(NOT PYCLINGOLPX_ENABLE STREQUAL "auto")
            find_package(PythonLibs "3.6" REQUIRED)
        else()
            find_package(PythonLibs "3.6")
        endif()
        if(PYTHONLIBS_FOUND)
            add_library(Python::Python INTERFACE IMPORTED)
            add_library(Python::Module INTERFACE IMPORTED)
            set(Python_Development_FOUND ON)
            set(Python_Development.Module_FOUND ON)
            set(Python_INCLUDE_DIRS "${PYTHON_INCLUDE_DIRS}")
            set(Python_VERSION "${PYTHONLIBS_VERSION_STRING}")
            set_property(TARGET Python::Python PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${PYTHON_INCLUDE_DIRS}")
            set_property(TARGET Python::Module PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${PYTHON_INCLUDE_DIRS}")
            set_property(TARGET Python::Python PROPERTY INTERFACE_LINK_LIBRARIES "${PYTHON_LIBRARIES}")
            if(MSVC)
                set_property(TARGET Python::Module PROPERTY INTERFACE_LINK_LIBRARIES "${PYTHON_LIBRARIES}")
            endif()
        endif()
    endif()
endif()
if(Python_Development_FOUND AND Python_VERSION_MAJOR LESS 3)
    message(FATAL_ERROR "Clingo does not support Python 2.")
endif()

add_library(clingolpx_python_config INTERFACE)
if(MSVC)
    target_compile_definitions(clingolpx_python_config INTERFACE Py_NO_LINK_LIB)
endif()
get_filename_component(_py_lib_name "${Python_LIBRARY}" NAME_WE)
target_compile_definitions(clingolpx_python_config INTERFACE _CFFI_NO_LIMITED_API)
if(_py_lib_name MATCHES "python[0-9]+[0-9]+t$")
    target_compile_definitions(clingolpx_python_config INTERFACE Py_GIL_DISABLED)
endif()

if(PYCLINGOLPX_ENABLE STREQUAL "pip")
    if(NOT CLINGOLPX_BUILD_SHARED)
        message(FATAL_ERROR "building against the pip package requires CLINGOLPX_BUILD_SHARED.")
    endif()
    add_library(libclingo INTERFACE)
    execute_process(
        COMMAND "${Python_EXECUTABLE}" -c "import importlib.util, os; print(os.path.dirname(importlib.util.find_spec('clingo').origin))"
        OUTPUT_VARIABLE _clingo_path
        OUTPUT_STRIP_TRAILING_WHITESPACE
        RESULT_VARIABLE _clingo_path_rc
    )
    if(NOT _clingo_path_rc EQUAL 0)
        message(FATAL_ERROR "Could not determine clingo path from build environment")
    endif()
    file(TO_CMAKE_PATH "${_clingo_path}" _PYCLINGOLPX_PIP_PATH)
    message(STATUS "Using clingo path: ${_clingo_path}")

    target_include_directories(libclingo INTERFACE "${_clingo_path}")
    if(WIN32)
        target_link_libraries(libclingo INTERFACE "${_clingo_path}/import__clingo.lib")
    elseif(APPLE)
        target_link_options(libclingo INTERFACE "-undefined" "dynamic_lookup")
    endif()
    add_subdirectory(libclingo-lpx)
    add_subdirectory(libpyclingo-lpx)
else()
    find_package(Clingo REQUIRED)
    add_subdirectory(libclingo-lpx)
    if(PYCLINGOLPX_ENABLE AND Python_Development_FOUND)
        add_subdirectory(libpyclingo-lpx)
    endif()
    add_subdirectory(app)
endif()
