# Init CMake (require at least version 2.6)
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)

# General build settings
IF (NOT CMAKE_BUILD_TYPE)
	SET(CMAKE_BUILD_TYPE "Release" CACHE STRING "Possible build types: None Debug Release RelWithDebInfo MinSizeRel")
ENDIF()

IF (NOT ENABLE_SERIALDEV)
	SET(ENABLE_SERIALDEV 0 CACHE BOOL "Enable support for serial port devices using libserialport")
ENDIF()

IF (NOT ENABLE_OPENCV)
	SET(ENABLE_OPENCV 0 CACHE BOOL "Enable support for frame enhancements using OpenCV")
ENDIF()

# Project name and version
PROJECT(libcaer C CXX)
SET(PROJECT_VERSION_MAJOR 2)
SET(PROJECT_VERSION_MINOR 4)
SET(PROJECT_VERSION_PATCH 0)
SET(PROJECT_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH})

# Define installation paths.
INCLUDE(GNUInstallDirs)

# Set compiler info
SET(CC_CLANG FALSE)
SET(CC_GCC FALSE)
SET(CC_ICC FALSE)
SET(CC_MSVC FALSE)

IF ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
	SET(CC_CLANG TRUE)
ELSEIF ("${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang")
	SET(CC_CLANG TRUE)
ELSEIF ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
	SET(CC_GCC TRUE)
ELSEIF ("${CMAKE_C_COMPILER_ID}" STREQUAL "Intel")
	SET(CC_ICC TRUE)
ELSEIF ("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
	SET(CC_MSVC TRUE)
ENDIF()

# Set operating system info
SET(OS_UNIX FALSE)
SET(OS_LINUX FALSE)
SET(OS_MACOSX FALSE)
SET(OS_WINDOWS FALSE)

IF (UNIX)
	SET(OS_UNIX TRUE)
	ADD_DEFINITIONS(-DOS_UNIX=1)
ENDIF()

IF (UNIX AND "${CMAKE_SYSTEM_NAME}" MATCHES "Linux")
	SET(OS_LINUX TRUE)
	ADD_DEFINITIONS(-DOS_LINUX=1)
ENDIF()

IF (UNIX AND APPLE AND "${CMAKE_SYSTEM_NAME}" MATCHES "Darwin")
	SET(OS_MACOSX TRUE)
	ADD_DEFINITIONS(-DOS_MACOSX=1)
ENDIF()

IF (WIN32 AND "${CMAKE_SYSTEM_NAME}" MATCHES "Windows")
	SET(OS_WINDOWS TRUE)
	ADD_DEFINITIONS(-DOS_WINDOWS=1)
ENDIF()

# Check GCC compiler version, 4.9 is needed at least for atomics support.
IF (CC_GCC)
	IF (${CMAKE_C_COMPILER_VERSION} VERSION_LESS "4.9.0")
		MESSAGE(FATAL_ERROR "GCC version found is ${CMAKE_C_COMPILER_VERSION}. Required >= 4.9.0.")
	ENDIF()
ENDIF()

# Check Clang compiler version, 3.6 is needed at least for atomics support.
IF (CC_CLANG)
	IF (${CMAKE_C_COMPILER_VERSION} VERSION_LESS "3.6.0")
		MESSAGE(FATAL_ERROR "Clang version found is ${CMAKE_C_COMPILER_VERSION}. Required >= 3.6.0.")
	ENDIF()
ENDIF()

# Test if we are on a big-endian architecture
INCLUDE(TestBigEndian)
TEST_BIG_ENDIAN(SYSTEM_BIGENDIAN)

# Check size of various types
INCLUDE(CheckTypeSize)
CHECK_TYPE_SIZE("size_t" SIZEOF_SIZE_T)
CHECK_TYPE_SIZE("void *" SIZEOF_VOID_PTR)

IF (NOT "${SIZEOF_VOID_PTR}" STREQUAL "${SIZEOF_SIZE_T}")
	MESSAGE(SEND_ERROR "Size of void * and size_t must be the same!")
ENDIF()

# Check threads support (almost nobody implements C11 threads yet!)
FIND_PACKAGE(Threads)
SET(HAVE_PTHREADS FALSE)
SET(HAVE_WIN32_THREADS FALSE)

IF (DEFINED "CMAKE_USE_PTHREADS_INIT")
	IF (${CMAKE_USE_PTHREADS_INIT})
		SET(HAVE_PTHREADS TRUE)
		ADD_DEFINITIONS(-DHAVE_PTHREADS=1)
	ENDIF()
ENDIF()

IF (DEFINED "CMAKE_USE_WIN32_THREADS_INIT")
	IF (${CMAKE_USE_WIN32_THREADS_INIT})
		SET(HAVE_WIN32_THREADS TRUE)
		ADD_DEFINITIONS(-DHAVE_WIN32_THREADS=1)
	ENDIF()
ENDIF()

# Add system defines for header features
IF (OS_UNIX AND HAVE_PTHREADS)
	# POSIX system (Unix, Linux, MacOS X)
	ADD_DEFINITIONS(-D_XOPEN_SOURCE=700)
	ADD_DEFINITIONS(-D_DEFAULT_SOURCE=1)

	IF (OS_MACOSX)
		ADD_DEFINITIONS(-D_DARWIN_C_SOURCE=1)
	ENDIF()
ENDIF()

IF (OS_WINDOWS AND (CC_GCC OR CC_CLANG))
	ADD_DEFINITIONS(-D__USE_MINGW_ANSI_STDIO=1)
ENDIF()

# C11 standard needed (atomics, threads)
IF (CC_GCC OR CC_CLANG)
	SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11")
	SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
ENDIF()

# Enable all warnings for GCC / Clang
IF (CC_GCC OR CC_CLANG)
	SET(WARN_COMMON_FLAGS "-pedantic -Wall -Wextra")
	SET(WARN_C_FLAGS "")
	SET(WARN_CXX_FLAGS "")

	IF (CC_GCC)
		# Enable all useful warnings in GCC one-by-one.
		SET(WARN_COMMON_FLAGS "${WARN_COMMON_FLAGS} -Wunused -Wundef -Wformat=2 -Winit-self -Wuninitialized")
		SET(WARN_COMMON_FLAGS "${WARN_COMMON_FLAGS} -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings")
		SET(WARN_COMMON_FLAGS "${WARN_COMMON_FLAGS} -Wredundant-decls -Wmissing-declarations -Wstack-protector")
		SET(WARN_COMMON_FLAGS "${WARN_COMMON_FLAGS} -Wshadow -Wfloat-equal -Wconversion -Wstrict-overflow=5")
		SET(WARN_COMMON_FLAGS "${WARN_COMMON_FLAGS} -Wdouble-promotion")

		SET(WARN_C_FLAGS "${WARN_C_FLAGS} -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs")
		SET(WARN_C_FLAGS "${WARN_C_FLAGS} -Wbad-function-cast -Wjump-misses-init -Wunsuffixed-float-constants")
	ENDIF()

	IF (CC_CLANG)
		# Enable all warnings in Clang, then turn off useless ones.
		SET(WARN_COMMON_FLAGS "${WARN_COMMON_FLAGS} -Weverything -Wno-packed -Wno-padded -Wno-unreachable-code-break")
		SET(WARN_COMMON_FLAGS "${WARN_COMMON_FLAGS} -Wno-disabled-macro-expansion -Wno-reserved-id-macro -Wno-vla")
		SET(WARN_COMMON_FLAGS "${WARN_COMMON_FLAGS} -Wno-parentheses-equality -Wno-covered-switch-default")
		SET(WARN_COMMON_FLAGS "${WARN_COMMON_FLAGS} -Wno-used-but-marked-unused -Wno-cast-align")

		SET(WARN_CXX_FLAGS "${WARN_CXX_FLAGS} -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-old-style-cast")
	ENDIF()

	# Apply all flags.
	SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARN_COMMON_FLAGS} ${WARN_C_FLAGS}")
	SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARN_COMMON_FLAGS} ${WARN_CXX_FLAGS}")
