# CMake module for R
# Borrows ideas from RStudio's FindLibR.cmake
#
# Defines the following:
#  LIBR_FOUND
#  LIBR_HOME
#  LIBR_EXECUTABLE
#  LIBR_INCLUDE_DIRS
#  LIBR_LIB_DIR
#  LIBR_CORE_LIBRARY
# and a cmake function to create R.lib for MSVC
#
# The following could be provided by user through cmake's -D options:
#  LIBR_EXECUTABLE (for unix and win)
#  R_VERSION (for win)
#  R_ARCH (for win 64 when want 32 bit build)
#
# TODO:
# - someone to verify OSX detection,
# - possibly, add OSX detection based on current R in PATH or LIBR_EXECUTABLE
# - improve registry-based R_HOME detection in Windows (from a set of R_VERSION's)


# Windows users might want to change this to their R version:
if(NOT R_VERSION)
  set(R_VERSION "4.0.0")
endif()
if(NOT R_ARCH)
  if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4")
    set(R_ARCH "i386")
  else()
    set(R_ARCH "x64")
  endif()
endif()


# Creates R.lib and R.def in the build directory for linking with MSVC
function(create_rlib_for_msvc)
  # various checks and warnings
  if(NOT WIN32 OR (NOT MSVC AND NOT MINGW))
    message(FATAL_ERROR "create_rlib_for_msvc() can only be used with MSVC or MINGW")
  endif()
  if(NOT EXISTS "${LIBR_LIB_DIR}")
    message(FATAL_ERROR "LIBR_LIB_DIR was not set!")
  endif()
  find_program(DLLTOOL_EXE dlltool)
  if(NOT DLLTOOL_EXE)
    message(FATAL_ERROR "\ndlltool.exe not found!\
      \nDo you have Rtools installed with its MinGW's bin/ in PATH?")
  endif()

  # extract symbols from R.dll into R.def and R.lib import library
  get_filename_component(
    LIBR_RSCRIPT_EXECUTABLE_DIR
    ${LIBR_EXECUTABLE}
    DIRECTORY
  )
  set(LIBR_RSCRIPT_EXECUTABLE "${LIBR_RSCRIPT_EXECUTABLE_DIR}/Rscript")

  execute_process(
    COMMAND ${LIBR_RSCRIPT_EXECUTABLE}
    "${CMAKE_CURRENT_BINARY_DIR}/../../R-package/inst/make-r-def.R"
    "${LIBR_LIB_DIR}/R.dll" "${CMAKE_CURRENT_BINARY_DIR}/R.def"
  )

  execute_process(COMMAND ${DLLTOOL_EXE}
    "--input-def" "${CMAKE_CURRENT_BINARY_DIR}/R.def"
    "--output-lib" "${CMAKE_CURRENT_BINARY_DIR}/R.lib")
endfunction(create_rlib_for_msvc)


# detection for OSX
if(APPLE)

  find_library(LIBR_LIBRARIES R)

  if(LIBR_LIBRARIES MATCHES ".*\\.framework")
    set(LIBR_HOME "${LIBR_LIBRARIES}/Resources" CACHE PATH "R home directory")
    set(LIBR_INCLUDE_DIRS "${LIBR_HOME}/include" CACHE PATH "R include directory")
    set(LIBR_EXECUTABLE "${LIBR_HOME}/R" CACHE PATH "R executable")
    set(LIBR_LIB_DIR "${LIBR_HOME}/lib" CACHE PATH "R lib directory")
  else()
    get_filename_component(_LIBR_LIBRARIES "${LIBR_LIBRARIES}" REALPATH)
    get_filename_component(_LIBR_LIBRARIES_DIR "${_LIBR_LIBRARIES}" DIRECTORY)
    set(LIBR_EXECUTABLE "${_LIBR_LIBRARIES_DIR}/../bin/R")
    execute_process(
      COMMAND ${LIBR_EXECUTABLE} "--slave" "--vanilla" "-e" "cat(R.home())"
      OUTPUT_VARIABLE LIBR_HOME)
    set(LIBR_HOME ${LIBR_HOME} CACHE PATH "R home directory")
    set(LIBR_INCLUDE_DIRS "${LIBR_HOME}/include" CACHE PATH "R include directory")
    set(LIBR_LIB_DIR "${LIBR_HOME}/lib" CACHE PATH "R lib directory")
  endif()

