cmake_minimum_required(VERSION 3.17)

cmake_policy(SET CMP0005 NEW)

# Add path for additional FindXXX.cmake scenarios
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})

project(SEMS)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Read the version from the VERSION file
execute_process(
  COMMAND cat "${CMAKE_SOURCE_DIR}/VERSION"
  OUTPUT_VARIABLE FILE_VERSION_RAW
  OUTPUT_STRIP_TRAILING_WHITESPACE)

# If the file is missing or empty, fail with an error
if(NOT FILE_VERSION_RAW)
  message(FATAL_ERROR "VERSION file is missing or empty! Please create it.")
endif()

# Clean up version string: replace dashes with dots and remove 'v' prefix
string(REGEX REPLACE "^v" "" FILE_VERSION "${FILE_VERSION_RAW}")
string(REGEX REPLACE "-" "." FILE_VERSION "${FILE_VERSION}")

# Print version for debugging
message(STATUS "SEMS_VERSION: ${FILE_VERSION}")

# Pass version as a compile definition
add_definitions(-DSEMS_VERSION="${FILE_VERSION}")
add_definitions(-DARCH="${CMAKE_SYSTEM_PROCESSOR}")
add_definitions(-DOS="${CMAKE_SYSTEM_NAME}")

add_definitions(-DSYSTEM_SAMPLECLOCK_RATE=32000LL)
add_definitions(-DSEMS_APP_NAME="sems")

message(STATUS "Configuring ${CMAKE_PROJECT_NAME} v. ${FILE_VERSION}")

