cmake_minimum_required(VERSION 3.17)

option(ENABLE_ICONV "Use libiconv to convert encoding" ON)
option(ENABLE_STANDALONE "Build utils standalone" OFF)
option(INSTALL_DEP_FILES "Install a file with dependences." OFF)
option(ENABLE_SSL "Enable SSL" OFF)
option(ENABLE_ZLIB "Use Zlib to uncompress http data." OFF)
option(ENABLE_UTILS_TESTING "Test utils with GTest." OFF)

if (ENABLE_STANDALONE)
    project(utils)
    include(GNUInstallDirs)
endif()

if (MSVC)
    add_compile_options(/utf-8)
    add_compile_options(/EHsc)
endif()

include_directories(${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${CMAKE_CURRENT_BINARY_DIR})

list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}")
if (ENABLE_ICONV)
    find_package(Iconv)
endif()

if (ENABLE_SSL)
    find_package(OpenSSL 3.0.0 REQUIRED)
    set(HAVE_OPENSSL 1)
endif()

if (ENABLE_ZLIB)
    find_package(ZLIB REQUIRED)
    set(HAVE_ZLIB 1)
endif()


if (Iconv_FOUND)
    set(HAVE_ICONV 1)
endif()

include(CheckIncludeFile)
include(CheckSymbolExists)
include(TestStrerrorR)
if (WIN32)
    check_symbol_exists(_access_s io.h HAVE__ACCESS_S)
    check_symbol_exists(_waccess_s io.h HAVE__WACCESS_S)
    check_symbol_exists(strerror_s "string.h" HAVE_STRERROR_S)
    check_symbol_exists(_wcserror_s "string.h" HAVE__WCSERROR_S)
    check_symbol_exists(printf_s "stdio.h" HAVE_PRINTF_S)
    check_symbol_exists(fprintf_s "stdio.h" HAVE_FPRINTF_S)
    check_symbol_exists(sscanf_s "stdio.h" HAVE_SSCANF_S)
    check_symbol_exists(_stricmp "string.h" HAVE__STRICMP)
    check_symbol_exists(_strnicmp "string.h" HAVE__STRNICMP)
    check_symbol_exists(_mkgmtime "time.h" HAVE__MKGMTIME)
    check_symbol_exists(_get_timezone "time.h" HAVE__GET_TIMEZONE)
else()
    check_symbol_exists(fseeko "stdio.h" HAVE_FSEEKO)
    check_symbol_exists(fseeko64 "stdio.h" HAVE_FSEEKO64)
    check_symbol_exists(ftello "stdio.h" HAVE_FTELLO)
    check_symbol_exists(ftello64 "stdio.h" HAVE_FTELLO64)
endif()
check_symbol_exists(strcasecmp "string.h" HAVE_STRCASECMP)
check_symbol_exists(strncasecmp "string.h" HAVE_STRNCASECMP)
check_symbol_exists(strerror_r "string.h" HAVE_STRERROR_R)
check_symbol_exists(strptime "time.h" HAVE_STRPTIME)
if (NOT HAVE_STRPTIME)
    set(TMP "${CMAKE_REQUIRED_DEFINITIONS}")
    set(CMAKE_REQUIRED_DEFINITIONS -D_XOPEN_SOURCE)
    check_symbol_exists(strptime "time.h" HAVE_STRPTIME1)
    if (HAVE_STRPTIME1)
        add_compile_definitions(_XOPEN_SOURCE)
        set(HAVE_STRPTIME 1)
    endif()
    set(CMAKE_REQUIRED_DEFINITIONS "${TMP}")
endif()
set(HAVE_GNU_SOURCE OFF)
if (NOT WIN32)
    set(TMP "${CMAKE_REQUIRED_DEFINITIONS}")
    set(CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE)
    check_symbol_exists(fcloseall "stdio.h" HAVE_FCLOSEALL)
    check_symbol_exists(timegm "time.h" HAVE_TIMEGM)
    if (HAVE_FCLOSEALL OR HAVE_TIMEGM)
        add_compile_definitions(_GNU_SOURCE)
        set(HAVE_GNU_SOURCE ON)
    endif()
    set(CMAKE_REQUIRED_DEFINITIONS "${TMP}")
