## ---------------------------------------------------------------------
##
## Copyright (C) 2012 - 2018 by the deal.II authors
##
## This file is part of the deal.II library.
##
## The deal.II library is free software; you can use it, redistribute
## it, and/or modify it under the terms of the GNU Lesser General
## Public License as published by the Free Software Foundation; either
## version 2.1 of the License, or (at your option) any later version.
## The full text of the license can be found in the file LICENSE.md at
## the top level directory of deal.II.
##
## ---------------------------------------------------------------------

#
# Try to find the petsc library
#
# This module exports:
#
#     PETSC_FOUND
#     PETSC_LIBRARIES
#     PETSC_INCLUDE_DIRS
#     PETSC_VERSION
#     PETSC_VERSION_MAJOR
#     PETSC_VERSION_MINOR
#     PETSC_VERSION_SUBMINOR
#     PETSC_VERSION_PATCH
#     PETSC_WITH_64BIT_INDICES
#     PETSC_WITH_COMPLEX
#     PETSC_WITH_HYPRE
#     PETSC_WITH_MPIUNI
#     PETSC_WITH_MUMPS
#

SET(PETSC_DIR "" CACHE PATH "An optional hint to a PETSc directory")
SET(PETSC_ARCH "" CACHE STRING "An optional hint to a PETSc arch")
SET_IF_EMPTY(PETSC_DIR "$ENV{PETSC_DIR}")
SET_IF_EMPTY(PETSC_ARCH "$ENV{PETSC_ARCH}")

DEAL_II_FIND_LIBRARY(PETSC_LIBRARY
  NAMES petsc libpetsc
  HINTS ${PETSC_DIR} ${PETSC_DIR}/${PETSC_ARCH}
  PATH_SUFFIXES lib${LIB_SUFFIX} lib64 lib
  )

#
# Search for the first part of the includes:
#

DEAL_II_FIND_PATH(PETSC_INCLUDE_DIR_ARCH petscconf.h
  HINTS ${PETSC_DIR} ${PETSC_DIR}/${PETSC_ARCH} ${PETSC_INCLUDE_DIRS}
  PATH_SUFFIXES petsc include include/petsc
)

SET(PETSC_PETSCCONF_H "${PETSC_INCLUDE_DIR_ARCH}/petscconf.h")

MACRO(_petsc_feature_check _var _regex)
  FILE(STRINGS "${PETSC_PETSCCONF_H}" PETSC_${_var}_STRING
    REGEX "${_regex}")
  IF("${PETSC_${_var}_STRING}" STREQUAL "")
    SET(PETSC_WITH_${_var} FALSE)
  ELSE()
    SET(PETSC_WITH_${_var} TRUE)
  ENDIF()
ENDMACRO()

IF(EXISTS ${PETSC_PETSCCONF_H})
  _petsc_feature_check(64BIT_INDICES "#define.*PETSC_USE_64BIT_INDICES 1")
  _petsc_feature_check(COMPLEX "#define.*PETSC_USE_COMPLEX 1")
  _petsc_feature_check(HYPRE "#define.*PETSC_HAVE_HYPRE 1")
  _petsc_feature_check(MPIUNI "#define.*PETSC_HAVE_MPIUNI 1")
  _petsc_feature_check(MUMPS "#define.*PETSC_HAVE_MUMPS 1")
ENDIF()

#
# Sometimes, this is not enough...
# If petsc is not installed but in source tree layout, there will be
#   ${PETSC_DIR}/${PETSC_ARCH}/include - which we should have found by now.
#   ${PETSC_DIR}/include               - which we still have to find.
#
# Or it is installed in a non standard layout in the system (e.g. in
# Gentoo), where there will be
#   ${PETSC_DIR}/${PETSC_ARCH}/include
#   /usr/include/petsc ...
#
# Either way, we must be able to find petscversion.h:
#

