cmake_minimum_required(VERSION 3.13)
project(pioneer LANGUAGES CXX)

set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_CXX_STANDARD 17)

set(PIONEER_VERSION "20250501"
    CACHE STRING "Version identifier for current compiled build")

set(PROJECT_VERSION_INFO ""
	CACHE STRING "Additional version information (optional)")

# If both libGL.so and libOpenGL.so are found, default to the latter
# (former is a legacy name).
# Set OpenGL_GL_PREFERENCE=LEGACY to force it to use the former.
if(POLICY CMP0072)
	cmake_policy(SET CMP0072 NEW)
endif()

include(cmake/TargetArchitecture.cmake)
include(cmake/InstallPioneer.cmake)

if (MINGW)
	# Fix build errors on AppVeyor with MinGW due to a broken GLEW config script
	list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_SOURCE_DIR}/cmake/Modules)
endif (MINGW)

# Put the output into the root dir so it can be run from Visual Studio
if (MSVC)
    set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR})
endif (MSVC)

if (MSVC)
	# Avoid annoying warnings from Visual Studio
	add_definitions(-D_CRT_SECURE_NO_WARNINGS)

	# Use M_PI/M_E macros from math.h
	add_definitions(-D_USE_MATH_DEFINES -DHAVE_M_PI)

    # Disable warning C4506 so that src/lua/LuaObject.h: template <> void LuaObject<SystemPath>::PushToLua(const SystemPath &o);
    # doesn't spew multiple warnings

	set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP /wd4506")
	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /wd4506")

	# Disable warnings to avoid MSVC spam (moved from libs.h):
	# #pragma warning(disable : 4244) // "conversion from x to x: possible loss of data"
	# #pragma warning(disable : 4800) // int-to-bool "performance warning"
	# #pragma warning(disable : 4355) // 'this' used in base member initializer list
	# #pragma warning(disable : 4351) // new behavior [after vs2003!]: elements of array 'array' will be default initialized
	set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /wd4244 /wd4800 /wd4355 /wd4351")
	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4244 /wd4800 /wd4355 /wd4351")

	add_definitions(-DNOMINMAX)
endif (MSVC)

if (APPLE)
	set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-gnu")
	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-gnu")
endif(APPLE)

option(USE_SSE42 "Compile for SSE4.2 compatible microarchitectures (enables optimizations)" ${PIONEER_TARGET_INTEL})

if (USE_SSE42)
	if (NOT MSVC)
		add_compile_options("-msse4.2" "-mpopcnt")
	endif()
endif (USE_SSE42)

option(USE_AVX2 "Compile for AVX2 compatible microarchitectures (Haswell and newer)" OFF)

if (USE_AVX2)
	if (MSVC)
		add_compile_options("/arch:AVX2")
	else()
		add_compile_options("-mavx2" "-mlzcnt")
	endif()
endif(USE_AVX2)

option(USE_LLD_LINKER "Use the LLVM lld linker instead of gcc's linker" OFF)
if (CMAKE_COMPILER_IS_GNUCXX)
	add_compile_options(
		-fdiagnostics-color=auto
		-Wall
		-Wextra
		-Wno-unused-parameter
		-Wno-unused-but-set-parameter
		-Wno-implicit-fallthrough
	)
	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive")
	if (USE_LLD_LINKER)
		set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fuse-ld=lld")
	endif(USE_LLD_LINKER)
	set(CMAKE_CXX_FLAGS_DEBUG "-g -Og")
endif (CMAKE_COMPILER_IS_GNUCXX)

option(USE_TIME_TRACE "Use -ftime-trace to profile compile times (requires Clang)" OFF)
if (USE_TIME_TRACE)
	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftime-trace")
endif()

option(USE_ASAN "Use -fsanitize=address when compiling (requires Clang)" OFF)
if (USE_ASAN)
	add_compile_options(
		-g
		-fsanitize=address
		-fno-omit-frame-pointer)
	add_link_options(
		-fsanitize=address)
endif()

include(CheckSymbolExists)
check_symbol_exists(feclearexcept "fenv.h" HAS_FECLEAREXCEPT)
check_symbol_exists(feenableexcept "fenv.h" HAS_FEENABLEEXCEPT)
check_symbol_exists(fedisableexcept "fenv.h" HAS_FEDISABLEEXCEPT)
if (HAS_FECLEAREXCEPT AND HAS_FEENABLEEXCEPT AND HAS_FEDISABLEEXCEPT)
	set(HAS_FPE_OPS ON)
endif()

