## Copyright (C) 2019-2021 ABINIT group (Yann Pouillon)

#
# Exchange-Correlation functionals library (LibXC)
#


                    # ------------------------------------ #


#
# Public macros
#


AC_DEFUN([SD_LIBXC_INIT], [
  # Init
  sd_libxc_cppflags=""
  sd_libxc_cflags=""
  sd_libxc_cxxflags=""
  sd_libxc_fcflags=""
  sd_libxc_ldflags=""
  sd_libxc_libs=""
  sd_libxc_enable=""
  sd_libxc_init="unknown"
  sd_libxc_c_ok="unknown"
  sd_libxc_fortran_ok="unknown"
  sd_libxc_ok="unknown"

  # Set adjustable parameters
  sd_libxc_options="$1"
  sd_libxc_libs_def="$2"
  sd_libxc_cppflags_def="$3"
  sd_libxc_cflags_def="$4"
  sd_libxc_cxxflags_def="$5"
  sd_libxc_fcflags_def="$6"
  sd_libxc_ldflags_def="$7"
  sd_libxc_enable_def=""
  sd_libxc_enable_fc=""
  sd_libxc_policy=""
  sd_libxc_status=""

  # Process options
  for kwd in ${sd_libxc_options}; do
    case "${kwd}" in
      auto)
        sd_libxc_enable_def="${kwd}"
        ;;
      implicit|required|optional)
        sd_libxc_status="${kwd}"
        ;;
      fail|skip|warn)
        sd_libxc_policy="${kwd}"
        ;;
      no-fortran)
        sd_libxc_enable_fc="no"
        ;;
      *)
        AC_MSG_ERROR([invalid Steredeg LibXC option: '${kwd}'])
        ;;
    esac
  done

  # Set reasonable defaults if not provided
  test -z "${sd_libxc_enable_fc}" && sd_libxc_enable_fc="yes"
  if test "${sd_libxc_enable_fc}" = "yes"; then
    test -z "${sd_libxc_libs_def}" && sd_libxc_libs_def="-lxcf90 -lxc"
  else
    test -z "${sd_libxc_libs_def}" && sd_libxc_libs_def="-lxc"
  fi
  test -z "${sd_libxc_policy}" && sd_libxc_policy="fail"
  test -z "${sd_libxc_status}" && sd_libxc_status="optional"
  test -z "${sd_libxc_enable_def}" && sd_libxc_enable_def="no"
  case "${sd_libxc_status}" in
    implicit|required)
      sd_libxc_enable_def="yes"
      ;;
  esac

  # Declare configure option
  # TODO: make it switchable for the implicit case 
  AC_ARG_WITH([libxc],
    [AS_HELP_STRING([--with-libxc],
      [Install prefix of the LibXC library (e.g. /usr/local).])],
    [ if test "${withval}" = "no" -o "${withval}" = "yes"; then
        sd_libxc_enable="${withval}"
        sd_libxc_init="yon"
      else
        sd_libxc_enable="yes"
        sd_libxc_init="dir"
      fi],
    [ sd_libxc_enable="${sd_libxc_enable_def}"; sd_libxc_init="def"])

  # Declare environment variables
  AC_ARG_VAR([LIBXC_CPPFLAGS], [C preprocessing flags for LibXC.])
  AC_ARG_VAR([LIBXC_CFLAGS], [C flags for LibXC.])
  AC_ARG_VAR([LIBXC_FCFLAGS], [Fortran flags for LibXC.])
  AC_ARG_VAR([LIBXC_LDFLAGS], [Linker flags for LibXC.])
  AC_ARG_VAR([LIBXC_LIBS], [Library flags for LibXC.])

  # Detect use of environment variables
  if test "${sd_libxc_enable}" = "yes" -o "${sd_libxc_enable}" = "auto"; then
    tmp_libxc_vars="${LIBXC_CPPFLAGS}${LIBXC_CFLAGS}${LIBXC_LDFLAGS}${LIBXC_LIBS}"
    if test "${sd_libxc_enable_fc}" = "yes"; then
      tmp_libxc_vars="${tmp_libxc_vars}${LIBXC_FCFLAGS}"
    fi
    if test "${sd_libxc_init}" = "def" -a ! -z "${tmp_libxc_vars}"; then
      sd_libxc_enable="yes"
      sd_libxc_init="env"
    fi
  fi

  # Make sure configuration is correct
  if test "${STEREDEG_BYPASS_CONSISTENCY}" != "yes"; then
    _SD_LIBXC_CHECK_CONFIG
  fi
  # Adjust configuration depending on init type
  if test "${sd_libxc_enable}" = "yes" -o "${sd_libxc_enable}" = "auto"; then

    # Set LibXC-specific flags
    case "${sd_libxc_init}" in

      def|yon)
        sd_libxc_cppflags="${sd_libxc_cppflags_def}"
        sd_libxc_cflags="${sd_libxc_cflags_def}"
        test "${sd_libxc_enable_fc}" = "yes" && \
          sd_libxc_fcflags="${sd_libxc_fcflags_def}"
        sd_libxc_ldflags="${sd_libxc_ldflags_def}"
        sd_libxc_libs="${sd_libxc_libs_def}"
        ;;

      dir)
        sd_libxc_cppflags="-I${with_libxc}/include"
        sd_libxc_cflags="${sd_libxc_cflags_def}"
        test "${sd_libxc_enable_fc}" = "yes" && \
          sd_libxc_fcflags="${sd_libxc_fcflags_def} -I${with_libxc}/include"
        sd_libxc_ldflags="${sd_libxc_ldflags_def}"
        sd_libxc_libs="-L${with_libxc}/lib ${sd_libxc_libs_def}"
        ;;

      env)
        sd_libxc_cppflags="${sd_libxc_cppflags_def}"
        sd_libxc_cflags="${sd_libxc_cflags_def}"
        test "${sd_libxc_enable_fc}" = "yes" && \
          sd_libxc_fcflags="${sd_libxc_fcflags_def}"
        sd_libxc_ldflags="${sd_libxc_ldflags_def}"
        sd_libxc_libs="${sd_libxc_libs_def}"
        test ! -z "${LIBXC_CPPFLAGS}" && sd_libxc_cppflags="${LIBXC_CPPFLAGS}"
        test ! -z "${LIBXC_CFLAGS}" && sd_libxc_cflags="${LIBXC_CFLAGS}"
        if test "${sd_libxc_enable_fc}" = "yes"; then
          test ! -z "${LIBXC_FCFLAGS}" && sd_libxc_fcflags="${LIBXC_FCFLAGS}"
        fi
        test ! -z "${LIBXC_LDFLAGS}" && sd_libxc_ldflags="${LIBXC_LDFLAGS}"
        test ! -z "${LIBXC_LIBS}" && sd_libxc_libs="${LIBXC_LIBS}"
        ;;

      *)
        AC_MSG_ERROR([invalid init type for LibXC: '${sd_libxc_init}'])
        ;;

    esac

  fi

  # Override the default configuration if the ESL Bundle is available
  # Note: The setting of the build flags is done once for all
  #       ESL-bundled packages
  if test "${sd_libxc_init}" = "def" -a ! -z "${ESL_BUNDLE_PREFIX}"; then
    sd_libxc_init="esl"
    sd_libxc_cppflags=""
    sd_libxc_cflags=""
    sd_libxc_fcflags=""
    sd_libxc_ldflags=""
    sd_libxc_libs=""
  fi

  # Display configuration
  _SD_LIBXC_DUMP_CONFIG

  # Export configuration
  AC_SUBST(sd_libxc_options)
  AC_SUBST(sd_libxc_enable_def)
  AC_SUBST(sd_libxc_enable_fc)
  AC_SUBST(sd_libxc_policy)
  AC_SUBST(sd_libxc_status)
  AC_SUBST(sd_libxc_enable)
  AC_SUBST(sd_libxc_init)
  AC_SUBST(sd_libxc_ok)
  AC_SUBST(sd_libxc_cppflags)
  AC_SUBST(sd_libxc_cflags)
  AC_SUBST(sd_libxc_fcflags)
  AC_SUBST(sd_libxc_ldflags)
  AC_SUBST(sd_libxc_libs)
  AC_SUBST(with_libxc)

  # Clean-up
  unset tmp_libxc_vars
]) # SD_LIBXC_INIT


