# 
# libkeypoint build system
#
# Dependencies:
#  * OpenCV
#
# Optional for binary file output
#  * HDF5
#
# Optional for fast approximate keypoint matching
#  * flann
#
# Optional for keypoint_thread_test:
#  * Boost (threads)
#
# Optional for GPU-SIFT:
# * Cg
# * GLEW
# * glut
# * DevIL
#
# Optional for CUDA-SIFT:
# * CUDA
#
# Optional for GPU-SURF:
# * Cg
# * CUDA, 
# * GLEW
# * glut
# * libmagick++
#

PROJECT(libkeypoint)

CMAKE_MINIMUM_REQUIRED(VERSION 2.8)

set(LIBKEYPOINT_VERSION 20170112.0.0)

#--------------------------------#
# Enforce an out-of-source build #
#--------------------------------#

STRING(COMPARE EQUAL "${PROJECT_SOURCE_DIR}" "${PROJECT_BINARY_DIR}" INSOURCE)
IF(INSOURCE)
  MESSAGE(FATAL_ERROR "This project requires an out of source build. This is handled by the Makefile. Please type 'make'.")
ENDIF(INSOURCE)


#--------------------------------#
# Build Options                  #
#--------------------------------#

# this gets CMake to use the system triplet (x86_64-linux-gnu)
# for library locations
INCLUDE(GNUInstallDirs)

# Options can be set from the command line when running cmake.
#   eg. cmake . -DCMAKE_BUILD_TYPE=Release -DDEBUG_PROFILING=on

# Build types are: Release, Debug, RelWithDebInfo, MinSizeRel
IF( NOT CMAKE_BUILD_TYPE )
  SET( CMAKE_BUILD_TYPE Release CACHE STRING
       "Build type options are: None Debug Release RelWithDebInfo MinSizeRel."
       FORCE )
ENDIF( NOT CMAKE_BUILD_TYPE )


# HDF5 I/O support option
OPTION( USE_HDF5 "Enable HDF5 I/O support" ON )

# flann support option
OPTION( USE_FLANN "Enable flann support for fast approximate matching" OFF )


# Keypoint type options
# GPU methods are disabled by default, since they don't work well. You will 
# need to modify these values here or in ccache if you want to use them.
OPTION( USE_SIFT      "Enable SIFT support"                             ON  )
OPTION( USE_SURF      "Enable SURF support (32-bit x86 Linux Only)"     ON  )
OPTION( USE_GPU_SIFT  "Enable GPU-SIFT support"                         OFF )
OPTION( USE_CUDA_SIFT "Enable CUDA-SIFT support"                        OFF )
OPTION( USE_GPU_SURF  "Enable GPU-SURF support (32-bit x86 Linux Only)" OFF )
OPTION( USE_CV_SURF   "Enable CV-SURF support"                          ON  )


# Check HDF5
IF( USE_HDF5 )
  FIND_PACKAGE(HDF5)
  IF( NOT HDF5_FOUND )
    MESSAGE( "WARNING - HDF5 library not found. USE_HDF5 will be set to OFF." )
    SET( USE_HDF5 OFF )
  ENDIF( NOT HDF5_FOUND )
ENDIF( USE_HDF5 )

# Check flann
IF( USE_FLANN )
  FIND_PACKAGE(FLANN)
  IF( NOT FLANN_FOUND )
    MESSAGE( "WARNING - flann library not found. USE_FLANN will be set to OFF." )
    SET( USE_FLANN OFF )
  ENDIF( NOT FLANN_FOUND )
ENDIF( USE_FLANN )


# Check SURF requirements
IF( USE_SURF )
  IF( APPLE )
    MESSAGE( "WARNING - SURF can only be used on Linux. USE_SURF will be set to OFF." )
    SET( USE_SURF OFF )
  ENDIF( APPLE )

  IF( CMAKE_SIZEOF_VOID_P MATCHES "8" )
    MESSAGE( "WARNING - SURF can only be used on 32-bit machines. USE_SURF will be set to OFF." )
    SET( USE_SURF OFF )
  ENDIF( CMAKE_SIZEOF_VOID_P MATCHES "8" )
ENDIF( USE_SURF )

# Set SURF options
IF( USE_SURF )
  SET( SURF_LIBS "surf")
ENDIF( USE_SURF )


