cmake_minimum_required(VERSION 3.10)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
project(AXPBox VERSION 1.2.0)

# x86-64 JIT (requires the asmjit sources in third_party/asmjit; see README)
option(ES40_DISABLE_ASMJIT "Disable the x86-64 JIT (asmjit)" ON)

# Source files
file(GLOB srcs src/*.cpp src/base/*.cpp src/gui/*.cpp)
file(GLOB include_sources src/base/*WIN32.cpp src/base/*POSIX.cpp)
list(REMOVE_ITEM srcs ${include_sources})
# Cirrus is not ported to the MAME-derived VGA core (disabled upstream too)
list(REMOVE_ITEM srcs ${CMAKE_SOURCE_DIR}/src/Cirrus.cpp)

add_executable(axpbox ${srcs} src/Main.cpp)
target_include_directories(axpbox PRIVATE src src/base src/gui ${CMAKE_BINARY_DIR}/src)

if (NOT ES40_DISABLE_ASMJIT)
    if (NOT EXISTS ${CMAKE_SOURCE_DIR}/third_party/asmjit/CMakeLists.txt)
        message(FATAL_ERROR "asmjit missing: clone it into third_party/asmjit\n"
            "  git clone https://github.com/asmjit/asmjit third_party/asmjit\n"
            "  git -C third_party/asmjit checkout 0bd5787b54b575ed94bf32ac452153b34385c514")
    endif()
    set(ASMJIT_NO_INSTALL ON)
    add_subdirectory(third_party/asmjit)
    target_sources(axpbox PRIVATE src/jit/jitengine.cpp)
    target_compile_definitions(axpbox PRIVATE ES40_JIT)
    target_include_directories(axpbox PRIVATE ${CMAKE_SOURCE_DIR}/third_party/asmjit/src)
    target_link_libraries(axpbox asmjit)
    message(STATUS "asmjit JIT enabled")
endif()

# Path to additional CMake modules
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)

if (CMAKE_GENERATOR MATCHES "Visual Studio")
  add_definitions(-DNOMINMAX -D_WIN32_WINNT=0x0A00 -DLANG_CXX11 -DCOMPILER_MSVC -D__VERSION__=\"MSVC\")
  add_definitions(-DWIN32 -DOS_WIN -D_MBCS -DWIN64 -DWIN32_LEAN_AND_MEAN -DPLATFORM_WINDOWS)
  add_definitions(/bigobj /nologo /EHsc /GF /FC /MP /Gm-)
  # Suppress warnings to reduce build log size.
  add_definitions(/wd4267 /wd4244 /wd4800 /wd4503 /wd4554 /wd4996 /wd4348 /wd4018)
  add_definitions(/wd4099 /wd4146 /wd4267 /wd4305 /wd4307)
  add_definitions(/wd4715 /wd4722 /wd4723 /wd4838 /wd4309 /wd4334)
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
endif()

find_package(Threads REQUIRED)
target_link_libraries(axpbox Threads::Threads)

# Configuration options
include(CheckSymbolExists)
include(CheckIncludeFile)
include(CheckLibraryExists)
include(CheckTypeSize)
include(CheckIncludeFiles)
include(TestBigEndian)
include(CheckLargeFiles)

TEST_BIG_ENDIAN(IS_BIG_ENDIAN)
if(IS_BIG_ENDIAN)
    set(ES40_BIG_ENDIAN 1)
else()
    set(ES40_LITTLE_ENDIAN 1)
endif()

check_symbol_exists(alarm "unistd.h" HAVE_ALARM)
check_include_file("arpa/inet.h" HAVE_ARPA_INET_H)
check_include_file("arpa/telnet.h" HAVE_ARPA_TELNET_H)
check_symbol_exists(atexit "stdlib.h" HAVE_ATEXIT)
check_include_file("ctype.h" HAVE_CTYPE_H)
check_include_file("errno.h" HAVE_ERRNO_H)
check_include_file("fcntl.h" HAVE_FCNTL_H)
check_symbol_exists(fopen "stdio.h" HAVE_FOPEN)
check_symbol_exists(fopen64 "stdio.h" HAVE_FOPEN64)
check_symbol_exists(fork "unistd.h" HAVE_FORK)
check_symbol_exists(fseek "stdio.h" HAVE_FSEEK)
check_symbol_exists(fseeko "stdio.h" HAVE_FSEEKO)
check_symbol_exists(fseeko64 "stdio.h" HAVE_FSEEKO64)
check_symbol_exists(ftell "stdio.h" HAVE_FTELL)
check_symbol_exists(ftello "stdio.h" HAVE_FTELLO)
check_symbol_exists(ftello64 "stdio.h" HAVE_FTELLO64)
check_symbol_exists(gmtime_s "time.h" HAVE_GMTIME_S)
check_symbol_exists(inet_aton "arpa/inet.h" HAVE_INET_ATON)
check_include_file("inet.h" HAVE_INET_H)
check_include_file("inttypes.h" HAVE_INTTYPES_H)
check_include_file("in.h" HAVE_IN_H)
check_symbol_exists(isblank "ctype.h" HAVE_ISBLANK)
check_symbol_exists(localtime_s "time.h" HAVE_LOCALTIME_S)
check_symbol_exists(malloc "stdlib.h" HAVE_MALLOC)
check_include_file("malloc.h" HAVE_MALLOC_H)
check_include_file("memory.h" HAVE_MEMORY_H)
check_symbol_exists(memset "string.h" HAVE_MEMSET)
check_include_file("netinet/in.h" HAVE_NETINET_IN_H)
check_symbol_exists(pow "math.h" HAVE_POW)
check_include_file("process.h" HAVE_PROCESS_H)
check_library_exists(pthread pthread_self "" HAVE_PTHREAD)
check_include_file("pthread.h" HAVE_PTHREAD_H)
check_symbol_exists(realloc "stdlib.h" HAVE_REALLOC)
check_symbol_exists(select "sys/select.h" HAVE_SELECT)
check_include_file("signal.h" HAVE_SIGNAL_H)
check_symbol_exists(socket "sys/socket.h" HAVE_SOCKET)
check_include_file("socket.h" HAVE_SOCKET_H)
check_symbol_exists(sqrt "math.h" HAVE_SQRT)
check_include_file("stdbool.h" HAVE_STDBOOL_H)
check_include_file("stdint.h" HAVE_STDINT_H)
check_include_file("stdlib.h" HAVE_STDLIB_H)
check_symbol_exists(strcasecmp "string.h" HAVE_STRCASECMP)
check_symbol_exists(strchr "string.h" HAVE_STRCHR)
check_symbol_exists(strdup "string.h" HAVE_STRDUP)
check_include_file("stddef.h" HAVE_STDDEF_H)
check_include_file("strings.h" HAVE_STRINGS_H)
check_include_file("string.h" HAVE_STRING_H)
check_symbol_exists(strncasecmp "string.h" HAVE_STRNCASECMP)
check_symbol_exists(strspn "string.h" HAVE_STRSPN)
check_include_file("sys/param.h" HAVE_SYS_PARAM_H)
check_include_file("sys/select.h" HAVE_SYS_SELECT_H)
check_include_file("sys/socket.h" HAVE_SYS_SOCKET_H)
check_include_file("sys/stat.h" HAVE_SYS_STAT_H)
check_include_file("sys/time.h" HAVE_SYS_TIME_H)
check_include_file("sys/types.h" HAVE_SYS_TYPES_H)
check_include_file("sys/wait.h" HAVE_SYS_WAIT_H)
check_include_file("unistd.h" HAVE_UNISTD_H)
check_symbol_exists(vfork "unistd.h" HAVE_VFORK)
check_include_file("vfork.h" HAVE_VFORK_H)
check_include_file("windows.h" HAVE_WINDOWS_H)
check_include_file("winsock2.h" HAVE_WINSOCK2_H)
set(HAVE_WORKING_FORK ${HAVE_FORK})
set(HAVE_WORKING_VFORK ${HAVE_VFORK})
check_include_file("ws2tcpip.h" HAVE_WS2TCPIP_H)
check_type_size(_Bool _BOOL)
check_symbol_exists(_fseeki64 "stdio.h" HAVE__FSEEKI64)
check_symbol_exists(_ftelli64 "stdio.h" HAVE__FTELLI64)
check_symbol_exists(_strdup "string.h" HAVE__STRDUP)
check_symbol_exists(_stricasecmp "string.h" HAVE__STRICASECMP)
check_symbol_exists(_stricmp "string.h" HAVE__STRICMP)

# Features
check_include_file("X11/X.h" HAVE_X11)
# Large file support (fopen64 / disk files > 2 GB)
AXPBOX_TEST_LARGE_FILES(HAVE_LARGE_FILES)

if (DISABLE_PCAP STREQUAL "yes")
    set(WANT_PCAP 0)
else()
    set(WANT_PCAP 1)
endif()

if(WANT_PCAP)
    find_package(PCAP)
    if(PCAP_FOUND)
        message(STATUS "pcap found. Networking support enabled")
        set(HAVE_PCAP 1)
        include_directories(${PCAP_INCLUDE_DIR})
        target_link_libraries(axpbox ${PCAP_LIBRARY})
    else()
        message(STATUS "pcap not found. Networking support disabled")
    endif()
else()
    message(STATUS "pcap disabled. Networking support disabled")
endif()

if (DISABLE_SDL STREQUAL "yes")
    set(HAVE_SDL 0)
else()
    find_package(SDL3 CONFIG QUIET)
    if (SDL3_FOUND)
        set(HAVE_SDL 1)
        set(HAVE_SDL3 1)
        message(STATUS "SDL3 found (system)")
    elseif (EXISTS ${CMAKE_SOURCE_DIR}/third_party/SDL/CMakeLists.txt)
        # Bundled SDL3 (git submodule third_party/SDL), built statically so
        # the executable does not depend on an SDL3 shared library.
        set(SDL_SHARED OFF CACHE BOOL "" FORCE)
        set(SDL_STATIC ON CACHE BOOL "" FORCE)
        set(SDL_TEST_LIBRARY OFF CACHE BOOL "" FORCE)
        add_subdirectory(third_party/SDL EXCLUDE_FROM_ALL)
        set(HAVE_SDL 1)
        set(HAVE_SDL3 1)
        message(STATUS "SDL3 built from bundled submodule (third_party/SDL, static)")
    else()
        set(HAVE_SDL 0)
    endif()
endif()
if (DISABLE_X11 STREQUAL "yes")
    set(HAVE_X11 0)
endif()

if(HAVE_SDL)
    target_link_libraries(axpbox SDL3::SDL3)
    message(STATUS "SDL3 found. SDL graphics support enabled")
else()
    message(WARNING "SDL3 not found (no system SDL3 and third_party/SDL "
                    "submodule not initialized; run: git submodule update "
                    "--init). Building without SDL graphics support")
endif()
if(HAVE_X11)
    target_link_libraries(axpbox X11)
    message(STATUS "x11 found. x11 graphics support enabled")
else()
    message(WARNING "x11 not found. Building without x11 graphics support")
endif()

if (STATIC_COMPILE STREQUAL "yes")
    message(STATUS "Static compilation")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static -lpthread -lrt -Wl,--whole-archive -lpthread -Wl,--no-whole-archive")
    set(CMAKE_EXE_LINKER_FLAGS "-static")
    set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
    set(BUILD_SHARED_LIBS OFF)
endif()

if (NOT CMAKE_GENERATOR MATCHES "Visual Studio")
    if (CMAKE_BUILD_TYPE STREQUAL "Debug")
        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Og -ggdb")
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Og -ggdb")
    elseif (CMAKE_BUILD_TYPE STREQUAL "Release")
        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3")
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -DNDEBUG")
    endif()
endif()

if(HAVE_LARGE_FILES)
    message(STATUS "Large file support enabled")
else()
    message(WARNING "Building without large file graphics support")
endif()

find_package(Git)
if (GIT_FOUND)
    execute_process(COMMAND
        "${GIT_EXECUTABLE}" describe --match=NeVeRmAtCh --always --abbrev=40 --dirty
        WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
        OUTPUT_VARIABLE GIT_SHA
        ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)

    set(PACKAGE_GITSHA "\"${GIT_SHA}\"")
endif()

set(VERSION "\"${PROJECT_VERSION}\"")
set(PACKAGE ${PROJECT_NAME})
set(PACKAGE_BUGREPORT "tglozar@gmail.com")
set(PACKAGE_NAME string(TOLOWER ${PROJECT_NAME}))
set(PACKAGE_VERSION ${PROJECT_VERSION})
set(PACKAGE_STRING ${PACKAGE_NAME}-${PACKAGE_VERSION})
set(PACKAGE_TARNAME ${PACKAGE_STRING}.tar.gz)
set(PACKAGE_URL "http://github.com/lenticularis39/axpbox")

set(PTHREAD_CREATE_JOINABLE "PTHREAD_CREATE_JOINABLE")
set(SELECT_TYPE_ARG1 "int")
set(SELECT_TYPE_ARG234 "fd_set *")
set(SELECT_TYPE_ARG5 "struct timeval *")
set(RETSIGTYPE "void")
set(STDC_HEADERS 1)
check_include_files("time.h;sys/time.h" TIME_WITH_SYS_TIME)

configure_file(src/config.hpp.in src/config.hpp)

install(TARGETS axpbox DESTINATION bin)

message(STATUS "C++ compiler flags  : ${CMAKE_CXX_FLAGS}")
message(STATUS "C compiler flags    : ${CMAKE_C_FLAGS}")
message(STATUS "Linker flags        : ${CMAKE_EXE_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS} ${CMAKE_STATIC_LINKER_FLAGS}")