AC_DEFUN([SD_LIBXC_DETECT], [
  # Display configuration
  _SD_LIBXC_DUMP_CONFIG

  # Check whether we can compile and link a simple program
  # and update build flags if successful
  if test "${sd_libxc_enable}" = "auto" -o "${sd_libxc_enable}" = "yes"; then
    _SD_LIBXC_CHECK_USE

    if test "${sd_libxc_ok}" = "yes"; then
      if test "${sd_libxc_init}" = "esl"; then
        sd_esl_bundle_libs="${sd_libxc_libs_def} ${sd_esl_bundle_libs}"
      else
        FCFLAGS="${FCFLAGS} ${sd_libxc_fcflags}"
        LIBS="${sd_libxc_libs} ${LIBS}"
      fi
      LDFLAGS="${LDFLAGS} ${sd_libxc_ldflags}"

      AC_DEFINE([HAVE_LIBXC], 1,
        [Define to 1 if you have the LibXC library.])
    else
      if test "${sd_libxc_status}" = "optional" -a \
              "${sd_libxc_init}" = "def"; then
        sd_libxc_enable="no"
        sd_libxc_cppflags=""
        sd_libxc_cflags=""
        sd_libxc_fcflags=""
        sd_libxc_ldflags=""
        sd_libxc_libs=""
      else
        AC_MSG_FAILURE([invalid LibXC configuration])
      fi
    fi
  fi
])


                    # ------------------------------------ #


