cmake_minimum_required(VERSION 3.13)
include(CMakePushCheckState)
include(CheckStructHasMember)
include(CheckSymbolExists)

project(shout_mixxx LANGUAGES C VERSION 2.4.1)

add_library(${PROJECT_NAME} STATIC
    src/codec_opus.c
    src/codec_vorbis.c
    src/common/avl/avl.c
    src/common/net/sock.c
    src/common/net/resolver.c
    src/common/timing/timing.c
    src/common/httpp/httpp.c
    src/common/httpp/encoding.c
    src/common/thread/thread.c
    src/format_mpeg.c
    src/format_ogg.c
    src/format_webm.c
    src/proto_http.c
    src/proto_icy.c
    src/proto_roaraudio.c
    src/proto_xaudiocast.c
    src/queue.c
    src/shout.c
    src/tls.c
    src/util.c
)
target_include_directories(${PROJECT_NAME} PRIVATE include src src/common)

target_compile_definitions(${PROJECT_NAME}
    PRIVATE
        VERSION="${PROJECT_VERSION}"
        LIBSHOUT_MAJOR=${PROJECT_VERSION_MAJOR}
        LIBSHOUT_MINOR=${PROJECT_VERSION_MINOR}
        LIBSHOUT_MICRO=${PROJECT_VERSION_PATCH}
)

option(WARNINGS_PEDANTIC "Let the compiler show even more warnings" OFF)
if(MSVC)
  if(WARNINGS_PEDANTIC)
    target_compile_options(${PROJECT_NAME} PRIVATE /W4)
  else()
    target_compile_options(${PROJECT_NAME} PRIVATE /W3)
    target_compile_definitions(${PROJECT_NAME} PRIVATE _SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING _CRT_SECURE_NO_WARNINGS _WINSOCK_DEPRECATED_NO_WARNINGS)
  endif()
else()
  target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra $<$<COMPILE_LANGUAGE:CXX>:-Woverloaded-virtual> -Wfloat-conversion -Werror=return-type -Wno-unused-parameter)
  if(WARNINGS_PEDANTIC)
    target_compile_options(${PROJECT_NAME} PRIVATE -pedantic)
  endif()
endif()

cmake_push_check_state()

# Ensure we have pthreads
if(WIN32)
    # vcpkg provides CMake targets for pthreads4w
    # This won't work if pthreads4w was built without vcpkg.
    find_package(pthreads REQUIRED)
    target_link_libraries(${PROJECT_NAME} PRIVATE PThreads4W::PThreads4W)

    target_compile_definitions(${PROJECT_NAME} PRIVATE
        __CLEANUP_C _TIMESPEC_DEFINED _USRDLL _MBCS _LIB _WIN32)
    if(STATIC_DEPS)
        target_compile_definitions(${PROJECT_NAME} PRIVATE PTW32_STATIC_LIB)
    endif()
else()
    set(THREADS_PREFER_PTHREAD_FLAG ON)
    find_package(Threads REQUIRED)
    if (NOT CMAKE_USE_PTHREADS_INIT)
        message (FATAL_ERROR "Building ${PROJECT_NAME} requires the pthread library and its development heraders")
    endif ()
    target_link_libraries(${PROJECT_NAME} PRIVATE Threads::Threads)
endif()

# Configure shout.h
set(SHOUT_THREADSAFE 1)
set(SHOUT_TLS 1)
configure_file(include/shoutidjc/shout.h.in "${CMAKE_CURRENT_BINARY_DIR}/include/shoutidjc/shout.h" @ONLY)

# Required system dependencies
# Ogg Vorbis
find_package(Ogg REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE Ogg::ogg)

find_package(Vorbis REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE Vorbis::vorbis Vorbis::vorbisenc)

# OpenSSL
find_package(OpenSSL REQUIRED)
target_compile_definitions(${PROJECT_NAME} PRIVATE HAVE_OPENSSL)
target_link_libraries(${PROJECT_NAME} PRIVATE OpenSSL::SSL OpenSSL::Crypto)

option(SPEEX "Speex support" OFF)
if(SPEEX)
    target_sources(${PROJECT_NAME} src/codec_speex.c)
    # TODO: Add support for finding speex headers and linking