DEAL_II_FIND_PATH(PETSC_INCLUDE_DIR_COMMON petscversion.h
  HINTS ${PETSC_DIR} ${PETSC_DIR}/${PETSC_ARCH} ${PETSC_INCLUDE_DIRS}
  PATH_SUFFIXES petsc include include/petsc
)

SET(PETSC_PETSCVERSION_H "${PETSC_INCLUDE_DIR_COMMON}/petscversion.h")
IF(EXISTS ${PETSC_PETSCVERSION_H})
  FILE(STRINGS "${PETSC_PETSCVERSION_H}" PETSC_VERSION_MAJOR_STRING
    REGEX "^#[ \t]*define[ \t]+PETSC_VERSION_MAJOR[ \t]+[0-9]+[ \t]*$")
  STRING(REGEX REPLACE "^#[ \t]*define[ \t]+PETSC_VERSION_MAJOR[ \t]+([0-9]+)[ \t]*$" "\\1"
    PETSC_VERSION_MAJOR "${PETSC_VERSION_MAJOR_STRING}"
    )
  FILE(STRINGS "${PETSC_PETSCVERSION_H}" PETSC_VERSION_MINOR_STRING
    REGEX "^#[ \t]*define[ \t]+PETSC_VERSION_MINOR[ \t]+[0-9]+[ \t]*$")
  STRING(REGEX REPLACE "^#[ \t]*define[ \t]+PETSC_VERSION_MINOR[ \t]+([0-9]+)[ \t]*$" "\\1"
    PETSC_VERSION_MINOR "${PETSC_VERSION_MINOR_STRING}"
    )
  FILE(STRINGS "${PETSC_PETSCVERSION_H}" PETSC_VERSION_SUBMINOR_STRING
    REGEX "^#[ \t]*define[ \t]+PETSC_VERSION_SUBMINOR[ \t]+[0-9]+[ \t]*$")
  STRING(REGEX REPLACE "^#[ \t]*define[ \t]+PETSC_VERSION_SUBMINOR[ \t]+([0-9]+)[ \t]*$" "\\1"
    PETSC_VERSION_SUBMINOR "${PETSC_VERSION_SUBMINOR_STRING}"
    )
  FILE(STRINGS "${PETSC_PETSCVERSION_H}" PETSC_VERSION_PATCH_STRING
    REGEX "^#[ \t]*define[ \t]+PETSC_VERSION_PATCH[ \t]+[0-9]+[ \t]*$")
  STRING(REGEX REPLACE "^#[ \t]*define[ \t]+PETSC_VERSION_PATCH[ \t]+([0-9]+)[ \t]*$" "\\1"
    PETSC_VERSION_PATCH "${PETSC_VERSION_PATCH_STRING}"
    )
  SET(PETSC_VERSION
    "${PETSC_VERSION_MAJOR}.${PETSC_VERSION_MINOR}.${PETSC_VERSION_SUBMINOR}.${PETSC_VERSION_PATCH}"
    )
ENDIF()

#
# So, up to this point it was easy. Now, the tricky part. Search for
# petscvariables and determine the includes and the link interface from
# that file:
#

DEAL_II_FIND_FILE(PETSC_PETSCVARIABLES
  NAMES petscvariables
  HINTS ${PETSC_DIR}/${PETSC_ARCH} ${PETSC_DIR}
  PATH_SUFFIXES conf lib/petsc/conf
  )