# Check GPU_SIFT requirements
IF( USE_GPU_SIFT )
   INCLUDE(${PROJECT_SOURCE_DIR}/../CMakeUtils/FindCg.cmake )
   FIND_PACKAGE(Cg)
   IF( NOT CG_FOUND )
      MESSAGE( "WARNING - Can't find CG. USE_GPU_SIFT will be set to OFF." )
      SET( USE_GPU_SIFT OFF )
   ENDIF( NOT CG_FOUND )

   INCLUDE(${PROJECT_SOURCE_DIR}/../CMakeUtils/FindGLEW.cmake ) 
   IF( NOT GLEW_FOUND )
      MESSAGE( "WARNING - Can't find GLEW. USE_GPU_SIFT will be set to OFF." )
      SET( USE_GPU_SIFT OFF )
   ENDIF( NOT GLEW_FOUND )

   INCLUDE(${PROJECT_SOURCE_DIR}/../CMakeUtils/FindDevIL.cmake ) 
   IF( NOT DevIL_FOUND )
      MESSAGE( "WARNING - Can't find FindDevIL. USE_GPU_SIFT will be set to OFF." )
      SET( USE_GPU_SIFT OFF )
   ENDIF( NOT DevIL_FOUND )

   INCLUDE(${PROJECT_SOURCE_DIR}/../CMakeUtils/FindGLUT.cmake ) 
   IF( NOT GLUT_FOUND )
      MESSAGE( "WARNING - Can't find GLUT. USE_GPU_SIFT will be set to OFF." )
      SET( USE_GPU_SIFT OFF )
   ENDIF( NOT GLUT_FOUND )
ENDIF( USE_GPU_SIFT )


# Check CUDA-SIFT Requirements
IF( USE_CUDA_SIFT )
   INCLUDE(${PROJECT_SOURCE_DIR}/../CMakeUtils/FindCUDA.cmake ) 
   IF( NOT CUDA_FOUND )
      MESSAGE( "WARNING - Can't find CUDA. USE_CUDA_SIFT will be set to OFF." )
      SET( USE_CUDA_SIFT OFF )
      #SET( CUDA_INCLUDE_PATH "" )
   ENDIF( NOT CUDA_FOUND )
ENDIF( USE_CUDA_SIFT )


# Check GPU-SURF Requirements: 
#   System: 32-bit Linux
#   Libraries: Cg, CUDA, GLEW, glut, libmagick++
IF( USE_GPU_SURF )

   IF( APPLE )
      MESSAGE( "WARNING - GPU_SURF can only be used on Linux. USE_GPU_SURF will be set to OFF." )
      SET( USE_GPU_SURF OFF )
   ENDIF( APPLE )

   IF( CMAKE_SIZEOF_VOID_P MATCHES "8" )
      MESSAGE( "WARNING - GPU_SURF can only be used on 32-bit machines. USE_GPU_SURF will be set to OFF." )
      SET( USE_GPU_SURF OFF )
   ENDIF( CMAKE_SIZEOF_VOID_P MATCHES "8" )

   INCLUDE(${PROJECT_SOURCE_DIR}/../CMakeUtils/FindCg.cmake ) 
   IF( NOT CG_FOUND )
      MESSAGE( "WARNING - Can't find CG. USE_GPU_SURF will be set to OFF." )
      SET( USE_GPU_SURF OFF )
   ENDIF( NOT CG_FOUND )

   INCLUDE(${PROJECT_SOURCE_DIR}/../CMakeUtils/FindCUDA.cmake ) 
   IF( NOT CUDA_FOUND )
      MESSAGE( "WARNING - Can't find CUDA. USE_GPU_SURF will be set to OFF." )
      SET( USE_GPU_SURF OFF )
      #SET( CUDA_INCLUDE_PATH "" )
   ENDIF( NOT CUDA_FOUND )

   INCLUDE(${PROJECT_SOURCE_DIR}/../CMakeUtils/FindGLEW.cmake ) 
   IF( NOT GLEW_FOUND )
      MESSAGE( "WARNING - Can't find GLEW. USE_GPU_SURF will be set to OFF." )
      SET( USE_GPU_SURF OFF )
   ENDIF( NOT GLEW_FOUND )

   INCLUDE(${PROJECT_SOURCE_DIR}/../CMakeUtils/FindGLUT.cmake ) 
   IF( NOT GLUT_FOUND )
      MESSAGE( "WARNING - Can't find GLUT. USE_GPU_SURF will be set to OFF." )
      SET( USE_GPU_SURF OFF )
   ENDIF( NOT GLUT_FOUND )

   FIND_PATH( MAGICK_INCLUDE_DIR "Magick++.h" /usr/include/ImageMagick )
   SET( CMAKE_REQUIRED_INCLUDES ${MAGICK_INCLUDE_DIR} )
   CHECK_INCLUDE_FILE_CXX( "Magick++.h" HAVE_MAGICK )
   IF( NOT HAVE_MAGICK )
      MESSAGE( "WARNING - Can't find libmagick++. USE_GPU_SURF will be set to OFF." )
      SET( USE_GPU_SURF OFF )
   ELSE( NOT HAVE_MAGICK )
      SET( MAGICK_LIBRARIES Magick++ )
   ENDIF( NOT HAVE_MAGICK )

