###############################################################################
# SETUP
###############################################################################

cmake_minimum_required(VERSION 3.10)

if(WIN32)
	project(VirtualGL-Utils)
else()
	project(VirtualGL)
endif()
string(TOLOWER ${CMAKE_PROJECT_NAME} CMAKE_PROJECT_NAME_LC)
set(VERSION 3.1.3)
set(DOCVERSION 3.1.3)

macro(boolean_number var)
	if(${var})
		set(${var} 1 ${ARGN})
	else()
		set(${var} 0 ${ARGN})
	endif()
endmacro()

macro(report_option var desc)
	if(${var})
		message(STATUS "${desc} enabled (${var} = ${${var}})")
	else()
		message(STATUS "${desc} disabled (${var} = ${${var}})")
	endif()
endmacro()

if(UNIX AND NOT APPLE AND NOT CYGWIN)
	option(VGL_BUILDSERVER "Build ${CMAKE_PROJECT_NAME} server components" TRUE)
	boolean_number(VGL_BUILDSERVER)
	report_option(VGL_BUILDSERVER "VirtualGL server components")
endif()

if(NOT CMAKE_BUILD_TYPE)
	set(CMAKE_BUILD_TYPE Release)
endif()

message(STATUS "CMAKE_BUILD_TYPE = ${CMAKE_BUILD_TYPE}")

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)

string(TIMESTAMP DEFAULT_BUILD "%Y%m%d")
set(BUILD ${DEFAULT_BUILD} CACHE STRING "Build string (default: ${DEFAULT_BUILD})")

message(STATUS "VERSION = ${VERSION}, BUILD = ${BUILD}")
add_definitions(-D__VERSION="${VERSION}" -D__BUILD="${BUILD}"
	-D__APPNAME="${CMAKE_PROJECT_NAME}")

# Detect CPU type and word size
math(EXPR BITS "${CMAKE_SIZEOF_VOID_P} * 8")
string(TOLOWER ${CMAKE_SYSTEM_PROCESSOR} CMAKE_SYSTEM_PROCESSOR_LC)
if(CMAKE_SYSTEM_PROCESSOR_LC MATCHES "x86_64" OR
	CMAKE_SYSTEM_PROCESSOR_LC MATCHES "amd64" OR
	CMAKE_SYSTEM_PROCESSOR_LC MATCHES "i[0-9]86" OR
	CMAKE_SYSTEM_PROCESSOR_LC MATCHES "x86" OR
	CMAKE_SYSTEM_PROCESSOR_LC MATCHES "ia32")
	if(BITS EQUAL 64)
		set(CPU_TYPE x86_64)
	else()
		set(CPU_TYPE i386)
	endif()
	if(NOT CMAKE_SYSTEM_PROCESSOR STREQUAL ${CPU_TYPE})
		set(CMAKE_SYSTEM_PROCESSOR ${CPU_TYPE})
	endif()
elseif(CMAKE_SYSTEM_PROCESSOR_LC STREQUAL "aarch64" OR
	CMAKE_SYSTEM_PROCESSOR_LC MATCHES "^arm")
	if(BITS EQUAL 64)
		set(CPU_TYPE arm64)
	else()
		set(CPU_TYPE arm)
	endif()
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "ppc64" AND BITS EQUAL 32)
	set(CPU_TYPE ppc)
	set(CMAKE_SYSTEM_PROCESSOR ${CPU_TYPE})
else()
	set(CPU_TYPE ${CMAKE_SYSTEM_PROCESSOR_LC})
endif()
if(CMAKE_VERSION VERSION_LESS 3.20)
	include(TestBigEndian)
	test_big_endian(VGL_BIG_ENDIAN)
elseif(CMAKE_C_BYTE_ORDER)
	if(CMAKE_C_BYTE_ORDER STREQUAL "BIG_ENDIAN")
		set(VGL_BIG_ENDIAN 1)
	else()
		set(VGL_BIG_ENDIAN 0)
	endif()
else()
	message(FATAL_ERROR "Could not determine endianness.")
endif()
if(VGL_BIG_ENDIAN)
	set(ENDIANNESS "big-endian")
	add_definitions(-DVGL_BIG_ENDIAN)
else()
	set(ENDIANNESS "little-endian")
endif()
message(STATUS "${BITS}-bit ${ENDIANNESS} build (${CPU_TYPE})")
add_definitions(-D__BITS=${BITS})