if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR AND NOT MSVC_IDE)
  message(
    FATAL_ERROR
      "In-source builds are not allowed.
CMake would overwrite all makefiles distributed with SEMS.
Please create a directory and run cmake from there, passing the path
to this source directory as the last argument.
This process created the file `CMakeCache.txt' and the directory `CMakeFiles'.
Please delete them.")
endif(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR AND NOT MSVC_IDE)

option(SEMS_USE_OPUS "Build with Opus" OFF)
option(SEMS_USE_SPANDSP "Build with spandsp" OFF)
option(SEMS_USE_LIBSAMPLERATE "Build with libsamplerate" OFF)
option(SEMS_USE_ZRTP "Build with ZRTP" OFF)
option(SEMS_USE_MP3 "Build with MP3" OFF)
option(SEMS_USE_ILBC "Build with iLBC library (fallback to bundled)" ON)
option(SEMS_USE_G729 "Build with bcg729 library" OFF)
option(SEMS_USE_CODEC2 "Build with codec2 library" OFF)
option(SEMS_USE_TTS "Build with Text-to-speech support (requires Flite)" OFF)
option(SEMS_USE_OPENSSL "Build with OpenSSL" OFF)
option(SEMS_USE_MONITORING "Build with monitoring support" ON)
option(SEMS_USE_IPV6 "Build with IPv6 support" ON)
option(SEMS_USE_PYTHON "Build with Python modules" ON)
option(SEMS_USE_ASAN "Build with AddressSanitizer (memory error detector)" OFF)

# AddressSanitizer configuration
if(SEMS_USE_ASAN)
  message(STATUS "AddressSanitizer enabled")
  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address")
  set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fsanitize=address")
endif()

# add -lm and -lpthread / -lthr to all targets

set(CMAKE_DL_LIBS ${CMAKE_DL_LIBS} m)
if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
  set(CMAKE_DL_LIBS ${CMAKE_DL_LIBS} thr)
else(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
  set(CMAKE_DL_LIBS ${CMAKE_DL_LIBS} pthread)
endif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")

if(NOT SEMS_CFG_PREFIX)
  set(SEMS_CFG_PREFIX "")
endif(NOT SEMS_CFG_PREFIX)
message(STATUS "SEMS cfg prefix was set to: ${SEMS_CFG_PREFIX}")

if(NOT SEMS_AUDIO_PREFIX)
  set(SEMS_AUDIO_PREFIX "/usr/local/lib")
endif(NOT SEMS_AUDIO_PREFIX)
message(STATUS "SEMS audio prefix was set to: ${SEMS_AUDIO_PREFIX}")

if(NOT SEMS_EXEC_PREFIX)
  set(SEMS_EXEC_PREFIX "/usr/local")
endif(NOT SEMS_EXEC_PREFIX)
message(STATUS "SEMS exec prefix was set to: ${SEMS_EXEC_PREFIX}")

if(NOT SEMS_RUNDIR_PREFIX)
  set(SEMS_RUNDIR_PREFIX "/var/local/run")
endif(NOT SEMS_RUNDIR_PREFIX)
message(STATUS "SEMS run dir prefix was set to: ${SEMS_RUNDIR_PREFIX}")

if(NOT SEMS_LIBDIR)
  set(SEMS_LIBDIR "lib")
endif(NOT SEMS_LIBDIR)
message(STATUS "SEMS libdir was set to: ${SEMS_LIBDIR}")

if(NOT SEMS_DOC_PREFIX)
  set(SEMS_DOC_PREFIX "/usr/share/doc")
endif(NOT SEMS_DOC_PREFIX)
message(STATUS "SEMS doc prefix was set to: ${SEMS_DOC_PREFIX}")

set(CMAKE_VERBOSE_MAKEFILE ON)

find_package(Libevent2 REQUIRED)

# compile with spandsp DTMF detection? see soft-switch.org this needs a fairly
# new version of spandsp - tested with 0.0.4pre11 will not work with spandsp
# 0.0.2 . (which means that current debian and gentoo packages don't work)
if(SEMS_USE_SPANDSP)
  find_package(Spandsp REQUIRED)
  add_definitions(-DUSE_SPANDSP -D__STDC_LIMIT_MACROS)
  message(STATUS "Using spandsp: YES")
else(SEMS_USE_SPANDSP)
  message(STATUS "Using spandsp: NO (default)")
endif(SEMS_USE_SPANDSP)

# compile with Opus codec?
if(SEMS_USE_OPUS)
  find_package(Opus REQUIRED)
  message(STATUS "Using Opus: YES")
else(SEMS_USE_OPUS)
  message(STATUS "Using Opus: NO (default)")
endif(SEMS_USE_OPUS)

# compile with sample rate conversion from secret rabbit code? (see
# http://www.mega-nerd.com/SRC/)
if(SEMS_USE_LIBSAMPLERATE)
  find_package(Libsamplerate REQUIRED)
  add_definitions(-DUSE_LIBSAMPLERATE)
  message(STATUS "Using libsamplerate: YES")
else(SEMS_USE_LIBSAMPLERATE)
  message(STATUS "Using libsamplerate: NO (default)")
  add_definitions(-DUSE_INTERNAL_RESAMPLER)
endif(SEMS_USE_LIBSAMPLERATE)

# ZRTP support? (see zfoneproject.com)
if(SEMS_USE_ZRTP)
  find_package(Libzrtp REQUIRED)
  message(STATUS "Using libzrtp: YES")
  add_definitions(-DWITH_ZRTP -DBUILD_DEFAULT_CACHE -DBUILD_DEFAULT_TIMER
                  -DUNIX -DBUILD_ZRTP_MUTEXES)
else(SEMS_USE_ZRTP)
  message(STATUS "Using libzrtp: NO (default)")
endif(SEMS_USE_ZRTP)

# Lame MP3 support
if(SEMS_USE_MP3)
  find_package(Lame REQUIRED)
  if(LAME_FOUND)
    message(STATUS "Using mp3 encoding (LAME library): YES")
    find_package(Mpg123)
    if(MPG123_FOUND)
      add_definitions(-DWITH_MPG123DECODER)
      message(STATUS "Using mp3 decoding (mpg123 library): YES")
    else(MPG123_FOUND)
      message(STATUS "Using mp3 decoding (mpg123 library): NO")
    endif(MPG123_FOUND)
  endif(LAME_FOUND)
else(SEMS_USE_MP3)
  message(STATUS "Using mp3: NO (default)")
endif(SEMS_USE_MP3)

if(SEMS_USE_ILBC)
  find_package(Ilbc)
  if(ILBC_FOUND)
    message(STATUS "Using iLBC library: YES (external)")
  else(ILBC_FOUND)
    message(STATUS "Using iLBC library: YES (bundled, default)")
  endif(ILBC_FOUND)
else(SEMS_USE_ILBC)
  message(STATUS "Using iLBC library: NO")
endif(SEMS_USE_ILBC)

if(SEMS_USE_G729)
  find_package(Bcg729 REQUIRED)
  message(STATUS "Using bcg729 library: YES")
else(SEMS_USE_G729)
  message(STATUS "Using bcg729 library: NO (default)")
endif(SEMS_USE_G729)

if(SEMS_USE_CODEC2)
  find_package(Codec2 REQUIRED)
  message(STATUS "Using codec2 library: YES")
else(SEMS_USE_CODEC2)
  message(STATUS "Using codec2 library: NO (default)")
endif(SEMS_USE_CODEC2)

# build in support for monitoring?
if(SEMS_USE_MONITORING)
  message(STATUS "Enable monitoring: YES (default)")
  add_definitions(-DUSE_MONITORING)
else(SEMS_USE_MONITORING)
  message(STATUS "Enable monitoring: NO")
endif(SEMS_USE_MONITORING)

if(SEMS_USE_IPV6)
  add_definitions(-DSUPPORT_IPV6)
  message(STATUS "Enable IPv6 support: YES (default)")
else(SEMS_USE_IPV6)
  message(STATUS "Enable IPv6 support: NO")
endif(SEMS_USE_IPV6)

# Let's try to find GSM library and header files
find_package(Gsm)
if(GSM_FOUND)
  message(STATUS "Using gsm: YES")
else(GSM_FOUND)
  message(STATUS "Using gsm: NO")
endif(GSM_FOUND)

# Let's try to find SPEEX library and header files
find_package(Speex)
if(SPEEX_FOUND)
  message(STATUS "Using speex: YES")
else(SPEEX_FOUND)
  message(STATUS "Using speex: NO")
endif(SPEEX_FOUND)

if(SEMS_USE_OPENSSL)
  find_package(OpenSSL REQUIRED QUIET)
  message(STATUS "Found OpenSSL includes:	${OPENSSL_INCLUDE_DIR}/openssl/ssl.h")
  message(STATUS "Found OpenSSL library: ${OPENSSL_LIBRARIES}")
  message(STATUS "Using OpenSSL: YES")
else(SEMS_USE_OPENSSL)
  message(STATUS "Using OpenSSL: NO (default)")
endif(SEMS_USE_OPENSSL)

# Let's try to find Python development files
if(SEMS_USE_PYTHON)
  find_package(Python3 COMPONENTS Interpreter Development)
  if(Python3_FOUND)
    message(STATUS "Found Python includes: ${Python3_INCLUDE_DIRS}")
    message(STATUS "Found Python library: ${Python3_LIBRARIES}")
    message(STATUS "Found Python version: ${Python3_VERSION}")
    message(STATUS "Using Python: YES")
  else(Python3_FOUND)
    message(STATUS "Using Python: NO")
  endif(Python3_FOUND)

  # Let's try to find SIP (Python bindings generator) development files
  find_package(SIP)
  if(SIP_FOUND)
    message(STATUS "Using SIP: YES")
  else(SIP_FOUND)
    message(STATUS "Using SIP: NO")
  endif(SIP_FOUND)
else(SEMS_USE_PYTHON)
  message(STATUS "Using Python modules: NO")
endif(SEMS_USE_PYTHON)

# Let's try to find MySQL++ development files
find_package(MySQLpp)
if(MYSQLPP_FOUND)
  message(STATUS "Using MySQL++: YES")
else(MYSQLPP_FOUND)
  message(STATUS "Using MySQL++: NO")
endif(MYSQLPP_FOUND)

# Let's try to find MySQL-connector-C++ development files
find_package(MySQLcppconn)
if(MYSQLCPPCONN_FOUND)
  message(STATUS "Using MySQL-connector-C++: YES")
else(MYSQLCPPCONN_FOUND)
  message(STATUS "Using MySQL-connector-C++: NO")
endif(MYSQLCPPCONN_FOUND)

# Let's try to find Flite library and header files
if(SEMS_USE_TTS)
  find_package(Flite REQUIRED)
  message(STATUS "Using text-to-speech: YES")
else(SEMS_USE_TTS)
  set(FLITE_FOUND FALSE)
  message(STATUS "Using text-to-speech: NO (default)")
endif(SEMS_USE_TTS)

# Let's try to find mISDN library and header files
find_package(MISDN)
if(MISDN_FOUND)
  message(STATUS "Using mISDN: YES")
else(MISDN_FOUND)
  message(STATUS "Using mISDN: NO")
endif(MISDN_FOUND)

# Let's try to find libev development files
find_package(Libev)
if(LIBEV_FOUND)
  message(STATUS "Using libev: YES")
else(LIBEV_FOUND)
  message(STATUS "Using libev: NO")
endif(LIBEV_FOUND)

# Let's try to find librtmp development files
find_package(Librtmp)
if(RTMP_FOUND)
  message(STATUS "Using librtmp: YES")
else(RTMP_FOUND)
  message(STATUS "Using librtmp: NO")
endif(RTMP_FOUND)

# Redis support
find_package(Hiredis)
if(HIREDIS_FOUND)
  message(STATUS "Using hiredis: YES")
else(HIREDIS_FOUND)
  message(STATUS "Using hiredis: NO")
endif(HIREDIS_FOUND)

if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL FreeBSD)
  # Check for res_search in -lresolv
  include(CheckSymbolExists)
  set(CMAKE_REQUIRED_LIBRARIES resolv)
  check_symbol_exists(res_search "netinet/in.h;resolv.h" HAVE_RES_SEARCH)
  message(STATUS "Have res_search: ${HAVE_RES_SEARCH}")
endif(NOT ${CMAKE_SYSTEM_NAME} STREQUAL FreeBSD)

include_directories(${SEMS_SOURCE_DIR}/core/ampi)
include_directories(${SEMS_SOURCE_DIR}/core/amci)
include_directories(${SEMS_SOURCE_DIR}/core)

if(${CMAKE_SYSTEM_NAME} STREQUAL FreeBSD)
  include_directories(/usr/local/include)
  link_directories(/usr/local/lib)
endif(${CMAKE_SYSTEM_NAME} STREQUAL FreeBSD)

if(${CMAKE_SYSTEM_NAME} STREQUAL SunOS)
  set(CMAKE_C_FLAGS
      "${CMAKE_C_FLAGS} -DHAVE_SYS_SOCKIO_H -DBSD_COMP -fPIC -include compat/solaris.h"
  )
  set(CMAKE_CXX_FLAGS
      "${CMAKE_CXX_FLAGS} -DHAVE_SYS_SOCKIO_H -DBSD_COMP -fPIC -include compat/solaris.h"
  )
  # For nanosleep.
  set(CMAKE_DL_LIBS ${CMAKE_DL_LIBS} rt)
  # For inet_aton.
  set(CMAKE_DL_LIBS ${CMAKE_DL_LIBS} resolv)
  set(CMAKE_DL_LIBS ${CMAKE_DL_LIBS} dl socket nsl)
endif(${CMAKE_SYSTEM_NAME} STREQUAL SunOS)

if(${CMAKE_SYSTEM_NAME} STREQUAL Darwin)
  if(EXISTS /opt/local/include)
    include_directories(/opt/local/include)
  endif()
  if(EXISTS /opt/local/lib)
    link_directories(/opt/local/lib)
  endif()
  set(CMAKE_C_FLAGS
      "${CMAKE_C_FLAGS} -fno-common -DBSD44SOCKETS -D__DARWIN_UNIX03")
  set(CMAKE_CXX_FLAGS
      "${CMAKE_CXX_FLAGS} -fno-common -DBSD44SOCKETS -D__DARWIN_UNIX03")
  set(CMAKE_SHARED_MODULE_CREATE_CXX_FLAGS
      "${CMAKE_SHARED_MODULE_CREATE_CXX_FLAGS} -undefined dynamic_lookup -bundle"
  )
  set(CMAKE_SHARED_MODULE_CREATE_C_FLAGS
      "${CMAKE_SHARED_MODULE_CREATE_C_FLAGS} -undefined dynamic_lookup -bundle"
  )
endif(${CMAKE_SYSTEM_NAME} STREQUAL Darwin)

if(${CMAKE_SYSTEM_NAME} STREQUAL iOS)
  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-common -DBSD44SOCKETS")
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-common -DBSD44SOCKETS")
  set(CMAKE_SHARED_MODULE_CREATE_CXX_FLAGS
      "${CMAKE_SHARED_MODULE_CREATE_CXX_FLAGS} -undefined dynamic_lookup -bundle"
  )
  set(CMAKE_SHARED_MODULE_CREATE_C_FLAGS
      "${CMAKE_SHARED_MODULE_CREATE_C_FLAGS} -undefined dynamic_lookup -bundle"
  )
  set(CMAKE_DL_LIBS ${CMAKE_DL_LIBS} gcc_eh)
endif(${CMAKE_SYSTEM_NAME} STREQUAL iOS)

if(CMAKE_COMPILER_IS_GNUCC)
  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_THREAD_SAFE -D_REENTRANT -Wall -O2")
endif(CMAKE_COMPILER_IS_GNUCC)

if(CMAKE_COMPILER_IS_GNUCXX)
  set(CMAKE_CXX_FLAGS
      "${CMAKE_CXX_FLAGS} -D_THREAD_SAFE -D_REENTRANT -Wall -std=c++11 -O2")
endif(CMAKE_COMPILER_IS_GNUCXX)

if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
  set(CMAKE_CXX_FLAGS
      "${CMAKE_CXX_FLAGS} -D_THREAD_SAFE -D_REENTRANT -Wall -std=c++11 -O2")
endif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")

if("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_THREAD_SAFE -D_REENTRANT -Wall -O2")
endif("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")

set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_DEBUG")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}   -D_DEBUG")

