cmake_minimum_required(VERSION 3.16)

set(DATECONV_VERSION_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/date_converter.h")
file(READ "${DATECONV_VERSION_HEADER}" DATECONV_VERSION_HEADER_CONTENT)

foreach(component MAJOR MINOR PATCH)
    string(REGEX MATCH
        "#define[ \t]+DATE_CONVERTER_VERSION_${component}[ \t]+([0-9]+)"
        DATECONV_VERSION_${component}_MATCH
        "${DATECONV_VERSION_HEADER_CONTENT}"
    )
    if(NOT DATECONV_VERSION_${component}_MATCH)
        message(FATAL_ERROR
            "DATE_CONVERTER_VERSION_${component} was not found in date_converter.h"
        )
    endif()
    set(DATECONV_VERSION_${component} "${CMAKE_MATCH_1}")
endforeach()

set(DATECONV_VERSION
    "${DATECONV_VERSION_MAJOR}.${DATECONV_VERSION_MINOR}.${DATECONV_VERSION_PATCH}"
)
project(dateconv VERSION "${DATECONV_VERSION}" LANGUAGES C)

include(GNUInstallDirs)

option(BUILD_SHARED_LIBS "Build the shared library" ON)
option(BUILD_STATIC_LIBS "Build the static library" ON)
option(BUILD_TESTING "Enable the test target" ON)

if(NOT BUILD_SHARED_LIBS AND NOT BUILD_STATIC_LIBS)
    message(FATAL_ERROR "At least one of BUILD_SHARED_LIBS or BUILD_STATIC_LIBS must be ON")
endif()

set(DATECONV_SOURCES date_converter.c)

set(LIBM "")
if(UNIX AND NOT APPLE AND NOT CYGWIN AND NOT MSYS)
    set(LIBM "-lm")
endif()

set(DATECONV_WINDOWS_ABI FALSE)
if(WIN32 OR CYGWIN OR MSYS)
    set(DATECONV_WINDOWS_ABI TRUE)
endif()

set(DATECONV_MODE_644
    OWNER_READ OWNER_WRITE
    GROUP_READ
    WORLD_READ
)
set(DATECONV_MODE_755
    OWNER_READ OWNER_WRITE OWNER_EXECUTE
    GROUP_READ GROUP_EXECUTE
    WORLD_READ WORLD_EXECUTE
)

function(dateconv_configure_target target)
    target_compile_features(${target} PUBLIC c_std_99)
    target_include_directories(${target}
        PUBLIC
            $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
            $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
    )
endfunction()

function(dateconv_set_build_mode target mode)
    if(UNIX)
        add_custom_command(TARGET ${target} POST_BUILD
            COMMAND chmod ${mode} "$<TARGET_FILE:${target}>"
            VERBATIM
        )
    endif()
endfunction()

if(BUILD_SHARED_LIBS)
    add_library(dateconv_shared SHARED ${DATECONV_SOURCES})
    dateconv_configure_target(dateconv_shared)
    set_target_properties(dateconv_shared PROPERTIES
        VERSION ${PROJECT_VERSION}
        SOVERSION ${PROJECT_VERSION_MAJOR}
    )

    if(DATECONV_WINDOWS_ABI)
        target_compile_definitions(dateconv_shared
            PRIVATE DATE_CONVERTER_BUILD_SHARED=1
            INTERFACE DATE_CONVERTER_USE_SHARED=1
        )
        if(CYGWIN OR MSYS)
            set_target_properties(dateconv_shared PROPERTIES
                RUNTIME_OUTPUT_NAME dateconv
                ARCHIVE_OUTPUT_NAME dateconv
            )
        else()
            set_target_properties(dateconv_shared PROPERTIES
                RUNTIME_OUTPUT_NAME "dateconv-${PROJECT_VERSION_MAJOR}"
                ARCHIVE_OUTPUT_NAME dateconv
            )
        endif()
        if(MSVC)
            set_property(SOURCE libdateconv_w.rc APPEND PROPERTY
                COMPILE_DEFINITIONS DATE_CONVERTER_MSVC_RESOURCE=1
            )
            set_target_properties(dateconv_shared PROPERTIES PREFIX "")
        else()
            set_target_properties(dateconv_shared PROPERTIES
                PREFIX "lib"
                IMPORT_PREFIX "lib"
            )
        endif()
        target_sources(dateconv_shared PRIVATE
            libdateconv_w.rc
            libdateconv.def
        )
    else()
        target_compile_options(dateconv_shared PRIVATE
            $<$<COMPILE_LANG_AND_ID:C,GNU,Clang,AppleClang>:-fvisibility=hidden>
        )
        set_target_properties(dateconv_shared PROPERTIES OUTPUT_NAME dateconv)
        if(APPLE)
            set_target_properties(dateconv_shared PROPERTIES
                MACOSX_RPATH OFF
                INSTALL_NAME_DIR ""
                BUILD_WITH_INSTALL_NAME_DIR ON
            )
        endif()
    endif()

    if(LIBM)
        target_link_libraries(dateconv_shared PRIVATE m)
    endif()

    dateconv_set_build_mode(dateconv_shared 755)
    set(DATECONV_LINK_TARGET dateconv_shared)
endif()

if(BUILD_STATIC_LIBS)
    add_library(dateconv_static STATIC ${DATECONV_SOURCES})
    dateconv_configure_target(dateconv_static)
    if(WIN32 AND MSVC)
        set_target_properties(dateconv_static PROPERTIES
            OUTPUT_NAME dateconv_static
            PREFIX ""
        )
    else()
        set_target_properties(dateconv_static PROPERTIES OUTPUT_NAME dateconv)
    endif()
    if(LIBM)
        target_link_libraries(dateconv_static PUBLIC m)
    endif()

    dateconv_set_build_mode(dateconv_static 644)
    if(NOT DEFINED DATECONV_LINK_TARGET)
        set(DATECONV_LINK_TARGET dateconv_static)
    endif()
endif()

if(BUILD_SHARED_LIBS)
    install(TARGETS dateconv_shared
        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
            PERMISSIONS ${DATECONV_MODE_644}
        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
            PERMISSIONS ${DATECONV_MODE_755}
        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
            PERMISSIONS ${DATECONV_MODE_755}
    )
endif()

if(BUILD_STATIC_LIBS)
    install(TARGETS dateconv_static
        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
            PERMISSIONS ${DATECONV_MODE_644}
    )
endif()

install(FILES date_converter.h
    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
    PERMISSIONS ${DATECONV_MODE_644}
)

set(prefix "${CMAKE_INSTALL_PREFIX}")
set(exec_prefix "\${prefix}")
if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}")
    set(libdir "${CMAKE_INSTALL_LIBDIR}")