if(UNIX)
	set(CMAKE_INSTALL_DEFAULT_PREFIX /opt/${CMAKE_PROJECT_NAME})
	if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
		set(CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_DEFAULT_PREFIX}" CACHE PATH
			"Directory into which to install ${CMAKE_PROJECT_NAME} (default: ${CMAKE_INSTALL_DEFAULT_PREFIX})"
			FORCE)
	endif()
endif()
message(STATUS "CMAKE_INSTALL_PREFIX = ${CMAKE_INSTALL_PREFIX}")

if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
	set(GNUCC 1)
else()
	set(GNUCC 0)
endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
	set(GNUCXX 1)
else()
	set(GNUCXX 0)
endif()

# When the prefix is /opt/${CMAKE_PROJECT_NAME}, we assume that an "official"
# build is being created, and thus we install things into specific locations.

if((UNIX AND CMAKE_INSTALL_PREFIX STREQUAL "${CMAKE_INSTALL_DEFAULT_PREFIX}")
	OR WIN32)
	set(CMAKE_INSTALL_DEFAULT_DATAROOTDIR "")
	set(CMAKE_INSTALL_DEFAULT_DOCDIR "<CMAKE_INSTALL_DATAROOTDIR>/doc")
	if(UNIX)
		set(CMAKE_INSTALL_DEFAULT_LIBDIR "lib${BITS}")
	endif()
endif()
if(WIN32)
	set(CMAKE_INSTALL_DEFAULT_BINDIR ".")
	set(CMAKE_INSTALL_DEFAULT_DOCDIR ".")
endif()

include(cmakescripts/GNUInstallDirs.cmake)

macro(report_directory var)
	if(CMAKE_INSTALL_${var} STREQUAL CMAKE_INSTALL_FULL_${var})
		message(STATUS "CMAKE_INSTALL_${var} = ${CMAKE_INSTALL_${var}}")
	else()
		message(STATUS "CMAKE_INSTALL_${var} = ${CMAKE_INSTALL_${var}} (${CMAKE_INSTALL_FULL_${var}})")
	endif()
	mark_as_advanced(CLEAR CMAKE_INSTALL_${var})
endmacro()

set(DIRLIST "BINDIR;DATAROOTDIR;DOCDIR")
if(VGL_BUILDSERVER)
	list(APPEND DIRLIST "INCLUDEDIR;LIBDIR")
endif()
foreach(dir ${DIRLIST})
	report_directory(${dir})
endforeach()

include_directories(${CMAKE_SOURCE_DIR}/include)

if(MSVC)
	# Use the static C library for all build types
	foreach(var CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE
		CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO
		CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
		CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
		if(${var} MATCHES "/MD")
			string(REGEX REPLACE "/MD" "/MT" ${var} "${${var}}")
		endif()
	endforeach()

	add_definitions(-wd4996 -D_CRT_SECURE_NO_DEPRECATE)

	# Don't auto-generate manifests
	set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /MANIFEST:NO")

	# Avoid linker warning when doing Debug build if dependent libraries are
	# linked with the Release version of the static C library.
	set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} /NODEFAULTLIB:LIBCMT")
endif()

# This ensures that we don't depend on libstdc++ or libgcc
if(GNUCXX AND NOT APPLE AND NOT CYGWIN)
	set(DEFAULT_VGL_BUILDSTATIC 1)
	option(VGL_BUILDSTATIC
		"Link statically against libgcc and libstdc++, if possible"
		${DEFAULT_VGL_BUILDSTATIC})
	if(VGL_BUILDSTATIC)
		string(REGEX REPLACE " " ";" CMAKE_CXX_FLAGS_SEMICOLON
			"${CMAKE_CXX_FLAGS}")
		execute_process(COMMAND ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_FLAGS_SEMICOLON}
			--print-file-name=libstdc++.a OUTPUT_VARIABLE LIBSTDCPLUSPLUS
			RESULT_VARIABLE RESULT)
		string(REGEX REPLACE "\n" "" LIBSTDCPLUSPLUS ${LIBSTDCPLUSPLUS})
		if(RESULT MATCHES 0 AND LIBSTDCPLUSPLUS AND
			NOT LIBSTDCPLUSPLUS STREQUAL "libstdc++.a")
			message(STATUS "Linking with static libstdc++:\n   ${LIBSTDCPLUSPLUS}")
			file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/staticlib)
			execute_process(COMMAND ${CMAKE_COMMAND} -E remove
				${CMAKE_BINARY_DIR}/staticlib/libstdc++.a)
			execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink
				${LIBSTDCPLUSPLUS} ${CMAKE_BINARY_DIR}/staticlib/libstdc++.a)
			set(CMAKE_EXE_LINKER_FLAGS
				"${CMAKE_EXE_LINKER_FLAGS} -L${CMAKE_BINARY_DIR}/staticlib")
			set(CMAKE_SHARED_LINKER_FLAGS
				"${CMAKE_SHARED_LINKER_FLAGS} -L${CMAKE_BINARY_DIR}/staticlib")
		else()
			message(WARNING "Cannot find static libstdc++.  VirtualGL will depend on dynamic libstdc++.")
		endif()
		if(CMAKE_COMPILER_IS_GNUCXX)
			add_definitions(-static-libgcc)
		endif()
		set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc")
		set(CMAKE_SHARED_LINKER_FLAGS
			"${CMAKE_SHARED_LINKER_FLAGS} -static-libgcc")
	endif()