endif()

option(THEORA "Theora support" OFF)
if(THEORA)
    target_sources(${PROJECT_NAME} src/codec_theora.c)
    # TODO: Add support for finding theora headers and linking
    #find_package(unofficial-theora)
    #if(TARGET unofficial::theora::theora)
    #    target_compile_definitions(${PROJECT_NAME} PRIVATE HAVE_THEORA)
    #    target_link_libraries(${PROJECT_NAME} PRIVATE unofficial::theora::theora unofficial::theora::theoradec unofficial::theora::theoraenc)
    #endif()
endif()

check_symbol_exists(__GNU_LIBRARY__ "features.h" _GNU_SOURCE)
if (NOT _GNU_SOURCE)
    unset(_GNU_SOURCE CACHE)
    check_symbol_exists(_GNU_SOURCE "features.h" _GNU_SOURCE)
endif ()
if (_GNU_SOURCE)
    list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE)
    target_compile_definitions(${PROJECT_NAME} PRIVATE _GNU_SOURCE)
endif ()

if(UNIX AND NOT APPLE)
    list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_XOPEN_SOURCE=600)
    target_compile_definitions(${PROJECT_NAME} PRIVATE _XOPEN_SOURCE=600)
endif()

check_include_file(arpa/inet.h HAVE_ARPA_INET_H)
if(HAVE_ARPA_INET_H)
    target_compile_definitions(${PROJECT_NAME} PRIVATE HAVE_ARPA_INET_H)
endif()

check_include_file(inttypes.h HAVE_INTTYPES_H)
if(HAVE_INTTYPES_H)
    target_compile_definitions(${PROJECT_NAME} PRIVATE HAVE_INTTYPES_H)
endif()

check_include_file(stdint.h HAVE_STDINT_H)
if(HAVE_STDINT_H)
    target_compile_definitions(${PROJECT_NAME} PRIVATE HAVE_STDINT_H)
endif()

check_include_file(sys/select.h HAVE_SYS_SELECT_H)
if(HAVE_SYS_SELECT_H)
    target_compile_definitions(${PROJECT_NAME} PRIVATE HAVE_SYS_SELECT_H)
endif()

check_include_file(sys/socket.h HAVE_SYS_SOCKET_H)
if(HAVE_SYS_SOCKET_H)
    target_compile_definitions(${PROJECT_NAME} PRIVATE HAVE_SYS_SOCKET_H)
endif()

check_include_file(winsock2.h HAVE_WINSOCK2_H)
if(HAVE_WINSOCK2_H)
    target_compile_definitions(${PROJECT_NAME} PRIVATE HAVE_WINSOCK2_H)
endif()

check_include_file(sys/uio.h HAVE_SYS_UIO_H)
if(HAVE_SYS_UIO_H)
    target_compile_definitions(${PROJECT_NAME} PRIVATE HAVE_SYS_UIO_H)
     
    check_symbol_exists(writev "sys/uio.h" HAVE_WRITEV)
    if(HAVE_WRITEV)
        target_compile_definitions(${PROJECT_NAME} PRIVATE HAVE_WRITEV)
    endif()
endif()

check_include_file(sys/time.h HAVE_SYS_TIME_H)
if(HAVE_SYS_TIME_H)
    target_compile_definitions(${PROJECT_NAME} PRIVATE HAVE_SYS_TIME_H)

    check_symbol_exists(gettimeofday "sys/time.h" HAVE_GETTIMEOFDAY)
    if(HAVE_GETTIMEOFDAY)
        target_compile_definitions(${PROJECT_NAME} PRIVATE HAVE_GETTIMEOFDAY)
    endif()
endif()

check_include_file(sys/timeb.h HAVE_SYS_TIMEB_H)
if(HAVE_SYS_TIMEB_H)
    target_compile_definitions(${PROJECT_NAME} PRIVATE HAVE_SYS_TIMEB_H)

    check_symbol_exists(ftime "sys/timeb.h" HAVE_FTIME)
    if(HAVE_FTIME)
        target_compile_definitions(${PROJECT_NAME} PRIVATE HAVE_FTIME)
    endif()