ENDIF()

# Search for external libraries with pkg-config.
INCLUDE(FindPkgConfig)

# Required: basic USB devices support
PKG_CHECK_MODULES(LIBUSB1 REQUIRED libusb-1.0>=1.0.17)
SET(PKGCONFIG_REQUIRES_PRIVATE "libusb-1.0 >= 1.0.17")

SET(LIBCAER_INCDIRS ${LIBUSB1_INCLUDE_DIRS})
SET(LIBCAER_LIBDIRS ${LIBUSB1_LIBRARY_DIRS})
SET(LIBCAER_LIBS ${LIBUSB1_LIBRARIES})

# Optional: serial devices support
IF (ENABLE_SERIALDEV)
	# Require libserialport, minimum 0.1.1 version.
	PKG_CHECK_MODULES(SERIALPORT REQUIRED libserialport>=0.1.1)
	SET(PKGCONFIG_REQUIRES_PRIVATE "${PKGCONFIG_REQUIRES_PRIVATE}, libserialport >= 0.1.1")

	SET(LIBCAER_INCDIRS ${LIBCAER_INCDIRS} ${SERIALPORT_INCLUDE_DIRS})
	SET(LIBCAER_LIBDIRS ${LIBCAER_LIBDIRS} ${SERIALPORT_LIBRARY_DIRS})
	SET(LIBCAER_LIBS ${LIBCAER_LIBS} ${SERIALPORT_LIBRARIES})
ENDIF()

# Optional: OpenCV support for frame enhancement
IF (ENABLE_OPENCV)
	# Require new OpenCV 3.1 at least.
	PKG_CHECK_MODULES(OPENCV3 REQUIRED opencv>=3.1.0)
	SET(PKGCONFIG_REQUIRES_PRIVATE "${PKGCONFIG_REQUIRES_PRIVATE}, opencv >= 3.1.0")

	SET(LIBCAER_INCDIRS ${LIBCAER_INCDIRS} ${OPENCV3_INCLUDE_DIRS})
	SET(LIBCAER_LIBDIRS ${LIBCAER_LIBDIRS} ${OPENCV3_LIBRARY_DIRS})
	SET(LIBCAER_LIBS ${LIBCAER_LIBS} ${OPENCV3_LIBRARIES})