# detection for UNIX & Win32
else()

  # attempt to find R executable
  if(NOT LIBR_EXECUTABLE)
    find_program(LIBR_EXECUTABLE NAMES R R.exe)
  endif()

  if(UNIX)

    if(NOT LIBR_EXECUTABLE)
      message(FATAL_ERROR "Unable to locate R executable.\
        \nEither add its location to PATH or provide it through the LIBR_EXECUTABLE cmake variable")
    endif()

    # ask R for the home path
    execute_process(
      COMMAND ${LIBR_EXECUTABLE} "--slave" "--vanilla" "-e" "cat(R.home())"
      OUTPUT_VARIABLE LIBR_HOME
    )
    # ask R for the include dir
    execute_process(
      COMMAND ${LIBR_EXECUTABLE} "--slave" "--vanilla" "-e" "cat(R.home('include'))"
      OUTPUT_VARIABLE LIBR_INCLUDE_DIRS
    )
    # ask R for the lib dir
    execute_process(
      COMMAND ${LIBR_EXECUTABLE} "--slave" "--vanilla" "-e" "cat(R.home('lib'))"
      OUTPUT_VARIABLE LIBR_LIB_DIR
    )

  # Windows
  else()
    # ask R for R_HOME
    if(LIBR_EXECUTABLE)
      execute_process(
        COMMAND ${LIBR_EXECUTABLE} "--slave" "--no-save" "-e" "cat(normalizePath(R.home(),winslash='/'))"
        OUTPUT_VARIABLE LIBR_HOME)
    endif()
    # if R executable not available, query R_HOME path from registry
    if(NOT LIBR_HOME)
      get_filename_component(LIBR_HOME
        "[HKEY_LOCAL_MACHINE\\SOFTWARE\\R-core\\R\\${R_VERSION};InstallPath]"
        ABSOLUTE)
      if(NOT LIBR_HOME)
        message(FATAL_ERROR "\nUnable to locate R executable.\
          \nEither add its location to PATH or provide it through the LIBR_EXECUTABLE cmake variable")
      endif()
    endif()
    # set exe location based on R_ARCH
    if(NOT LIBR_EXECUTABLE)
      set(LIBR_EXECUTABLE "${LIBR_HOME}/bin/${R_ARCH}/R.exe")
    endif()
    # set other R paths based on home path
    set(LIBR_INCLUDE_DIRS "${LIBR_HOME}/include")
    set(LIBR_LIB_DIR "${LIBR_HOME}/bin/${R_ARCH}")

message(STATUS "LIBR_HOME [${LIBR_HOME}]")
message(STATUS "LIBR_EXECUTABLE [${LIBR_EXECUTABLE}]")
message(STATUS "LIBR_INCLUDE_DIRS [${LIBR_INCLUDE_DIRS}]")
message(STATUS "LIBR_LIB_DIR [${LIBR_LIB_DIR}]")
message(STATUS "LIBR_CORE_LIBRARY [${LIBR_CORE_LIBRARY}]")

  endif()

endif()

if((WIN32 AND MSVC) OR (WIN32 AND MINGW))
  # create a local R.lib import library for R.dll if it doesn't exist
  if(NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/R.lib")
    create_rlib_for_msvc()
  endif()
endif()

# look for the core R library
find_library(LIBR_CORE_LIBRARY NAMES R
  HINTS "${CMAKE_CURRENT_BINARY_DIR}" "${LIBR_LIB_DIR}" "${LIBR_HOME}/bin" "${LIBR_LIBRARIES}")
if(LIBR_CORE_LIBRARY-NOTFOUND)
  message(STATUS "Could not find R core shared library.")
endif()

set(LIBR_HOME ${LIBR_HOME} CACHE PATH "R home directory")
set(LIBR_EXECUTABLE ${LIBR_EXECUTABLE} CACHE PATH "R executable")
set(LIBR_INCLUDE_DIRS ${LIBR_INCLUDE_DIRS} CACHE PATH "R include directory")
set(LIBR_LIB_DIR ${LIBR_LIB_DIR} CACHE PATH "R shared libraries directory")
set(LIBR_CORE_LIBRARY ${LIBR_CORE_LIBRARY} CACHE PATH "R core shared library")

# define find requirements
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(LibR DEFAULT_MSG
  LIBR_HOME
  LIBR_EXECUTABLE
  LIBR_INCLUDE_DIRS
  LIBR_LIB_DIR
  LIBR_CORE_LIBRARY
)

if(LIBR_FOUND)
  message(STATUS "Found R: ${LIBR_EXECUTABLE}")
endif()