if (NOT CMAKE_BUILD_TYPE)
	set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING
		"Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel."
		FORCE)
	set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS None Debug Release RelWithDebInfo MinSizeRel)
endif()

# Get the GIT hash of the latest commit
include(FindGit OPTIONAL)
if (GIT_FOUND AND EXISTS ${PROJECT_SOURCE_DIR}/.git)
	execute_process(
		COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD
		WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
		OUTPUT_VARIABLE PROJECT_VERSION_GIT
		OUTPUT_STRIP_TRAILING_WHITESPACE
	)
endif()

if(DEFINED PROJECT_VERSION_GIT)
	string(JOIN " " PROJECT_VERSION_INFO ${PROJECT_VERSION_GIT} ${PROJECT_VERSION_INFO})
endif()

if (MINGW)
	# Enable PRIxYY macros on MinGW
	add_definitions(-D__STDC_FORMAT_MACROS)
endif (MINGW)

option(WITH_OBJECTVIEWER "Include the object viewer in the build" ON)
option(WITH_DEVKEYS "Include various extra keybindings for dev functions" ON)
option(USE_SYSTEM_LIBGLEW "Use the system's libglew" OFF)
option(USE_SYSTEM_LIBLUA "Use the system's liblua" OFF)
option(PROFILER_ENABLED "Build pioneer with profiling support built-in." OFF)
option(REMOTE_LUA_REPL "Enable remote LUA console" OFF)

if (REMOTE_LUA_REPL)
	set(REMOTE_LUA_REPL_PORT 12345 CACHE STRING "TCP port for remote LUA console")
endif (REMOTE_LUA_REPL)

list(APPEND SRC_FOLDERS
	src/
	src/collider
	src/galaxy
	src/graphics
	src/graphics/dummy
	src/graphics/opengl
	src/math
	src/pigui
	src/scenegraph
	src/ship
	src/sound
	src/terrain
	src/text
)