#
# Private macros
#


AC_DEFUN([_SD_LIBXC_CHECK_USE], [
  # Prepare environment
  SD_ESL_SAVE_FLAGS
  if test "${sd_libxc_init}" = "esl"; then
    AC_MSG_NOTICE([will look for LibXC in the installed ESL Bundle])
    SD_ESL_ADD_FLAGS
    SD_ESL_ADD_LIBS([${sd_libxc_libs_def}])
  else
    CPPFLAGS="${CPPFLAGS} ${sd_libxc_cppflags}"
    CFLAGS="${CFLAGS} ${sd_libxc_cflags}"
    FCFLAGS="${FCFLAGS} ${sd_libxc_fcflags}"
    LDFLAGS="${LDFLAGS} ${sd_libxc_ldflags}"
    LIBS="${sd_libxc_libs} ${LIBS}"
  fi

  # Check LibXC C API
  AC_MSG_CHECKING([whether the LibXC library works])
  AC_LANG_PUSH([C])
  AC_LINK_IFELSE([AC_LANG_PROGRAM(
    [[
#include <xc.h>
    ]],
    [[
      int ma, mi, mu;
      xc_version(&ma, &mi, &mu);
    ]])], [sd_libxc_c_ok="yes"], [sd_libxc_c_ok="no"])
  AC_LANG_POP([C])
  AC_MSG_RESULT([${sd_libxc_c_ok}])

  # Check old LibXC C API
  if test "${sd_libxc_c_ok}" != "yes"; then
    AC_MSG_CHECKING([whether the LibXC library has an old API])
    AC_LANG_PUSH([C])
    AC_LINK_IFELSE([AC_LANG_PROGRAM(
      [[
#include <xc.h>
      ]],
      [[
        int ma, mi;
        xc_version(&ma, &mi);
      ]])], [sd_libxc_c_ok="yes"], [sd_libxc_c_ok="no"])
    AC_LANG_POP([C])
    AC_MSG_RESULT([${sd_libxc_c_ok}])
  fi

  # Check LibXC Fortran API
  if test "${sd_libxc_c_ok}" = "yes" -a "${sd_libxc_enable_fc}" = "yes"; then
    AC_MSG_CHECKING([whether the LibXC Fortran interface works])
    for tmp_incs in "" "-I/usr/include"; do
      FCFLAGS="${FCFLAGS} ${tmp_incs}"
      AC_LANG_PUSH([Fortran])
      AC_LINK_IFELSE([AC_LANG_PROGRAM([],
        [[
          use libxc_m
          implicit none
          integer :: ma, mi, mu
          call xc_f90_version(ma, mi, mu)
        ]])], [sd_libxc_fortran_ok="yes"], [sd_libxc_fortran_ok="no"])
      AC_LANG_POP([Fortran])
      if test "${sd_libxc_fortran_ok}" = "yes"; then
        test "${sd_fc_sys_incpath}" = "" && sd_fc_sys_incpath="${tmp_incs}"
        break
      fi
    done
    AC_MSG_RESULT([${sd_libxc_fortran_ok}])
  fi
  unset tmp_incs

  # Combine the available results
  sd_libxc_ok="no"
  if test "${sd_libxc_enable_fc}" = "yes"; then
    if test "${sd_libxc_c_ok}" = "yes" -a \
            "${sd_libxc_fortran_ok}" = "yes"; then
      sd_libxc_ok="yes"
    fi
  else
    if test "${sd_libxc_c_ok}" = "yes"; then
      sd_libxc_ok="yes"
    fi
  fi

  # Restore environment
  SD_ESL_RESTORE_FLAGS
]) # _SD_LIBXC_CHECK_USE


                    # ------------------------------------ #
                    # ------------------------------------ #