# compile with session thread pool support? use this for very high concurrent
# call count applications (e.g. for signaling only) if compiled with thread
# pool, there will be a thread pool of configurable size processing the
# signaling and application logic of the calls. if compiled without thread pool
# support, every session will have its own thread.
#
# ADD_DEFINITIONS(-DSESSION_THREADPOOL)

# ADD_DEFINITIONS(-DNO_THREADID_LOG) ADD_DEFINITIONS(-DLOG_LOC_DATA_ATEND)

# Support for long debug messages? (useful for debugging SIP messages' contents)
#
# disable for slight performance gain
add_definitions(-DLOG_BUFFER_LEN=2048)

# ADD_DEFINITIONS(-DDEBUG_PLAYOUTBUF)

# Propagate all unparsed SIP headers into AmSipReply. Disabled by default
# because only special applications might need this and it brings higher memory
# demands and affects performance.
# ADD_DEFINITIONS(-DPROPAGATE_UNPARSED_REPLY_HEADERS)

# Disable the ability to run sems as daemon
# ADD_DEFINITIONS(-DDISABLE_DAEMON_MODE)

if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
  add_definitions(-DPROPAGATE_COREDUMP_SETTINGS)
endif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")

set(CMAKE_SHARED_LIBRARY_PREFIX "")

enable_testing()

add_subdirectory(apps)
add_subdirectory(core)
add_subdirectory(tools)

# Documentation targets (doc, fulldoc)
include(cmake/doxygen.cmake)

# CPack configuration for source tarballs (rpmtar, package_source)
include(cmake/cpack-source.cmake)