endif()

# Don't build RPATH into libraries generated in the build directory
set(CMAKE_SKIP_BUILD_RPATH 1)

if(GNUCC)
	add_definitions(-D_GNU_SOURCE)
	if(NOT APPLE)
		set(MAPFLAG "-Wl,--version-script,")
	endif()
	# Use the maximum optimization level for release builds
	foreach(var CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_RELWITHDEBINFO
		CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELWITHDEBINFO)
		if(${var} MATCHES "-O2")
			string(REGEX REPLACE "-O2" "-O3" ${var} "${${var}}")
		endif()
	endforeach()
	set(MINUSZ "-Wl,-z,")
endif()

if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
	add_definitions(-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64)
endif()

if(CMAKE_SYSTEM_NAME STREQUAL "SunOS")
	set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -lc")
	set(MAPFLAG "-Wl,-M,")
	if("${CMAKE_C_COMPILER_ID} ${CMAKE_CXX_COMPILER_ID}" MATCHES SunPro)
		add_definitions(-mt -norunpath)
		set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -mt")
		set(CMAKE_SHARED_LINKER_FLAGS
			"-z direct -lCrun -z nodirect -mt ${CMAKE_SHARED_LINKER_FLAGS}")
		set(MAPFLAG "-M")
		set(MINUSZ "-z")

		# Use the maximum optimization level for release builds
		foreach(var CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_RELWITHDEBINFO
			CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELWITHDEBINFO)
			if(${var} MATCHES "-xO3")
				string(REGEX REPLACE "-xO3" "-xO5" ${var} "${${var}}")
			endif()
			if(${var} MATCHES "-xO2")
				string(REGEX REPLACE "-xO2" "-xO5" ${var} "${${var}}")
			endif()
		endforeach()
	endif()
endif()

include(CheckCSourceCompiles)
if(MSVC)
	set(INLINE_OPTIONS "__inline;inline")
else()
	set(INLINE_OPTIONS "__inline__;inline")
endif()
option(VGL_FORCEINLINE "Force function inlining" TRUE)
boolean_number(VGL_FORCEINLINE)
if(VGL_FORCEINLINE)
	if(MSVC)
		list(INSERT INLINE_OPTIONS 0 "__forceinline")
	else()
		list(INSERT INLINE_OPTIONS 0 "inline __attribute__((always_inline))")
		list(INSERT INLINE_OPTIONS 0 "__inline__ __attribute__((always_inline))")
	endif()
endif()
foreach(inline ${INLINE_OPTIONS})
	check_c_source_compiles("${inline} static int foo(void) { return 0; } int main(void) { return foo(); }"
		INLINE_WORKS)
	if(INLINE_WORKS)
		set(INLINE ${inline})
		break()
	endif()
endforeach()
if(NOT INLINE_WORKS)
	message(FATAL_ERROR "Could not determine how to inline functions.")
endif()
message(STATUS "INLINE = ${INLINE} (VGL_FORCEINLINE = ${VGL_FORCEINLINE})")
configure_file(include/vglinline.h.in include/vglinline.h)
include_directories(${CMAKE_BINARY_DIR}/include)

option(VGL_USEHELGRIND
	"Include code annotations to suppress helgrind warning messages about innocuous VirtualGL behaviors."
	OFF)