ENDIF( USE_GPU_SURF )

# Set GPU_SURF Options
IF( USE_GPU_SURF )
   SET( GPU_SURF_LIBS "gpusurf;ncgl" )
ENDIF( USE_GPU_SURF )


# Check CV-SURF Requirements:
FIND_PACKAGE(OpenCV)
IF( NOT OpenCV_FOUND )
 MESSAGE( FATAL_ERROR "Error can't find OpenCV" )
ENDIF( NOT OpenCV_FOUND )
IF( USE_CV_SURF )
  SET( CMAKE_REQUIRED_LIBRARIES ${OpenCV_LIBRARIES} )
  INCLUDE(CheckLibraryExists)
  CHECK_LIBRARY_EXISTS( cv cvExtractSURF "" CV_SUPPORTS_SURF ) 
  IF( NOT CV_SUPPORTS_SURF )
    MESSAGE( "WARNING - The installed version of OpenCV doesn't support CV-SURF. OpenCV >= 1.1.0 required. USE_CV_SURF will be set to OFF" )
    SET( USE_CV_SURF OFF )
  ENDIF( NOT CV_SUPPORTS_SURF )
ENDIF( USE_CV_SURF )


# Boost and boost-threads
FIND_PACKAGE(Boost COMPONENTS thread system)
IF( NOT Boost_FOUND )
   MESSAGE( FATAL_ERROR  "ERROR - Can't find boost" )
ENDIF( NOT Boost_FOUND )
IF( NOT Boost_THREAD_FOUND )
   MESSAGE( "Can't find boost threads - keypoint_thread_test will not be built" )
ENDIF( NOT Boost_THREAD_FOUND )      

#-----------------------#
# Compiler setup        #
#-----------------------#

# Create the config.h file which defines USE_SIFT etc.
CONFIGURE_FILE( ${PROJECT_SOURCE_DIR}/src/config.h.in
    ${CMAKE_BINARY_DIR}/src/config.h )

ADD_DEFINITIONS( "-Wall" -O3 -funroll-loops ${FLANN_DEFINITIONS}  )


INCLUDE_DIRECTORIES( ${PROJECT_SOURCE_DIR}/include
                     ${PROJECT_SOURCE_DIR}/src/lowe_keys
                     ${PROJECT_SOURCE_DIR}/src/surf-v1.0.9
                     ${PROJECT_SOURCE_DIR}/src/SiftGPU/src/SiftGPU/
                     ${PROJECT_SOURCE_DIR}/src/cudaSift
                     ${PROJECT_SOURCE_DIR}/src/cudaSift/common
                     ${PROJECT_SOURCE_DIR}/src/gpusurf/libgpusurf/include
                     ${PROJECT_SOURCE_DIR}/src/gpusurf/libncgl/include
                     ${CMAKE_BINARY_DIR}/src
                     ${OpenCV_INCLUDE_DIRS}
                     ${CUDA_INCLUDE_PATH}
                     ${Boost_INCLUDE_DIR}
                     ${MAGICK_INCLUDE_DIR}
                     ${FLANN_INCLUDE_DIRS}
		     /usr/include/hdf5/serial
                   )

LINK_DIRECTORIES( ${OPENCV_LIBRARY_DIRS} )

#-----------------------#
# Print Some Info       #
#-----------------------#

MESSAGE( "" )
MESSAGE( "CONFIGURATION SUMMARY" )
MESSAGE( "---------------------" )
MESSAGE( "" )
MESSAGE( "CMAKE_BUILD_TYPE = '${CMAKE_BUILD_TYPE}'" )
MESSAGE( "" )
MESSAGE( "USE_HDF5 = ${USE_HDF5}" )
MESSAGE( "USE_FLANN = ${USE_FLANN}" )
MESSAGE( "" )
MESSAGE( "USE_SIFT = ${USE_SIFT}" )
MESSAGE( "USE_SURF = ${USE_SURF}" )
MESSAGE( "USE_GPU_SIFT = ${USE_GPU_SIFT}" )
MESSAGE( "USE_CUDA_SIFT = ${USE_CUDA_SIFT}" )
MESSAGE( "USE_GPU_SURF = ${USE_GPU_SURF}" )
MESSAGE( "USE_CV_SURF = ${USE_CV_SURF}" )
MESSAGE( "" )
MESSAGE( "Boost_THREAD_FOUND = ${Boost_THREAD_FOUND}" )
MESSAGE( "" )
MESSAGE( "CMAKE_INSTALL_PREFIX = '${CMAKE_INSTALL_PREFIX}'" )


