#!/bin/sh
#
# configure for the Uno R package.
#
# Builds the Uno static library from source via CMake (its HiGHS QP/LP
# subproblem solver and MUMPS are reached at run time through the 'highs' and
# 'rmumps' packages, headers only), then generates src/Makevars.

R_UNO_PKG_HOME=`pwd`

: ${R_HOME=`R RHOME`}
if test -z "${R_HOME}"; then
    echo "'R_HOME' could not be found!"
    exit 1
fi

# Locate the MUMPS C API headers of the 'rmumps' package. build_uno.sh compiles
# Uno's MUMPSSolver.cpp against them (header-only; NO MUMPS library is linked):
# the dmumps_c entry point is bound at load time by src/dmumps_shim.c to the
# C-callables rmumps registers. Two generations of rmumps are supported:
#   * rmumps >= 5.2.1-44 installs <rmumps/rmumps_api.h> (-DUNO_RMUMPS_API): the
#     shim uses the entry points named after the DMUMPS_STRUC_C layout it was
#     compiled against (RMUMPS_DMUMPS_ABI), so a layout mismatch is a clean
#     load-time error; rmumps 5.9.1-1 brings MUMPS 5.9.1 this way;
#   * rmumps <= 5.2.1-43 has only the top-level dmumps_c.h: the legacy "dmumps_c"
#     callable and the MUMPS 5.2.1 layout.
# This enables the interior-point ("ipopt") preset. Skip with R_UNO_NO_MUMPS=1
# (filtersqp/HiGHS only; -DUNO_R_NO_MUMPS tells the shim).
RUNO_MUMPS_CPPFLAGS="-DUNO_R_NO_MUMPS"
if test -z "${R_UNO_NO_MUMPS}"; then
    RMUMPS_INC=`"${R_HOME}/bin/Rscript" -e 'cat(system.file("include", package="rmumps"))'`
    if test -n "${RMUMPS_INC}" && test -f "${RMUMPS_INC}/rmumps/rmumps_api.h"; then
        export MUMPS_R_INCLUDE_DIRS="${RMUMPS_INC}/rmumps"
        RUNO_MUMPS_CPPFLAGS="-DUNO_RMUMPS_API"
        echo "MUMPS headers (rmumps, versioned C interface): ${MUMPS_R_INCLUDE_DIRS}"
    elif test -n "${RMUMPS_INC}" && test -f "${RMUMPS_INC}/dmumps_c.h"; then
        export MUMPS_R_INCLUDE_DIRS="${RMUMPS_INC}"
        RUNO_MUMPS_CPPFLAGS=""
        echo "MUMPS headers (rmumps, legacy dmumps_c): ${MUMPS_R_INCLUDE_DIRS}"
    else
        echo "rmumps headers not found (looked in '${RMUMPS_INC}')."
        echo "Install 'rmumps' (>= 5.2.1-43), or set R_UNO_NO_MUMPS=1 to build without MUMPS."
        exit 1
    fi
fi

# Generate src/uno_status_names.h from the bundled C API header, so the status
# labels the binding returns follow Uno's enums (tools/gen_status_table.R).
"${R_HOME}/bin/Rscript" tools/gen_status_table.R \
    inst/uno/interfaces/C/Uno_C_API.h src/uno_status_names.h \
    || { echo "Generating src/uno_status_names.h failed!"; exit 1; }

# HiGHS QP/LP subproblem solver (enables Uno's SQP presets, e.g. filtersqp):
# provided at RUN TIME by the 'highs' package (>= 1.15.1-2), which registers
# HiGHS' C API with R_RegisterCCallable and installs <highs_ccallable.h>.
# build_uno.sh compiles Uno's HiGHS backend against that header (header-only;
# NO HiGHS library is built or linked): each C API entry point is resolved
# through R_GetCCallable the first time it is called, so importing highs in
# the NAMESPACE (which loads its shared object) is all the binding needs.
# Skip with R_UNO_NO_HIGHS=1.
if test -z "${R_UNO_NO_HIGHS}"; then
    HIGHS_INC=`"${R_HOME}/bin/Rscript" -e 'cat(system.file("include", package="highs"))'`
    if test -n "${HIGHS_INC}" && test -f "${HIGHS_INC}/highs_ccallable.h"; then
        export HIGHS_R_INCLUDE_DIRS="${HIGHS_INC}"
        echo "HiGHS headers (highs package, C-callable API): ${HIGHS_R_INCLUDE_DIRS}"
    else
        echo "highs_ccallable.h not found (looked in '${HIGHS_INC}')."
        echo "Install 'highs' (>= 1.15.1-2), or set R_UNO_NO_HIGHS=1 to build without HiGHS."
        exit 1
    fi
fi

# Build the Uno static library (src/unolib/{lib,include}).
bash inst/build_uno.sh || { echo "Uno build failed!"; exit 1; }
cd "${R_UNO_PKG_HOME}"

# The static library (src/unolib) is now built; the binding links the built
# lib+headers, not the bundled Uno SOURCE tree.  That tree lives under inst/
# (not src/) so R CMD check's src-only pragma/line-ending scans never see the
# vendored third-party files.  It is kept OUT of the *installed* package via
# the top-level .Rinstignore (patterns /uno and /build_.*\.sh$), per CRAN's
# request -- NOT by deleting it from the extracted tarball here (which also
# avoids ever touching a developer's submodule checkout during an in-place
# `R CMD INSTALL .`).  See Writing R Extensions, ".Rinstignore".

# Remove the intermediate CMake build tree: the library is installed under
# src/unolib, so the build dir is dead weight, and its CMake-generated
# Makefiles carry CRLF line endings on Windows -> R CMD check "line endings in
# Makefiles" WARNING.
rm -rf uno_build

# Normalize the installed Uno headers so R CMD check stays quiet:
#   * strip CRLF -> LF  (Windows CMake emits CRLF -> "line endings in sources" NOTE)
#   * ensure a trailing newline  (some upstream headers ship without one ->
#     "sources/headers not terminated with a newline" NOTE)
# The trailing-newline test: $(tail -c1 f) strips a final LF in command
# substitution, so it is non-empty exactly when the last byte is NOT a newline.
# Both passes are no-ops on already-clean files.
for incdir in src/unolib/include; do
    test -d "${incdir}" || continue
    find "${incdir}" -type f -print | while read f; do
        tr -d '\r' < "$f" > "$f.tmp"
        if test -s "$f.tmp" && test -n "$(tail -c1 "$f.tmp")"; then
            printf '\n' >> "$f.tmp"
        fi
        mv "$f.tmp" "$f"
    done
done

R_UNO_LIB_DIR=${R_UNO_PKG_HOME}/src/unolib

# Link flags: the Uno static archive resolves its BLAS/LAPACK/Fortran-runtime
# externals at the final package .so link (HiGHS and MUMPS are reached at run
# time through the highs and rmumps packages; nothing of theirs is linked).
RUNO_PKG_LIBS="-L${R_UNO_LIB_DIR}/lib -luno"

sed -e "s|@RUNO_LIB_DIR@|${R_UNO_LIB_DIR}|g" \
    -e "s|@RUNO_PKG_LIBS@|${RUNO_PKG_LIBS}|g" \
    -e "s|@RUNO_MUMPS_CPPFLAGS@|${RUNO_MUMPS_CPPFLAGS}|g" \
    src/Makevars.in > src/Makevars

if [ -z "$(ls ${R_UNO_LIB_DIR} 2>/dev/null | grep 'include')" ]; then
    echo "Uno headers could not be found!"
    exit 1
fi