endif()
if (HAVE_STRERROR_R)
    test_strerror_r(HAVE_GNU_STRERROR_R ${HAVE_GNU_SOURCE})
endif()
if (NOT MSVC)
    check_symbol_exists(timezone "time.h" HAVE_TIMEZONE)
    check_symbol_exists(tzset "time.h" HAVE_TZSET)
endif()
if (NOT WIN32)
    check_symbol_exists(clock_gettime "time.h" HAVE_CLOCK_GETTIME)
    check_symbol_exists(usleep "unistd.h" HAVE_USLEEP)
    check_symbol_exists(nanosleep "time.h" HAVE_NANOSLEEP)
    CHECK_INCLUDE_FILE("netinet/in.h" HAVE_NETINET_IN_H)
endif()
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/utils_config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/utils_config.h")

if ("${CMAKE_C_COMPILER_ID}" STREQUAL GNU)
    add_compile_options(-fPIC)
endif()

set(SOURCE_FILE
    cfileop.cpp
    cpp2c.cpp
    cstr_util.c
    err.cpp
    fileop.cpp
    wchar_util.cpp
    memfile.c
    cmath.c
    time_util.cpp
    encoding.cpp
    str_util.cpp
    c_linked_list.cpp
    file_reader.c
    urlparse.cpp
    http_client.cpp
    utils_static.cpp
    hash_map.cpp
    reg_util.cpp
    hash_lib.cpp
)
set(SOURCE_FILE_HEADERS
    cfileop.h
    cpp2c.h
    cstr_util.h
    dict.h
    err.h
    fileop.h
    list_pointer.h
    wchar_util.h
    memfile.h
    cmath.h
    time_util.h
    encoding.h
    str_util.h
    linked_list.h
    c_linked_list.h
    file_reader.h
    urlparse.h
    http_client.h
    stack.h
    linked_stack.h
    circular_queue.h
    binary_tree.h
    binary_search_tree.h
    utils_static.h
    hash_map.h
    reg_util.h
    hash_lib.h
    stream.h
)

if (NOT HAVE_STRPTIME)
    list(APPEND SOURCE_FILE strptime/strptime.c strptime/strptime.h)
endif()

add_library(utils STATIC ${SOURCE_FILE} ${SOURCE_FILE_HEADERS})
target_compile_definitions(utils PRIVATE HAVE_UTILS_CONFIG_H)
if (Iconv_FOUND)
    if (TARGET Iconv::Iconv)
        target_link_libraries(utils Iconv::Iconv)
    endif()
endif()
if (NOT MSVC)
    target_link_libraries(utils m)
endif()
if (INSTALL_DEP_FILES)
    if (WIN32)
        target_link_libraries(utils shell32)
    endif()
endif()
if (WIN32)
    target_link_libraries(utils Ws2_32)
endif()
if (ENABLE_SSL)
    target_link_libraries(utils OpenSSL::SSL OpenSSL::Crypto)
endif()
if (ENABLE_ZLIB)
    target_link_libraries(utils ZLIB::ZLIB)
endif()
target_compile_features(utils PRIVATE cxx_std_17)
if (ENABLE_STANDALONE)
    install(TARGETS utils)
endif()
if (INSTALL_DEP_FILES)
    get_target_property(OUT utils LINK_LIBRARIES)
    if (OUT)
        file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/utils_dep.txt" "${OUT}")
        install(FILES "${CMAKE_CURRENT_BINARY_DIR}/utils_dep.txt" DESTINATION ${CMAKE_INSTALL_PREFIX})
    endif()
endif()

if (ENABLE_UTILS_TESTING)
    # For Windows: Prevent overriding the parent project's compiler/linker settings
    set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
    add_subdirectory(googletest)
    enable_testing()
    add_executable(unittest test/stack_test.cpp test/queue_test.cpp test/binary_tree_test.cpp
    test/hash_map_test.cpp test/hash_lib_test.cpp)
    target_link_libraries(unittest GTest::gtest_main utils)
    include(GoogleTest)
    gtest_discover_tests(unittest)
endif()