if(VGL_USEHELGRIND)
	add_definitions(-DUSEHELGRIND)
	boolean_number(VGL_USEHELGRIND)
	report_option(VGL_USEHELGRIND "Helgrind annotations")
endif()

if(WIN32)

set(VGL_USEXV 0)
add_subdirectory(util)
add_subdirectory(wgldemos)
add_subdirectory(diags)

else()

if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
	set(CMAKE_LIBRARY_PATH /usr/lib/${CPU_TYPE}-linux-gnu;/usr/lib${BITS};/usr/lib)
endif()
include(FindX11)
# Clarify that we need to link with the legacy OpenGL library (libGL) rather
# than the GLVND OpenGL library.  This shuts up CMake 3.11 and later.
set(OpenGL_GL_PREFERENCE LEGACY)
include(FindOpenGL)

# Ensure that we build and link against the X11 version of OpenGL on OS X/macOS
if(APPLE)
	set(X11_X11_INCLUDE_PATH /opt/X11/include)
	set(OPENGL_gl_LIBRARY /opt/X11/lib/libGL.dylib)
	set(OPENGL_glu_LIBRARY /opt/X11/lib/libGLU.dylib)
endif()

include_directories(${X11_X11_INCLUDE_PATH} ${OPENGL_INCLUDE_DIR})

option(VGL_USEXV "Enable X Video support" TRUE)
boolean_number(VGL_USEXV)

if(NOT X11_Xv_INCLUDE_PATH OR NOT X11_Xv_LIB)
	set(VGL_USEXV 0)
endif()
report_option(VGL_USEXV "X Video support")

if(VGL_USEXV)
	add_definitions(-DUSEXV)
	include_directories(${X11_Xv_INCLUDE_PATH})
endif()

if(NOT WIN32)
	include(cmakescripts/FindTurboJPEG.cmake)
endif()

if(NOT CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
	set(LIBDL dl)
endif()

endif() # WIN32

string(TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_UC)

set(EFFECTIVE_C_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${CMAKE_BUILD_TYPE_UC}}")
message(STATUS "Compiler flags = ${EFFECTIVE_C_FLAGS}")

set(EFFECTIVE_LD_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS_${CMAKE_BUILD_TYPE_UC}}")
message(STATUS "Linker flags = ${EFFECTIVE_LD_FLAGS}")


###############################################################################
# TARGETS
###############################################################################

if(NOT WIN32)

if(VGL_BUILDSERVER)
	set(DEFAULT_VGL_FAKER_NAME vglfaker)
	set(VGL_FAKER_NAME ${DEFAULT_VGL_FAKER_NAME} CACHE STRING
		"Base name for VirtualGL's GLX/OpenGL/X11/XCB interposer library (default: ${DEFAULT_VGL_FAKER_NAME})")

	set(DEFAULT_VGL_DLFAKER_NAME dlfaker)
	set(VGL_DLFAKER_NAME ${DEFAULT_VGL_DLFAKER_NAME} CACHE STRING
		"Base name for VirtualGL's dlopen() interposer library (default: ${DEFAULT_VGL_DLFAKER_NAME})")

	set(DEFAULT_VGL_GEFAKER_NAME gefaker)
	set(VGL_GEFAKER_NAME ${DEFAULT_VGL_GEFAKER_NAME} CACHE STRING
		"Base name for VirtualGL's getenv() interposer library (default: ${DEFAULT_VGL_GEFAKER_NAME})")

	set(CMAKE_POSITION_INDEPENDENT_CODE 1)
endif()

set(FBXLIB fbx)
if(VGL_USEXV)
	set(FBXLIB ${FBXLIB};fbxv)
endif()

add_subdirectory(util)
add_subdirectory(common)
if(VGL_BUILDSERVER)
	add_subdirectory(server)
endif()
add_subdirectory(client)
add_subdirectory(demos)
add_subdirectory(diags)
add_subdirectory(doc)

else()

install(FILES LICENSE.txt LGPL.txt DESTINATION ${CMAKE_INSTALL_DOCDIR})

endif() # WIN32


###############################################################################
# INSTALLATION AND PACKAGING
###############################################################################

include(cmakescripts/BuildPackages.cmake)

configure_file("${CMAKE_SOURCE_DIR}/cmakescripts/cmake_uninstall.cmake.in"
	"cmake_uninstall.cmake" IMMEDIATE @ONLY)

add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P cmake_uninstall.cmake)