ENDIF()

# Threads support
SET(LIBCAER_LIBS ${LIBCAER_LIBS} ${CMAKE_THREAD_LIBS_INIT})

# Math support
SET(LIBCAER_LIBS ${LIBCAER_LIBS} m)

# Add local directory to include paths
SET(LIBCAER_INCDIRS ${LIBCAER_INCDIRS} ${CMAKE_SOURCE_DIR}/include/)
SET(LIBCAER_INCDIRS ${LIBCAER_INCDIRS} ${CMAKE_SOURCE_DIR}/includecpp/)

INCLUDE_DIRECTORIES(${LIBCAER_INCDIRS})
LINK_DIRECTORIES(${LIBCAER_LIBDIRS})

# Set full RPATH
SET(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR})
SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
IF (OS_MACOSX)
	SET(CMAKE_MACOSX_RPATH TRUE)
ENDIF()

# Subdirectories
ADD_SUBDIRECTORY(include)
ADD_SUBDIRECTORY(includecpp)
ADD_SUBDIRECTORY(src)

# Generate pkg-config file
FOREACH (LIB ${CMAKE_THREAD_LIBS_INIT})
	SET(PKGCONFIG_LIBS_PRIVATE "${LIB} ${PKGCONFIG_LIBS_PRIVATE}")
ENDFOREACH()
SET(PKGCONFIG_LIBS_PRIVATE "${PKGCONFIG_LIBS_PRIVATE}-lm")

CONFIGURE_FILE(libcaer.pc.in libcaer.pc @ONLY)

INSTALL(FILES ${CMAKE_BINARY_DIR}/libcaer.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)

# Automatic documentation generation support
FIND_PACKAGE(Doxygen)

IF (DOXYGEN_FOUND)
	ADD_SUBDIRECTORY(docs)
ENDIF()

# Support automatic RPM generation
SET(CPACK_PACKAGE_NAME ${PROJECT_NAME})
SET(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
SET(CPACK_PACKAGE_RELEASE 1)
SET(CPACK_PACKAGE_CONTACT "support@inilabs.com")
SET(CPACK_PACKAGE_VENDOR "iniLabs")
SET(CPACK_PACKAGE_DESCRIPTION "Minimal C library to access, configure and get/send AER data from sensors or to/from neuromorphic processors.")
SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY ${CPACK_PACKAGE_DESCRIPTION})
SET(CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX})
SET(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_PACKAGE_RELEASE}.${CMAKE_SYSTEM_PROCESSOR}")
SET(CPACK_GENERATOR "RPM" "DEB")
SET(CPACK_RPM_PACKAGE_AUTOREQ 1)
#SET(CPACK_RPM_PACKAGE_REQUIRES "libusbx >= 1.0.17, libserialport >= 0.1.1, opencv >= 3.1.0")
SET(CPACK_DEBIAN_PACKAGE_SHLIBDEPS 1)
#SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libusb-1.0-0 (>= 1.0.17), libserialport0 (>= 0.1.1), libopencv-contrib3.1 (>= 3.1.0)")

INCLUDE(CPack)

# Print info summary for debug purposes
MESSAGE(STATUS "Project version is: ${PROJECT_VERSION}")
MESSAGE(STATUS "Compiler is Clang: ${CC_CLANG}")
MESSAGE(STATUS "Compiler is GCC: ${CC_GCC}")
MESSAGE(STATUS "Compiler is IntelCC: ${CC_ICC}")
MESSAGE(STATUS "Compiler is MS VisualC: ${CC_MSVC}")
MESSAGE(STATUS "OS is Unix: ${OS_UNIX}")
MESSAGE(STATUS "OS is Linux: ${OS_LINUX}")
MESSAGE(STATUS "OS is MacOS X: ${OS_MACOSX}")
MESSAGE(STATUS "OS is Windows: ${OS_WINDOWS}")
MESSAGE(STATUS "System is big-endian: ${SYSTEM_BIGENDIAN}")
MESSAGE(STATUS "Thread support is PThreads: ${HAVE_PTHREADS}")
MESSAGE(STATUS "Thread support is Win32 Threads: ${HAVE_WIN32_THREADS}")
MESSAGE(STATUS "C flags are: ${CMAKE_C_FLAGS}")
MESSAGE(STATUS "CXX flags are: ${CMAKE_CXX_FLAGS}")
MESSAGE(STATUS "Include directories are: ${LIBCAER_INCDIRS}")
MESSAGE(STATUS "Library directories are: ${LIBCAER_LIBDIRS}")
MESSAGE(STATUS "Linked libraries are: ${LIBCAER_LIBS}")
MESSAGE(STATUS "Compiled source files are: ${LIBCAER_SRC_FILES}")
MESSAGE(STATUS "Final install libdir is: ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
MESSAGE(STATUS "Final install includedir is: ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}/${CMAKE_PROJECT_NAME}")
