cmake_minimum_required(VERSION 3.19)

if (POLICY CMP0135)
    cmake_policy(SET CMP0135 NEW)
endif()

if (POLICY CMP0167)
    cmake_policy(SET CMP0167 NEW)
endif()

project(POLICE VERSION 0.1)

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

################################################################################
# Parameters

set(POLICE_LIB "policelib")
set(POLICE_EXE "police")
set(POLICE_TESTS "police-tests")

option(POLICE_Z3 "Z3 support" ON)
option(POLICE_GUROBI "Gurobi support" OFF)
option(POLICE_MARABOU "Marabou support" OFF)
option(POLICE_VERITAS "Veritas support" ON)
option(POLICE_EIGEN "Eigen support" ON)

option(POLICE_STATISTICS "Enable extra statistics" OFF)
option(POLICE_SAT_IC3 "IC3 engine using SAT/SMT-based technology" OFF)
option(POLICE_SYNTACTIC_IC3 "IC3 engine using model-dedicated syntactic reasoning" ON)

option(ENABLE_TESTS "Enable tests" OFF)
option(ENABLE_LIBCPP "Use clang's libc++" OFF)
option(ENABLE_SANITIZERS "Enable compilation with sanitizers" OFF)

################################################################################
# Compile options

set(CMAKE_EXPORT_COMPILE_COMMANDS On)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED On)

set(CMAKE_CXX_FLAGS "-Wall -Wextra -Wpedantic -Werror")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG")
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -ggdb")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -g -ggdb -DNDEBUG")
# set(CMAKE_LINK_LIBRARY_FLAG "-Wl,--no-as-needed")

if (ENABLE_LIBCPP)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
endif()

if (ENABLE_SANITIZERS)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fsanitize=address -fsanitize=leak")
endif()


################################################################################

# Prepare
add_library(${POLICE_LIB})
set(POLICE_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/src;${CMAKE_CURRENT_BINARY_DIR}/src")
target_include_directories(${POLICE_LIB} PUBLIC ${POLICE_INCLUDE_DIR})

add_executable(${POLICE_EXE})
target_link_libraries(${POLICE_EXE} PRIVATE -rdynamic -Wl,-whole-archive ${POLICE_LIB} -Wl,-no-whole-archive)

################################################################################
# Dependencies

include(FetchContent)

# JSON
FetchContent_Declare(
    json 
    URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz
)
FetchContent_MakeAvailable(json)

target_link_libraries(${POLICE_LIB} PUBLIC nlohmann_json::nlohmann_json)

# Catch2
FetchContent_Declare(
    Catch2
    GIT_REPOSITORY https://github.com/catchorg/Catch2.git
    GIT_TAG        v2.13.10 # or a later release
)
FetchContent_MakeAvailable(Catch2)

# Eigen
find_package (Eigen3 REQUIRED NO_MODULE)
if (${POLICE_EIGEN})
    if (TARGET Eigen3::Eigen)
        message(STATUS "Found Eigen in ${Eigen3_DIR}")
        add_definitions("-DPOLICE_EIGEN")
        target_link_libraries(${POLICE_LIB} PUBLIC Eigen3::Eigen)
    endif()
endif()

# Z3
find_package(Z3 QUIET)
if(${POLICE_Z3} AND ${Z3_FOUND})
    message(STATUS "Found Z3 in ${Z3_LIBRARIES}")
    add_definitions("-DPOLICE_Z3")
    target_include_directories(${POLICE_LIB} PRIVATE ${Z3_INCLUDE_DIRS} ${Z3_CXX_INCLUDE_DIRS})
    target_link_libraries(${POLICE_LIB} PRIVATE ${Z3_LIBRARIES})
else()
    message(STATUS "Compiling without Z3 support.")
endif()

# Gurobi
find_package(GUROBI QUIET)
if(${POLICE_GUROBI} AND ${GUROBI_FOUND})
    message(STATUS "Found Gurobi in ${GUROBI_LIBRARY}")
    add_definitions("-DPOLICE_GUROBI")
    target_include_directories(${POLICE_LIB} PRIVATE ${GUROBI_INCLUDE_DIRS})
    target_link_libraries(${POLICE_LIB} PRIVATE ${GUROBI_CXX_LIBRARY} ${GUROBI_LIBRARY})
else()
    message(STATUS "Compiling without Gurobi support.")
endif()

# Marabou
find_package(Marabou)
if(${POLICE_MARABOU} AND ${Marabou_FOUND})
    message(STATUS "Found Marabou in ${Marabou_LIBRARY}")
    add_definitions("-DPOLICE_MARABOU")
    target_include_directories(${POLICE_LIB} SYSTEM PRIVATE ${Marabou_INCLUDE_DIRS})
    target_link_libraries(${POLICE_LIB} PRIVATE ${Marabou_LIBRARY})
else()
    message(STATUS "Compiling without Marabou support.")
endif()

# Veritas
find_package(Veritas)
if(${POLICE_VERITAS} AND ${Veritas_FOUND})
    message(STATUS "Found Veritas in ${Veritas_LIBRARY}")
    add_definitions("-DPOLICE_VERITAS")
    target_include_directories(${POLICE_LIB} SYSTEM PRIVATE ${Veritas_INCLUDE_DIRS})
    target_link_libraries(${POLICE_LIB} PRIVATE ${Veritas_LIBRARY})
else()
    message(STATUS "Compiling without Veritas support.")
endif()

################################################################################

execute_process(
    COMMAND git log -1 --format=%h
    WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
    OUTPUT_VARIABLE GIT_HASH
    OUTPUT_STRIP_TRAILING_WHITESPACE
)

################################################################################

if(NOT POLICE_STATISTICS)
    add_definitions("-DPOLICE_NO_STATISTICS")
endif()

if (POLICE_SYNTACTIC_IC3)
    add_definitions("-DPOLICE_SYN_IC3")
endif()

if (POLICE_SAT_IC3)
    add_definitions("-DPOLICE_SAT_IC3")
endif()

add_subdirectory(src/police)
add_subdirectory(utils)

if(ENABLE_TESTS)
    message(STATUS "Unit tests enabled")

    enable_testing()
    # include(CTest)
    # include(Catch)

    add_executable(${POLICE_TESTS})
    target_link_libraries(${POLICE_TESTS} PRIVATE
        -rdynamic -Wl,-whole-archive ${POLICE_LIB} 
        -Wl,-no-whole-archive Catch2::Catch2
    )

    add_subdirectory(test)

    # catch_discover_tests(${POLICE_TESTS})
endif()