IF(NOT PETSC_PETSCVARIABLES MATCHES "-NOTFOUND")
  #
  # Includes:
  #

  FILE(STRINGS "${PETSC_PETSCVARIABLES}" _external_includes
    REGEX "^PETSC_CC_INCLUDES =.*")
  SEPARATE_ARGUMENTS(_external_includes)

  SET(_petsc_includes)
  FOREACH(_token ${_external_includes})
    #
    # workaround: Do not pull in scotch include directory. It clashes with
    # our use of the metis headers...
    #
    IF(_token MATCHES "^-I" AND NOT _token MATCHES "scotch$")
      STRING(REGEX REPLACE "^-I" "" _token "${_token}")
      LIST(APPEND _petsc_includes ${_token})
    ENDIF()
  ENDFOREACH()

  #
  # Link line:
  #

  FILE(STRINGS "${PETSC_PETSCVARIABLES}" PETSC_EXTERNAL_LINK_LINE
    REGEX "^PETSC_WITH_EXTERNAL_LIB =.*")

  SEPARATE_ARGUMENTS(PETSC_EXTERNAL_LINK_LINE)

  SET(_hints)
  SET(_petsc_libraries)
  SET(_cleanup_variables)
  FOREACH(_token ${PETSC_EXTERNAL_LINK_LINE})
    IF(_token MATCHES "^-L")
      # Build up hints with the help of all tokens passed with -L:
      STRING(REGEX REPLACE "^-L" "" _token "${_token}")
      LIST(APPEND _hints ${_token})
    ELSEIF(_token MATCHES "^-l")
      # Search for every library that was specified with -l:
      STRING(REGEX REPLACE "^-l" "" _token "${_token}")

      IF(NOT _token MATCHES "(petsc|stdc\\+\\+|gcc_s|clang_rt)")
        LIST(APPEND _cleanup_variables PETSC_LIBRARY_${_token})

        IF(_token MATCHES "^(c|quadmath|gfortran|m|rt|nsl|dl|pthread)$")
          FIND_SYSTEM_LIBRARY(PETSC_LIBRARY_${_token} NAMES ${_token})
        ELSE()
          DEAL_II_FIND_LIBRARY(PETSC_LIBRARY_${_token}
            NAMES ${_token}
            HINTS ${_hints}
            )
        ENDIF()
        IF(NOT PETSC_LIBRARY_${_token} MATCHES "-NOTFOUND")
          LIST(APPEND _petsc_libraries ${PETSC_LIBRARY_${_token}})
        ENDIF()

      ENDIF()

    ENDIF()
  ENDFOREACH()
ENDIF()

IF(PETSC_WITH_MPIUNI)
  #
  # Workaround: Some distributions happen to not install petscvariables and
  # we consequently might miss some essential include directories. Let's
  # try at least to find the mpiuni include directory.
  #
  DEAL_II_FIND_PATH(PETSC_INCLUDE_DIR_MPIUNI mpiuni/mpi.h
    HINTS ${PETSC_INCLUDE_DIR_COMMON} ${PETSC_INCLUDE_DIR_ARCH} ${_petsc_includes}
    PATH_SUFFIXES petsc
    )
  SET(PETSC_INCLUDE_DIR_MPIUNI "${PETSC_INCLUDE_DIR_MPIUNI}/mpiuni")
ENDIF()

DEAL_II_PACKAGE_HANDLE(PETSC
  LIBRARIES
    REQUIRED PETSC_LIBRARY
    OPTIONAL _petsc_libraries
  INCLUDE_DIRS
    REQUIRED PETSC_INCLUDE_DIR_COMMON PETSC_INCLUDE_DIR_ARCH
    OPTIONAL PETSC_INCLUDE_DIR_MPIUNI _petsc_includes
  USER_INCLUDE_DIRS
    REQUIRED PETSC_INCLUDE_DIR_COMMON PETSC_INCLUDE_DIR_ARCH
    OPTIONAL PETSC_INCLUDE_DIR_MPIUNI _petsc_includes
  CLEAR
    PETSC_LIBRARY PETSC_INCLUDE_DIR_COMMON PETSC_INCLUDE_DIR_ARCH
    PETSC_PETSCVARIABLES ${_cleanup_variables}
  )

IF(PETSC_FOUND)
  MARK_AS_ADVANCED(PETSC_ARCH)
ELSE()
  MARK_AS_ADVANCED(CLEAR PETSC_ARCH)
ENDIF()