IF( CMAKE_BUILD_TYPE STREQUAL Debug )
   MESSAGE( "CMAKE_CXX_FLAGS_DEBUG = '${CMAKE_CXX_FLAGS_DEBUG}'" )
ENDIF( CMAKE_BUILD_TYPE STREQUAL Debug )

IF( CMAKE_BUILD_TYPE STREQUAL RelWithDebInfo )
   MESSAGE( "CMAKE_CXX_FLAGS_RELWITHDEBINFO = '${CMAKE_CXX_FLAGS_RELWITHDEBINFO}'" )
ENDIF( CMAKE_BUILD_TYPE STREQUAL RelWithDebInfo )

IF( CMAKE_BUILD_TYPE STREQUAL Release )
   MESSAGE( "CMAKE_CXX_FLAGS_RELEASE = '${CMAKE_CXX_FLAGS_RELEASE}'" )
ENDIF( CMAKE_BUILD_TYPE STREQUAL Release )

IF( CMAKE_BUILD_TYPE STREQUAL MinSizeRel )
   MESSAGE( "CMAKE_CXX_FLAGS_MINSIZEREL = '${CMAKE_CXX_FLAGS_MINSIZEREL}'" )
ENDIF( CMAKE_BUILD_TYPE STREQUAL MinSizeRel )


#-----------------------#
# Build Instructions    #
#-----------------------#

# Locations for the library and binary files
SET( LIBRARY_OUTPUT_PATH     ${PROJECT_SOURCE_DIR}/lib )
SET( EXECUTABLE_OUTPUT_PATH  ${PROJECT_SOURCE_DIR}/bin )

ADD_SUBDIRECTORY(src)
ADD_SUBDIRECTORY(include)
ADD_SUBDIRECTORY(tests)

#-----------------------#
# Testing Instructions  #
#-----------------------#

ENABLE_TESTING( )
ADD_TEST( test_keypoints ${EXECUTABLE_OUTPUT_PATH}/test_keypoints ${PROJECT_SOURCE_DIR}/test_data/left.jpg  ${PROJECT_SOURCE_DIR}/test_data/right.jpg )
IF( USE_HDF5 )
   ADD_TEST( keypoint_hdf5_test ${EXECUTABLE_OUTPUT_PATH}/keypoint_hdf5_test ${PROJECT_SOURCE_DIR}/test_data/left.jpg )
ENDIF( USE_HDF5 )

#-----------------------#
# Installation Related  #
#-----------------------#

INCLUDE(CMakePackageConfigHelpers)
WRITE_BASIC_PACKAGE_VERSION_FILE(
    "${CMAKE_CURRENT_BINARY_DIR}/LibKeypoint/LibKeypointConfigVersion.cmake"
    VERSION ${LIBKEYPOINT_VERSION}
    COMPATIBILITY AnyNewerVersion
)

IF(CMAKE_VERSION VERSION_LESS "3.0")
    EXPORT(TARGETS keypoint
        FILE "${CMAKE_CURRENT_BINARY_DIR}/LibKeypoint/LibKeypointTargets.cmake"
        NAMESPACE libkeypoint::
    )
ELSE()
    EXPORT(EXPORT LibKeypointTargets
        FILE "${CMAKE_CURRENT_BINARY_DIR}/LibKeypoint/LibKeypointTargets.cmake"
        NAMESPACE libkeypoint::
    )
ENDIF()

CONFIGURE_FILE(cmake/LibKeypointConfig.cmake
    "${CMAKE_CURRENT_BINARY_DIR}/LibKeypoint/LibKeypointConfig.cmake"
    COPYONLY
)

set(ConfigPackageLocation lib/cmake/LibKeypoint)
install(EXPORT LibKeypointTargets
    FILE LibKeypointTargets.cmake
    NAMESPACE libkeypoint::
    DESTINATION ${ConfigPackageLocation}
)
install(
    FILES cmake/LibKeypointConfig.cmake
    "${CMAKE_CURRENT_BINARY_DIR}/LibKeypoint/LibKeypointConfigVersion.cmake"
    DESTINATION "${ConfigPackageLocation}"
)
