#
# @copyright (c) 2019-2020 Inria. All rights reserved.
#
# Marc Fuentes
# Florent Pruvost
#
# https://gitlab.inria.fr/sed-bso/findPetsc
#
# INPUT
# -----
#
# PETSC is not installed in a standard way on Unix systems so that
# this module requires hints to know where PETSC is installed. Please
# give the installation directory (contains ./include/petsc.h, ./lib/, etc):
# 1. by setting the PETSC_DIR variable
#    a. as an environment variable, e.g.
#       $ export PETSC_DIR=/usr/lib/petscdir/3.6.2/x86_64-linux-gnu-real
#    b. or as an CMake variable, e.g.
#       $ cmake .. -DPETSC_DIR==/usr/lib/petscdir/3.6.2/x86_64-linux-gnu-real
# 2. or by using the pkg-config mechanism, e.g.
#    $ export PKG_CONFIG_PATH=/usr/lib/petscdir/3.6.2/x86_64-linux-gnu-real/lib/pkgconfig:$PKG_CONFIG_PATH
#
# OUTPUT
# -------
# PETSC_INCLUDE_DIRS - the PETSC include directories
# PETSC_LIBRARIES    - Link these to use PETSC
# PETSC_LIBRARY_DIRS - Link these to use PETSC
# PETSC_MPIEXEC - Executable for running MPI programs
#
# if pkg-config is used i.e. pkgconfig installed, PETSC.pc file path
# in the PKG_CONFIG_PATH environment variable and PETSC_DIR not set
# then the following variables are set (or empty)
#
# PETSC_VERSION           ... version of the module
# PETSC_PREFIX            ... prefix-directory of the module
# PETSC_INCLUDEDIR        ... include-dir of the module
# PETSC_LIBDIR            ... lib-dir of the module
#
# <PREFIX>_FOUND          ... set to 1 if module(s) exist
# <PREFIX>_LIBRARIES      ... only the libraries (w/o the '-l')
# <PREFIX>_LIBRARY_DIRS   ... the paths of the libraries (w/o the '-L')
# <PREFIX>_LDFLAGS        ... all required linker flags
# <PREFIX>_LDFLAGS_OTHER  ... all other linker flags
# <PREFIX>_INCLUDE_DIRS   ... the '-I' preprocessor flags (w/o the '-I')
# <PREFIX>_CFLAGS         ... all required cflags
# <PREFIX>_CFLAGS_OTHER   ... the other compiler flags
#
# <PREFIX> = PETSC        for common case
# <PREFIX> = PETSC_STATIC for static linking
#
# PETSC_FOUND_WITH_PKGCONFIG - True if found with pkg-config
#
# This module defines the following :prop_tgt:`IMPORTED` target:
#
# ``MORSE::PETSC``
#   The headers and libraries to use for PETSC, if found.
#
# find_package(PETSC [QUIET] [REQUIRED])
#
# Setting these changes the behavior of the search
# PETSC_DIR - directory in which PETSC is installed
# PETSC_ARCH - build architecture

# Common macros to use in finds
include(FindMorseInit)