#
# Utility macros
#


AC_DEFUN([_SD_LIBXC_CHECK_CONFIG], [
  # Default trigger must be yes, no, or auto
  tmp_libxc_invalid="no"
  if test "${sd_libxc_enable_def}" != "auto" -a \
          "${sd_libxc_enable_def}" != "no" -a \
          "${sd_libxc_enable_def}" != "yes"; then
    case "${sd_libxc_policy}" in
      fail)
        AC_MSG_ERROR([invalid default value: sd_libxc_enable_def = '${sd_libxc_enable_def}'])
        ;;
      skip)
        tmp_libxc_invalid="yes"
        ;;
      warn)
        AC_MSG_WARN([invalid default value: sd_libxc_enable_def = '${sd_libxc_enable_def}'])
        tmp_libxc_invalid="yes"
        ;;
    esac
  fi

  # Fix wrong trigger default
  if test "${tmp_libxc_invalid}" = "yes"; then
    if test "${sd_libxc_status}" = "required"; then
      sd_libxc_enable_def="yes"
    else
      sd_libxc_enable_def="no"
    fi
    tmp_libxc_invalid="no"
    AC_MSG_NOTICE([setting sd_libxc_enable_def to '${sd_libxc_enable_def}'])
  fi

  # Check consistency between trigger value and package status
  tmp_libxc_invalid="no"
  if test "${sd_libxc_status}" = "implicit" -o \
          "${sd_libxc_status}" = "required"; then
    if test "${sd_libxc_enable}" = "no"; then
      case "${sd_libxc_policy}" in
        fail)
          AC_MSG_ERROR([The LibXC package is required and cannot be disabled
                  See https://tddft.org/programs/libxc/ for details on how to
                  install it.])
          ;;
        skip)
          tmp_libxc_invalid="yes"
          ;;
        warn)
          AC_MSG_WARN([The LibXC package is required and cannot be disabled])
          tmp_libxc_invalid="yes"
          ;;
      esac
    fi
    if test "${sd_libxc_enable}" = "auto"; then
      AC_MSG_NOTICE([setting LibXC trigger to yes])
      sd_libxc_enable="yes"
    fi
  fi

  # Fix wrong trigger value
  if test "${tmp_libxc_invalid}" = "yes"; then
    case "${sd_libxc_status}" in
      implicit|required)
        sd_libxc_enable="yes"
        ;;
      optional)
        sd_libxc_enable="no"
        ;;
    esac
    tmp_libxc_invalid="no"
    AC_MSG_NOTICE([setting sd_libxc_enable to '${sd_libxc_enable}'])
  fi

  # Environment variables conflict with --with-* options
  tmp_libxc_vars="${LIBXC_CPPFLAGS}${LIBXC_CFLAGS}${LIBXC_FCFLAGS}${LIBXC_LDFLAGS}${LIBXC_LIBS}"
  tmp_libxc_invalid="no"
  if test ! -z "${tmp_libxc_vars}" -a ! -z "${with_libxc}"; then
    case "${sd_libxc_policy}" in
      fail)
        AC_MSG_ERROR([conflicting option settings for LibXC
                  Please use LIBXC_FCFLAGS + LIBXC_LIBS or --with-libxc,
                  not both.])
        ;;
      skip)
        tmp_libxc_invalid="yes"
        ;;
      warn)
        AC_MSG_WARN([conflicting option settings for LibXC])
        tmp_libxc_invalid="yes"
        ;;
    esac
  fi

  # When using environment variables, triggers must be set to yes
  if test -n "${tmp_libxc_vars}"; then
    sd_libxc_enable="yes"
    sd_libxc_init="env"
    if test "${tmp_libxc_invalid}" = "yes"; then
      tmp_libxc_invalid="no"
      AC_MSG_NOTICE([overriding --with-libxc with LIBXC_{FCFLAGS,LDFLAGS,LIBS}])
    fi
  fi

  # Implicit status overrides everything
  if test "${sd_libxc_status}" = "implicit"; then
    if test "${sd_libxc_fcflags}" != ""; then
      sd_libxc_fcflags=""
      AC_MSG_NOTICE([resetting LibXC Fortran flags (implicit package)])
    fi
    if test "${sd_libxc_ldflags}" != ""; then
      sd_libxc_ldflags=""
      AC_MSG_NOTICE([resetting LibXC linker flags (implicit package)])
    fi
    if test "${sd_libxc_libs}" != ""; then
      sd_libxc_libs=""
      AC_MSG_NOTICE([resetting LibXC library flags (implicit package)])
    fi
  fi

  # Reset build parameters if disabled
  if test "${sd_libxc_enable}" = "implicit"; then
    sd_libxc_fcflags=""
    sd_libxc_ldflags=""
    sd_libxc_libs=""
  fi

  # Clean-up
  unset tmp_libxc_invalid
  unset tmp_libxc_vars
]) # _SD_LIBXC_CHECK_CONFIG


