#!/bin/sh

# Configure the optional native binary128 backend.  It is usable only when the
# active Fortran compiler can compile the REAL(16) spheroidal implementation
# and the active C++17 compiler can instantiate the matching complex type.

set -eu

srcdir=$(CDPATH= cd "$(dirname "$0")" && pwd)
if test -n "${R_HOME:-}"; then
    r_command="$R_HOME/bin/R"
else
    r_command=R
fi

FC=${FC:-$("$r_command" CMD config FC)}
FCFLAGS=${FCFLAGS:-$("$r_command" CMD config FCFLAGS)}
PKG_FFLAGS=${PKG_FFLAGS:-}
CXX17=${CXX17:-$("$r_command" CMD config CXX17)}
CXX17STD=${CXX17STD:-$("$r_command" CMD config CXX17STD)}
CXX17FLAGS=${CXX17FLAGS:-$("$r_command" CMD config CXX17FLAGS)}

quad_objects=
quad_cppflags=-DACOUSTICTS_HAVE_QUADMATH=0
fc_heap_flags=
fc_prolate_flags=
fc_diagnostic_flags=

makevars_template=${ACOUSTICTS_MAKEVARS_TEMPLATE:-Makevars.in}
makevars_output=${ACOUSTICTS_MAKEVARS_OUTPUT:-Makevars}

configure_tmp=$(mktemp -d "${TMPDIR:-/tmp}/acousticTS-configure.XXXXXX") || exit 1
trap 'rm -rf "$configure_tmp"' EXIT HUP INT TERM

cp "$srcdir/src/param_quad.f90" "$configure_tmp/param_quad.f90"
cp "$srcdir/src/prolate_swf.f90" "$configure_tmp/prolate_swf.f90"

cat > "$configure_tmp/conftest_heap.f90" <<'EOF'
subroutine conftest_heap(n)
    integer, intent(in) :: n
    real :: work(n)
    work = 0.0
end subroutine conftest_heap
EOF

# Some LLVM Flang builds accept R's global -Wall setting but diagnose it as
# an unused command-line argument.  Detect that behavior from compiler output
# and, only when needed and supported, silence that driver-level diagnostic.
printf '%s' 'checking whether the Fortran compiler needs unused-argument suppression... '
if (
    cd "$configure_tmp"
    eval "$FC $PKG_FFLAGS $FCFLAGS \
        -c conftest_heap.f90 -o conftest_diagnostic.o" \
        >diagnostic.log 2>&1 &&
    grep "argument unused during compilation: '-Wall'" diagnostic.log \
        >/dev/null 2>&1
); then
    for candidate in \
        -Wno-unused-command-line-argument \
        -Qunused-arguments
    do
        if (
            cd "$configure_tmp"
            eval "$FC $PKG_FFLAGS $FCFLAGS $candidate \
                -c conftest_heap.f90 -o conftest_diagnostic.o" \
                >diagnostic-suppressed.log 2>&1 &&
            ! grep "argument unused during compilation: '-Wall'" \
                diagnostic-suppressed.log >/dev/null 2>&1
        ); then
            fc_diagnostic_flags=$candidate
            break
        fi
    done
fi

if test -n "$fc_diagnostic_flags"; then
    echo "yes ($fc_diagnostic_flags)"
else
    echo no
fi

# Intel Fortran places runtime-sized local arrays on the stack by default.
# The spheroidal backend has several such arrays, particularly in REAL(16),
# so use the compiler's heap-allocation option when it is available.  Probe
# the option instead of identifying compilers by name.
printf '%s' 'checking whether the Fortran compiler supports heap arrays... '
if (
    cd "$configure_tmp"
    eval "$FC $PKG_FFLAGS $FCFLAGS -heap-arrays \
        -c conftest_heap.f90 -o conftest_heap.o" \
        >heap-arrays.log 2>&1
); then
    fc_heap_flags=-heap-arrays
    echo yes
else
    echo no
fi

# GCC 16 snapshots can crash in the SLP optimization pass while compiling
# prolate_swf.f90 at -O2. Limit the workaround to that compiler series and
# this source file; later compilers can use their normal optimization flags.
fc_version=$(
    eval "$FC -dumpfullversion -dumpversion" 2>/dev/null || true
)
case "$fc_version" in
    16|16.*)
        fc_prolate_flags=-fno-tree-slp-vectorize
        ;;
esac

cat > "$configure_tmp/conftest.cpp" <<'EOF'
#include <quadmath.h>
#include <complex>

int main() {
    std::complex<__float128> numerator(
        static_cast<__float128>(1), static_cast<__float128>(2));
    std::complex<__float128> denominator(
        static_cast<__float128>(3), static_cast<__float128>(4));
    const std::complex<__float128> result = numerator / denominator;
    return result.real() == static_cast<__float128>(0);
}
EOF

printf '%s' 'checking for a compatible native binary128 C++/Fortran backend... '
if test "${ACOUSTICTS_DISABLE_QUAD:-0}" != 1 && (
    cd "$configure_tmp"
    eval "$FC $PKG_FFLAGS $FCFLAGS $fc_heap_flags \
        -cpp -c param_quad.f90 -o param_quad.o" >fortran.log 2>&1
    eval "$FC $PKG_FFLAGS $FCFLAGS $fc_heap_flags $fc_prolate_flags \
        -cpp -DUSE_QUAD \
        -c prolate_swf.f90 -o prolate_swf_quad.o" >>fortran.log 2>&1
    eval "$CXX17 $CXX17STD $CXX17FLAGS -c conftest.cpp -o conftest.o" >cxx.log 2>&1
); then
    quad_objects='param_quad.o prolate_swf_quad.o'
    quad_cppflags=-DACOUSTICTS_HAVE_QUADMATH=1
    echo yes
else
    echo no
    echo '  native quad precision will be unavailable; the double backend will still be built'
fi

sed \
    -e "s|@QUAD_CPPFLAGS@|$quad_cppflags|g" \
    -e "s|@QUAD_OBJECTS@|$quad_objects|g" \
    -e "s|@FC_HEAP_FLAGS@|$fc_heap_flags|g" \
    -e "s|@FC_PROLATE_FLAGS@|$fc_prolate_flags|g" \
    -e "s|@FC_DIAGNOSTIC_FLAGS@|$fc_diagnostic_flags|g" \
    "$srcdir/src/$makevars_template" > "$srcdir/src/$makevars_output"

exit 0