# create a cmake cache variable
set(PETSC_DIR "" CACHE PATH "Installation directory of PETSC library")
if (NOT PETSC_FIND_QUIETLY AND NOT PETSC_DIR)
  message(STATUS "A cache variable, namely PETSC_DIR, has been set
  to specify a custom installation directory of PETSC")
endif()

# Use pkg-config to detect include/library dirs (if pkg-config is available)
# -------------------------------------------------------------------------------------
if( PKG_CONFIG_EXECUTABLE AND NOT PETSC_DIR )
  pkg_search_module(PETSC PETSc)
  if (NOT PETSC_FIND_QUIETLY)
    if (PETSC_FOUND AND PETSC_LIBRARIES)
      message(STATUS "Looking for PETSC - found using PkgConfig")
    else()
      message(STATUS "Looking for PETSC - not found using PkgConfig."
        "\n   Perhaps you should add the directory containing PETSC.pc to"
        "\n   the PKG_CONFIG_PATH environment variable.")
    endif()
  endif()
  set(PETSC_DIR "${PETSC_PREFIX}")
  if (PETSC_FOUND AND PETSC_LIBRARIES)
    if (NOT PETSC_INCLUDE_DIRS)
      pkg_get_variable(PETSC_INCLUDE_DIRS PETSc includedir)
    endif()
    set(PETSC_FOUND_WITH_PKGCONFIG "TRUE")
    morse_find_pkgconfig_libraries_absolute_path(PETSC)
  else()
    set(PETSC_FOUND_WITH_PKGCONFIG "FALSE")
  endif()
  if (PETSC_STATIC AND PETSC_STATIC_LIBRARIES)
    set (PETSC_DEPENDENCIES ${PETSC_STATIC_LIBRARIES})
    list (REMOVE_ITEM SLEPC_DEPENDENCIES "petsc")
    list (APPEND PETSC_LIBRARIES ${PETSC_DEPENDENCIES})
    if (NOT PETSC_FIND_QUIETLY)
      message(STATUS "PETSC_STATIC set to 1 by user, PETSC_LIBRARIES: ${PETSC_LIBRARIES}.")
    endif()
  endif()
endif()

# consider using the env. var. PETSC_DIR if not directly given through the CMake cache var.
if (NOT PETSC_DIR AND DEFINED ENV{PETSC_DIR})
  set(PETSC_DIR "$ENV{PETSC_DIR}")
endif()

if (PETSC_DIR)
    if (EXISTS ${PETSC_DIR})
      if (EXISTS ${PETSC_DIR}/include/petsc.h)
          if (NOT PETSC_FIND_QUIETLY)
            message(STATUS "PETSC_DIR = ${PETSC_DIR} contains include/petsc.h")
          endif()
        else()
          if (PETSC_FIND_REQUIRED)
            message(FATAL_ERROR "include/petsc.h not found in PETSC_DIR = ${PETSC_DIR}")
          endif()
        endif()
      else()
        if (PETSC_FIND_REQUIRED)
          message(FATAL_ERROR "PETSC_DIR defined, but ${PETSC_DIR} does not exist")
        endif()
    endif()
else()
  if (PETSC_FIND_REQUIRED)
    message(FATAL_ERROR "\
PETSC is not installed in a standard way on Unix systems so that
this module requires hints to know where PETSC is installed. Please
give the installation directory (contains ./include/petsc.h, ./lib/, etc):
1. by setting the PETSC_DIR variable
   a. as an environment variable, e.g.
      $ export PETSC_DIR=/usr/lib/petscdir/3.6.2/x86_64-linux-gnu-real
   b. or as an CMake variable, e.g.
      $ cmake .. -DPETSC_DIR==/usr/lib/petscdir/3.6.2/x86_64-linux-gnu-real
2. or by using the pkg-config mechanism, e.g.
   $ export PKG_CONFIG_PATH=/usr/lib/petscdir/3.6.2/x86_64-linux-gnu-real/lib/pkgconfig:$PKG_CONFIG_PATH\
    ")
  endif()
endif()

find_file(petscconf NAMES petscconf.h HINTS ${PETSC_DIR}/include)
if (petscconf)
  if (NOT PETSC_FIND_QUIETLY)
    message(STATUS "petscconf.h is located in ${petscconf}")
  endif()
else()
  if (PETSC_FIND_REQUIRED)
    message(FATAL_ERROR "petscconf.h not found
    we must find it into PETSC_DIR/include/")
  endif()
endif()

if (NOT PETSC_ARCH)
  if (petscconf)
    file(READ ${petscconf} contents)
    string(REGEX MATCH "#define *PETSC_ARCH *\"[a-z0-9-]*\" *\n" foundArch ${contents})
    if (foundArch)
      string(REGEX REPLACE "#define *PETSC_ARCH *\"([a-z0-9-]*)\" *\n" "\\1" archFromFile ${foundArch})
      if (NOT PETSC_FIND_QUIETLY)
        message(STATUS "PETSC_ARCH taken from petscconf.h = ${archFromFile}")
      endif()
      if(archFromFile)
        set(PETSC_ARCH ${archFromFile})
      endif(archFromFile)
    endif()
  endif(petscconf)
endif (NOT PETSC_ARCH)

find_file(petscvariables NAMES petscvariables HINTS ${PETSC_DIR}/lib/petsc/conf/ ${PETSC_DIR}/conf)
if (petscvariables)
  if (NOT PETSC_FIND_QUIETLY)
    message(STATUS "petscvariables = ${petscvariables}")
  endif()
else()
  if (PETSC_FIND_REQUIRED)
    message(FATAL_ERROR "petscvariables not found")
  endif()
endif()

# function extracting the value of variable defined in a file under the form
# NAME_VAR = VARIABLE_CONTENT
# note that VARIABLE_CONTENT is assumed not to contain ";"
function(get_variable var filename out)
    if (NOT EXISTS ${filename})
        message(FATAL_ERROR "${filename} does not exist")
    endif()
    file(READ ${filename} contents)
    string(REGEX REPLACE "\n" ";" contents "${contents}") # we divide line by lines because it did work without doing that
    foreach(Line ${contents})
        string(REGEX MATCHALL "^${var} = .*" foundVar ${Line})
        if (foundVar)
            string(REGEX REPLACE "^${var} = (.+)" "\\1" value ${foundVar})
            break()
        endif()
    endforeach()
    if(foundVar)
      set(${out} "${value}" PARENT_SCOPE)
    else()
        message(FATAL_ERROR "${var} not found in ${filename}")
    endif()
endfunction(get_variable)

get_variable(PETSC_WITH_EXTERNAL_LIB ${petscvariables} PETSC_LIBRARIES_RAW)
get_variable(MPIEXEC ${petscvariables} PETSC_MPIEXEC)
get_variable(PETSC_CC_INCLUDES ${petscvariables}  PETSC_INCLUDES_RAW)

# extract include paths from petscvariables
string(REGEX REPLACE "-I" "" PETSC_INCLUDES_RAW ${PETSC_INCLUDES_RAW} )
string(REGEX REPLACE " " ";" PETSC_INCLUDE_DIRS ${PETSC_INCLUDES_RAW} )

# extract library directories  from petscvariables
string(REGEX MATCHALL "-L([^ ]+) " foundLibPaths ${PETSC_LIBRARIES_RAW})
string(REGEX REPLACE "-L" "" PETSC_LIBRARY_DIRS ${foundLibPaths})
string(REGEX REPLACE " " ";" PETSC_LIBRARY_DIRS ${PETSC_LIBRARY_DIRS})

# extract libraries  from petscvariables
string(REGEX MATCHALL " -l([^ ]+) " foundLibs ${PETSC_LIBRARIES_RAW})
string(REGEX REPLACE " -l" "" PETSC_LIBRARIES ${foundLibs})
string(REGEX REPLACE " " ";" PETSC_LIBRARIES ${PETSC_LIBRARIES})
libraries_absolute_path(PETSC_LIBRARIES "${PETSC_LIBRARY_DIRS}")

include (FindPackageHandleStandardArgs)
find_package_handle_standard_args (PETSC
  "PETSC could not be found. Be sure to set PETSC_DIR."
  PETSC_MPIEXEC PETSC_INCLUDE_DIRS PETSC_LIBRARIES PETSC_LIBRARY_DIRS)

# Add imported target
if (PETSC_FOUND)
  morse_create_imported_target(PETSC)
endif()