else()
    set(libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}")
endif()
if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}")
    set(includedir "${CMAKE_INSTALL_INCLUDEDIR}")
else()
    set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
endif()
set(PACKAGE_VERSION "${PROJECT_VERSION}")
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/libdateconv.pc.in" DATECONV_PC_CONTENT)
if(WIN32 AND MSVC AND BUILD_STATIC_LIBS AND NOT BUILD_SHARED_LIBS)
    string(REPLACE "-ldateconv" "-ldateconv_static"
        DATECONV_PC_CONTENT "${DATECONV_PC_CONTENT}"
    )
endif()
string(CONFIGURE "${DATECONV_PC_CONTENT}" DATECONV_PC_CONTENT @ONLY)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/libdateconv.pc" "${DATECONV_PC_CONTENT}")
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libdateconv.pc"
    DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
    PERMISSIONS ${DATECONV_MODE_644}
)

if(BUILD_TESTING)
    add_executable(test EXCLUDE_FROM_ALL tests/test.c)
    target_link_libraries(test PRIVATE ${DATECONV_LINK_TARGET})

    if(BUILD_SHARED_LIBS AND APPLE)
        target_link_options(test PRIVATE
            "-Wl,-rpath,@loader_path"
            "-Wl,-headerpad_max_install_names"
        )
        add_custom_command(TARGET test POST_BUILD
            COMMAND "${CMAKE_INSTALL_NAME_TOOL}"
                -change
                "$<TARGET_SONAME_FILE_NAME:dateconv_shared>"
                "@rpath/$<TARGET_SONAME_FILE_NAME:dateconv_shared>"
                "$<TARGET_FILE:test>"
            VERBATIM
        )
    elseif(BUILD_SHARED_LIBS AND UNIX AND NOT DATECONV_WINDOWS_ABI)
        set_target_properties(test PROPERTIES
            SKIP_BUILD_RPATH FALSE
            BUILD_RPATH "$ORIGIN"
            BUILD_RPATH_USE_ORIGIN TRUE
        )
    endif()

    dateconv_set_build_mode(test 755)
    add_custom_target(check
        COMMAND $<TARGET_FILE:test>
        DEPENDS test
        WORKING_DIRECTORY $<TARGET_FILE_DIR:test>
        USES_TERMINAL
    )
endif()
