include(CheckSymbolExists)
include(CheckIncludeFile)
include(CheckTypeSize)
include(${CMAKE_SOURCE_DIR}/cmake/CheckSyntaxAttribute.cmake)

macro(define_if_include_found)
    set(FILE ${ARGV0})
    string(MAKE_C_IDENTIFIER "${ARGV0}" VAR)
    string(TOUPPER "HAVE_${VAR}" DEFINITION)
    string(TOUPPER "HAVE_${VAR}" VAR)
    check_include_file("${FILE}" "${VAR}")
    if ("${${VAR}}")
        add_definitions(-D"${DEFINITION}")
    endif ()
    unset(VAR)
endmacro()

macro(define_if_type_found)
    set(TYPE "${ARGV0}")
    string(TOUPPER "HAVE_${TYPE}" VAR)
    check_type_size("${TYPE}" "${VAR}")
    if ("${${VAR}}")
        add_definitions(-D${VAR})
    endif ()
endmacro()

macro(define_if_symbol_found)
    set(SYMBOL "${ARGV0}")
    string(MAKE_C_IDENTIFIER "${ARGV0}" VAR)
    string(TOUPPER "HAVE_${VAR}" VAR)
    check_symbol_exists("${ARGV0}" "${ARGV1}" "${VAR}")
    if ("${${VAR}}")
        add_definitions(-D"${VAR}")
    endif ()
    unset(VAR)
endmacro()

macro(define_if_qualifier_found)
    set(SYMBOL "${ARGV0}")
    cmake_parse_arguments(QUALIFIER
            "FUNCTION;VARIABLE;STRUCT" "OUTPUT" "" ${ARGN})
    if (QUALIFIER_OUTPUT)
        set(VAR "${QUALIFIER_OUTPUT}")
    else ()
        string(MAKE_C_IDENTIFIER "${SYMBOL}" VAR)
        string(TOUPPER "HAVE_QUALIFIER_${VAR}" VAR)
    endif ()
    if (QUALIFIER_FUNCTION)
        set(SOURCE_STRING "
#include <stdlib.h>
${SYMBOL} void func() { exit(1); }
int main() { return 0; }
")
    elseif (QUALIFIER_VARIABLE)
        set(SOURCE_STRING "
#include <stdlib.h>
${SYMBOL} static int var;
int main() { return 0; }
")
    elseif (QUALIFIER_STRUCT)
        set(SOURCE_STRING "
#include <stdlib.h>
${SYMBOL} static struct var_struct { int a; int b; int c; } var;
int main() { return 0; }
")
    else ()
        message(FATAL_ERROR "A qualifier type is required!!")
    endif ()
    check_c_source_compiles(SOURCE_FILE "${VAR}")
    unset(QUALIFIER_STRUCT)
    unset(QUALIFIER_VARIABLE)
    unset(QUALIFIER_FUNCTION)
    unset(QUALIFIER_OUTPUT)
    unset(VAR)
    unset(SOURCE_STRING)
endmacro()

macro(define_if_attribute_found)
    set(SYMBOL "${ARGV0}")
    cmake_parse_arguments(ATTRIBUTE
            "VERBATIM;FUNCTION;VARIABLE;STRUCT" "OUTPUT" "" ${ARGN})
    if (ATTRIBUTE_OUTPUT)
        set(VAR "${ATTRIBUTE_OUTPUT}")
    else ()
        string(MAKE_C_IDENTIFIER "${SYMBOL}" VAR)
        string(TOUPPER "HAVE_ATTRIBUTE_${VAR}" VAR)
    endif ()

    if (NOT ATTRIBUTE_STYLE)
        check_attribute_syntax()
    endif()

    if (ATTRIBUTE_STYLE STREQUAL "GCC")
        set(ATTR "__attribute__((${SYMBOL}))")
    elseif (ATTRIBUTE_STYLE STREQUAL "MSVC")
        set(ATTR "__declspec(${SYMBOL})")
    else ()
        # should fail unless we end up with a supporting C23 compiler
        set(ATTR "[[${SYMBOL}]]")
    endif ()
    if (ATTRIBUTE_FUNCTION)
        set(SOURCE_STRING "
#include <stdlib.h>
${ATTR} void func() { exit(1); }
int main() { return 0; }
")
    elseif (ATTRIBUTE_VARIABLE)
        set(SOURCE_STRING "
#include <stdlib.h>
${ATTR} static int var;
int main() { return 0; }
")
    elseif (ATTRIBUTE_STRUCT)
        set(SOURCE_STRING "
#include <stdlib.h>
${ATTR} static struct var_struct { int a; int b; int c; } var;
int main() { return 0; }
")
    else ()
        message(FATAL_ERROR "An attribute type is required!!")
    endif ()

    check_c_source_compiles("${SOURCE_STRING}" "${VAR}")
    if ("${${VAR}}")
        add_definitions(-D"${VAR}")
        if (NOT ATTRIBUTE_STYLE)
            if (ATTRIBUTE_GCC)
                add_definitions(-DGCC_STYLE_ATTRIBUTES)
                set(ATTRIBUTE_STYLE "GCC")
            elseif (ATTRIBUTE_MSVC)
                add_definitions(-DGCC_STYLE_ATTRIBUTES)
                set(ATTRIBUTE_STYLE "MSVC")
            endif ()
        endif ()
    endif ()
    unset(VAR)
    unset(ATTRIBUTE_STRUCT)
    unset(ATTRIBUTE_VARIABLE)
    unset(ATTRIBUTE_FUNCTION)
    unset(ATTRIBUTE_OUTPUT)
    unset(VAR)
    unset(SOURCE_STRING)
endmacro()