AC_DEFUN([_SD_LIBXC_DUMP_CONFIG], [
  AC_MSG_CHECKING([whether to enable LibXC])
  AC_MSG_RESULT([${sd_libxc_enable}])
  if test "${sd_libxc_enable}" != "no"; then
    AC_MSG_CHECKING([how LibXC parameters have been set])
    AC_MSG_RESULT([${sd_libxc_init}])
    AC_MSG_CHECKING([for LibXC C preprocessing flags])
    if test "${sd_libxc_cppflags}" = ""; then
      AC_MSG_RESULT([none])
    else
      AC_MSG_RESULT([${sd_libxc_cppflags}])
    fi
    AC_MSG_CHECKING([for LibXC C flags])
    if test "${sd_libxc_cflags}" = ""; then
      AC_MSG_RESULT([none])
    else
      AC_MSG_RESULT([${sd_libxc_cflags}])
    fi
    if test "${sd_libxc_enable_fc}" = "yes"; then
      AC_MSG_CHECKING([for LibXC Fortran flags])
      if test "${sd_libxc_fcflags}" = ""; then
        AC_MSG_RESULT([none])
      else
        AC_MSG_RESULT([${sd_libxc_fcflags}])
      fi
    fi
    AC_MSG_CHECKING([for LibXC linker flags])
    if test "${sd_libxc_ldflags}" = ""; then
      AC_MSG_RESULT([none])
    else
      AC_MSG_RESULT([${sd_libxc_ldflags}])
    fi
    AC_MSG_CHECKING([for LibXC library flags])
    if test "${sd_libxc_libs}" = ""; then
      AC_MSG_RESULT([none])
    else
      AC_MSG_RESULT([${sd_libxc_libs}])
    fi
  fi
]) # _SD_LIBXC_DUMP_CONFIG