macro(add_source_folders TARGET SRC_FOLDERS)
foreach (each IN LISTS ${SRC_FOLDERS})
	file(GLOB header_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${each}/*.h)
	list(APPEND ${TARGET}_HXX_FILES ${header_files})
	file(GLOB src_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${each}/*.cpp)
	list(APPEND ${TARGET}_CXX_FILES ${src_files})
endforeach ()
endmacro()

add_source_folders(PIONEER SRC_FOLDERS)

list(REMOVE_ITEM PIONEER_CXX_FILES
	src/main.cpp
	src/modelcompiler.cpp
	src/savegamedump.cpp
	src/tests.cpp
	src/textstress.cpp
	src/uitest.cpp
)

list(APPEND FILESYSTEM_CXX_FILES)

if (WIN32)
	list(APPEND FILESYSTEM_CXX_FILES
		src/win32/FileSystemWin32.cpp
		src/win32/OSWin32.cpp
		src/win32/TextUtils.cpp
	)
else (WIN32)
	list(APPEND FILESYSTEM_CXX_FILES
		src/posix/FileSystemPosix.cpp
		src/posix/OSPosix.cpp
	)
endif (WIN32)

configure_file(buildopts.h.cmakein buildopts.h @ONLY)

LIST(APPEND PIONEER_CXX_FILES ${FILESYSTEM_CXX_FILES})

option(USE_PIONEER_THIRDPARTY "Use pioneer's thirdparty library repository." OFF)
if (USE_PIONEER_THIRDPARTY)
	list(APPEND CMAKE_PREFIX_PATH ${CMAKE_SOURCE_DIR}/pioneer-thirdparty/usr)
	include_directories(${CMAKE_SOURCE_DIR}/pioneer-thirdparty/usr/include)
	link_directories(${CMAKE_SOURCE_DIR}/pioneer-thirdparty/usr/lib)
	find_package(Threads)
endif()

if (USE_SYSTEM_LIBGLEW)
	add_library(GLEW::GLEW INTERFACE IMPORTED)
	find_package(GLEW REQUIRED)
endif (USE_SYSTEM_LIBGLEW)

if (USE_SYSTEM_LIBLUA)
	find_package(Lua 5.2 EXACT REQUIRED)
	include_directories(${LUA_INCLUDE_DIR})
	if (WIN32)
		add_definitions(-DLUA_BUILD_AS_DLL)
	endif (WIN32)
endif (USE_SYSTEM_LIBLUA)

if (PROFILER_ENABLED)
	add_definitions(-DPIONEER_PROFILER=1)
endif(PROFILER_ENABLED)

if (WIN32)
	add_definitions(-DPSAPI_VERSION=1)
endif (WIN32)

macro(set_cxx_properties)
	set_target_properties(${ARGN} PROPERTIES
		CXX_STANDARD 17
		CXX_STANDARD_REQUIRED ON
		CXX_EXTENSIONS ON
	)
endmacro()

macro(define_pioneer_library library_name _src _header)
	add_library(${library_name} STATIC ${${_src}} ${${_header}})
	set_cxx_properties(${library_name})
endmacro()

if (MSVC)
	include(msvc-defaults.cmake)
else (MSVC)
	find_package(PkgConfig REQUIRED)

	pkg_check_modules(SDL2 REQUIRED sdl2)
	pkg_check_modules(SDL2_IMAGE REQUIRED SDL2_image)

	pkg_check_modules(ASSIMP REQUIRED assimp>=5.0)
	pkg_check_modules(SIGCPP REQUIRED sigc++-2.0)
	pkg_check_modules(VORBISFILE REQUIRED vorbisfile)
endif (MSVC)

find_package(Threads REQUIRED)
find_package(Freetype REQUIRED)
find_package(OpenGL REQUIRED)

set(FMT_INSTALL OFF CACHE BOOL "Enable install of libfmt" FORCE)

add_subdirectory(contrib/lz4)
add_subdirectory(contrib/fmt)

set(NANOSOCKETS_STATIC "1")
add_subdirectory(contrib/nanosockets)

set(PIONEER_SRC ${CMAKE_SOURCE_DIR}/src)
set(PIONEER_CONTRIB ${CMAKE_SOURCE_DIR}/contrib)

include_directories(
	${PIONEER_SRC}
	${PIONEER_CONTRIB}
	${PIONEER_CONTRIB}/doctest
	${PIONEER_CONTRIB}/fmt/include
	${ASSIMP_INCLUDE_DIRS}
	${FREETYPE_INCLUDE_DIRS}
	${OPENGL_INCLUDE_DIRS}
	${SDL2_INCLUDE_DIRS}
	${SDL2_IMAGE_INCLUDE_DIRS}
	${SIGCPP_INCLUDE_DIRS}
	${VORBISFILE_INCLUDE_DIRS}
	${GLEW_INCLUDE_DIRS}
)

# We don't want/need the GLU dependency
add_definitions(-DGLEW_NO_GLU)

if (NOT USE_SYSTEM_LIBGLEW)
	add_subdirectory(contrib/glew)
	add_library(GLEW::GLEW ALIAS glew)
	include_directories(contrib/glew)

	# Specify that we compile against a static build of Glew
	# (required on Windows)
	add_definitions(-DGLEW_STATIC)
endif (NOT USE_SYSTEM_LIBGLEW)

add_subdirectory(contrib/imgui)
add_definitions(-DIMGUI_DEFINE_MATH_OPERATORS)

add_subdirectory(contrib/jenkins)
add_subdirectory(contrib/PicoDDS)
add_subdirectory(contrib/profiler)
if (NOT USE_SYSTEM_LIBLUA)
	add_subdirectory(contrib/lua)
	set(LUA_LIBRARIES lua)
	include_directories(contrib/lua)
endif (NOT USE_SYSTEM_LIBLUA)

add_subdirectory(src/core)

define_pioneer_library(pioneer-lib PIONEER_CXX_FILES PIONEER_HXX_FILES)
target_link_libraries(pioneer-lib PUBLIC pioneer-core)

add_subdirectory(src/lua)

if (WIN32)
	string(TIMESTAMP BUILD_YEAR "%Y")
	string(TIMESTAMP BUILD_DATE "%Y%m%d")
	set(RESOURCES ${CMAKE_BINARY_DIR}/pioneer.rc)
	configure_file(pioneer.rc.cmakein ${RESOURCES} @ONLY)
endif()

link_directories(
	${ASSIMP_LIBRARY_DIRS}
	${SDL2_LIBRARY_DIRS}
	${SDL2_IMAGE_LIBRARY_DIRS}
	${SIGCPP_LIBRARY_DIRS}
	${VORBISFILE_LIBRARY_DIRS}
)

list(APPEND UNITTEST_SRC_FOLDERS
	src/test)
add_source_folders(UNITTEST UNITTEST_SRC_FOLDERS)

add_executable(${PROJECT_NAME} WIN32 src/main.cpp ${RESOURCES})
add_executable(unittest ${UNITTEST_CXX_FILES})
add_executable(modelcompiler src/modelcompiler.cpp)
add_executable(savegamedump
	src/savegamedump.cpp
	src/JsonUtils.cpp
	src/FileSystem.cpp
	src/StringF.cpp
	src/DateTime.cpp
	src/Lang.cpp
	${FILESYSTEM_CXX_FILES}
)

find_program(NATURALDOCS NAMES naturaldocs)
if (NATURALDOCS)
	add_custom_target(codedoc
		${CMAKE_COMMAND} -E make_directory codedoc
		COMMAND naturaldocs -i src/ -i data/libs/ -xi src/data/ -o HTML codedoc/ -p nd/ -do -ro -s Default Local
	WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
endif()

find_package(Python2 COMPONENTS Interpreter)
if (Python2_Interpreter_FOUND)
	add_custom_target(enums
		COMMAND "${Python2_EXECUTABLE}" scripts/scan_enums.py -o src/enum_table.cpp --pattern='*.h' -r src
		WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
	)
else()
	message(WARNING, "Python 2 not found; enums will not be scanned.")
endif()

target_link_libraries(pioneer-lib PUBLIC lz4 fmt::fmt)

list(APPEND pioneerLibs
	pioneer-core
	pioneer-lib
	pioneer-lua
	${ASSIMP_LIBRARIES}
	${FREETYPE_LIBRARIES}
	${OPENGL_LIBRARIES}
	${SDL2_LIBRARIES}
	${SDL2_IMAGE_LIBRARIES}
	${SIGCPP_LIBRARIES}
	${VORBISFILE_LIBRARIES}
	${LUA_LIBRARIES}
	GLEW::GLEW
	imgui
	jenkins
	picodds
	profiler
	Threads::Threads
)

if (WIN32)
	list(APPEND winLibs shlwapi psapi)
endif (WIN32)

add_subdirectory(src/editor)

target_link_libraries(${PROJECT_NAME} LINK_PRIVATE ${pioneerLibs} ${winLibs})
target_link_libraries(unittest LINK_PRIVATE ${pioneerLibs} ${winLibs})
target_link_libraries(modelcompiler LINK_PRIVATE ${pioneerLibs} ${winLibs})
target_link_libraries(savegamedump LINK_PRIVATE pioneer-core ${SDL2_IMAGE_LIBRARIES} ${winLibs})

set_cxx_properties(${PROJECT_NAME} unittest modelcompiler savegamedump)

if(MSVC)
	add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
		COMMAND xcopy ..\\pioneer-thirdparty\\win32\\bin\\${MSVC_ARCH}\\vs2019\\*.dll ${TargetDir}*.dll /Y /C
		WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
		COMMENT "copy the dlls into the directory"
		VERBATIM
	)
endif(MSVC)

if (CMAKE_CROSSCOMPILING)
	find_program(MODELCOMPILER modelcompiler DOC "modelcompiler executable for the host")
else (CMAKE_CROSSCOMPILING)
	set(MODELCOMPILER $<TARGET_FILE:modelcompiler>)
endif (CMAKE_CROSSCOMPILING)

add_custom_target(build-data)

if (MODELCOMPILER)
	# Optimize the models.
	# This really shouldn't be done inside the source tree...
	if (NOT MSVC)
		add_custom_target(build-models
			COMMAND ${CMAKE_COMMAND} -E env SDL_VIDEODRIVER=dummy PIONEER_LOCAL_DATA_ONLY=1
			${MODELCOMPILER} -b inplace
			WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
			COMMENT "Optimizing models" VERBATIM
		)
		add_dependencies(build-data build-models)
	endif (NOT MSVC)
else (MODELCOMPILER)
	message(WARNING "No modelcompiler provided, models won't be optimized!")
endif(MODELCOMPILER)

install(TARGETS ${PROJECT_NAME} editor modelcompiler savegamedump
	RUNTIME DESTINATION ${PIONEER_INSTALL_BINDIR}
)

if (MSVC)
	file(GLOB win_libs ${CMAKE_SOURCE_DIR}/../pioneer-thirdparty/win32/bin/${MSVC_ARCH}/vs2019/*.dll)
	install(FILES ${win_libs} DESTINATION ${PIONEER_INSTALL_BINDIR})
endif (MSVC)

install(DIRECTORY data/
	DESTINATION ${PIONEER_INSTALL_DATADIR}/data
	REGEX "/models" EXCLUDE
	PATTERN ".gitignore" EXCLUDE
	PATTERN "listdata.*" EXCLUDE
	PATTERN "Makefile.am" EXCLUDE
)

install(DIRECTORY data/models/
	DESTINATION ${PIONEER_INSTALL_DATADIR}/data/models
	FILES_MATCHING PATTERN "*.sgm" PATTERN "*.dds" PATTERN "*.png"
)