endif()

check_include_file(time.h HAVE_TIME_H)
if(HAVE_TIME_H)
    target_compile_definitions(${PROJECT_NAME} PRIVATE HAVE_TIME_H)
endif()

if(HAVE_SYS_TIME_H AND HAVE_TIME_H)
    target_compile_definitions(${PROJECT_NAME} PRIVATE TIME_WITH_SYS_TIME)
endif()

check_symbol_exists(pthread_spin_init "sys/time.h" HAVE_PTHREAD_SPIN_LOCK)
if(HAVE_PTHREAD_SPIN_LOCK)
    add_compile_definitions(HAVE_PTHREAD_SPIN_LOCK)
endif()

check_include_file(unistd.h HAVE_UNISTD_H)
if(HAVE_UNISTD_H)
    target_compile_definitions(${PROJECT_NAME} PRIVATE HAVE_UNISTD_H)
endif()

check_include_file(poll "poll.h" HAVE_POLL)
if(HAVE_POLL)
    target_compile_definitions(${PROJECT_NAME} PRIVATE HAVE_POLL)
endif()

check_include_file(winsock2.h HAVE_WINSOCK2_H)
if(HAVE_WINSOCK2_H)
    target_compile_definitions(${PROJECT_NAME} PRIVATE HAVE_WINSOCK2_H)

    check_symbol_exists(endhostent "winsock2.h;ws2tcpip.h" HAVE_ENDHOSTENT)
    check_symbol_exists(getaddrinfo "winsock2.h;ws2tcpip.h" HAVE_GETADDRINFO)
    check_symbol_exists(getnameinfo "winsock2.h;ws2tcpip.h" HAVE_GETNAMEINFO)
    check_symbol_exists(inet_aton "winsock2.h;ws2tcpip.h" HAVE_INET_ATON)
    check_symbol_exists(inet_pton "winsock2.h;ws2tcpip.h" HAVE_INET_PTON)
    check_symbol_exists(sethostent "winsock2.h;ws2tcpip.h" HAVE_SETHOSTENT)
else()
    check_symbol_exists(endhostent "netdb.h" HAVE_ENDHOSTENT)
    check_symbol_exists(getaddrinfo "sys/types.h;sys/socket.h;netdb.h" HAVE_GETADDRINFO)
    check_symbol_exists(getnameinfo "sys/socket.h;netdb.h" HAVE_GETNAMEINFO)
    check_symbol_exists(inet_aton "sys/socket.h;netinet/in.h;arpa/inet.h" HAVE_INET_ATON)
    check_symbol_exists(inet_pton "arpa/inet.h" HAVE_INET_PTON)
    check_symbol_exists(sethostent "netdb.h" HAVE_SETHOSTENT)
endif()

if(HAVE_GETADDRINFO)
    target_compile_definitions(${PROJECT_NAME} PRIVATE HAVE_GETADDRINFO)
endif()
if(HAVE_GETNAMEINFO)
    target_compile_definitions(${PROJECT_NAME} PRIVATE HAVE_GETNAMEINFO)
endif()
if(HAVE_INET_ATON)
    target_compile_definitions(${PROJECT_NAME} PRIVATE HAVE_INET_ATON)
endif()
if(HAVE_INET_PTON)
    target_compile_definitions(${PROJECT_NAME} PRIVATE HAVE_INET_PTON)
endif()
if(HAVE_SETHOSTENT)
    target_compile_definitions(${PROJECT_NAME} PRIVATE HAVE_SETHOSTENT)
endif()

check_symbol_exists(strcasestr "string.h" HAVE_STRCASESTR)
if(HAVE_STRCASESTR)
    target_compile_definitions(${PROJECT_NAME} PRIVATE HAVE_STRCASESTR)
endif()

check_symbol_exists(X509_check_host "openssl/x509v3.h" HAVE_X509_CHECK_HOST)
if(HAVE_X509_CHECK_HOST)
    target_compile_definitions(${PROJECT_NAME} PRIVATE XXX_HAVE_X509_check_host)
endif()

cmake_pop_check_state()
