#!/bin/sh

## configure for Windows
set -eu

## configure args
##
##    details:
##      args are passed as: --configure-args="--foo-bar"
##
##      args:
##          --force-vendor: accepted for parity with the Unix
##              configure script, but on Windows the vendored
##              TA-Lib source is always used.
##
##      any other argument is forwarded verbatim as a C/C++
##      compiler flag to both the vendored TA-Lib (CMake)
##      build and the R wrapper compile step (Makevars.win).
##      Example:
##
##          R CMD INSTALL . --configure-args="-O3 -march=native"
##
##      NOTE: binaries built with -march=native are tied to
##            the build host's CPU features and are NOT portable.
OPTFLAGS=""
for arg in "$@"; do
  case "$arg" in
    --force-vendor)
      echo "{talib} on Windows will always use the vendored TA-Lib source."
      echo "Feel free to submit a PR, if you have a solid solution to Windows + {talib} using system/user-installed TA-Lib."
      ;;
    ## autotools-style arguments injected by cross-compile harnesses —
    ## dropped so they don't leak into CMAKE_C_FLAGS as unknown compiler
    ## flags. Kept parallel with the UNIX `configure` script.
    --build=*|--host=*|--target=*|ac_cv_*=*)
      ;;
    *)
      OPTFLAGS="${OPTFLAGS} ${arg}"
      ;;
  esac
done

if [ -n "$OPTFLAGS" ]; then
  echo "Forwarding compiler flags:$OPTFLAGS"
fi

## 0) basic sanity checks
##    before starting the build
##    process
##
##    check if R exists on the
##    default PATH
: ${R_HOME=`R RHOME`}
if test -z "${R_HOME}"; then
  echo 
  echo "could not determine R_HOME"
  echo 
  exit 1
fi

## construct variable to R binaries taken
## from 'Writing R extensions'
##
##    "$(R_HOME)/bin$(R_ARCH_BIN)/Rscript" filename
##    "$(R_HOME)/bin$(R_ARCH_BIN)/Rscript" -e 'R expression'
R_BIN="${R_HOME}/bin${R_ARCH_BIN}/R"

## R compiler configuration
##
## details:
##  WRE Section 1.2.6 ("Using cmake") instructs to propagate CC/CXX/
##  CFLAGS/CXXFLAGS/CPPFLAGS/LDFLAGS from R's config to cmake.
##  On Windows the risk is that CMake auto-detects whatever
##  gcc.exe it finds first on PATH — Rtools43 ships gcc 13,
##  Rtools44 ships gcc 14, and a stray MSYS2 / system Mingw can
##  shadow these. Mismatch -> linker errors at the .dll link.
##
##  R CMD config returns the compiler with leading flags baked
##  in (e.g. "gcc -std=gnu2x"). CMAKE_C_COMPILER expects a bare
##  executable path, so split the first token off as the
##  compiler binary and fold the remainder into the flags.
R_CC_FULL=$("$R_BIN" CMD config CC)
R_CXX_FULL=$("$R_BIN" CMD config CXX)
R_CFLAGS=$("$R_BIN" CMD config CFLAGS)
R_CXXFLAGS=$("$R_BIN" CMD config CXXFLAGS)
R_CPPFLAGS=$("$R_BIN" CMD config CPPFLAGS)
R_LDFLAGS=$("$R_BIN" CMD config LDFLAGS)

R_CC=$(echo "$R_CC_FULL" | awk '{print $1}')
R_CC_EXTRA=$(echo "$R_CC_FULL" | cut -s -d' ' -f2-)
R_CXX=$(echo "$R_CXX_FULL" | awk '{print $1}')
R_CXX_EXTRA=$(echo "$R_CXX_FULL" | cut -s -d' ' -f2-)

## construct temporary directory
## for the build tests
## See: https://stackoverflow.com/a/53063602/10400791
TESTDIR=$(mktemp -d) || {
  echo
  echo "error: cannot create temporary directory"
  echo
  exit 1
}

## construct simple C routine
## that is to be compile
cat > "${TESTDIR}/conftest.c" <<'EOF'
void rtools_conftest(void) {}
EOF

## compile the conftest.c 
## and capture status
(
  cd "$TESTDIR" || exit 1
  "$R_BIN" CMD SHLIB conftest.c
) >/dev/null 2>&1

STATUS=$?

## throw an error if the conftest.c 
## fails
if [ $STATUS -ne 0 ] || { [ ! -f "${TESTDIR}/conftest.dll" ] && [ ! -f "${TESTDIR}/conftest.so" ]; }; then
  echo
  echo "configure: error: R CMD SHLIB test compilation failed; build tools may be missing or misconfigured." 
  echo 
  rm -rf "$TESTDIR"
  exit 1
fi

## remove TESTDIR
## from host-machine
rm -rf "$TESTDIR"

## 1) general setup of
##    the configure script
TALIB="src/ta-lib"
## use R's compiler for the conftest below, not a guessed `cc`
## which doesn't exist on stock Windows. Honour an explicit
## CC env var if the caller set one.
CC_BIN="${CC:-$R_CC}"

## 1.1) determine processors
##      for TA-Lib building
##      its much faster than regular 
##      building
nprocs() {
  if command -v nproc >/dev/null 2>&1; then nproc
  elif getconf _NPROCESSORS_ONLN >/dev/null 2>&1; then getconf _NPROCESSORS_ONLN
  else echo 2; fi
}

## 1.2) check if TA-Lib is vendored
##      correctly and determine an action
##      conditional of how the repository
##      have been downloaded
if [ ! -f "$TALIB/CMakeLists.txt" ]; then

   ## check if git is available
    ## on the system
    if ! command -v git >/dev/null 2>&1; then
      echo
      echo "Build-error: Git is not found on default PATH."
      echo "=========================================================================="
      echo "Install Git:"
      echo "  Windows:"
      echo "    Winget (Win10/11):  winget install --id Git.Git -e"
      echo "    Chocolatey:         choco install git"
      echo "    Or download Git for Windows from: https://git-scm.com/downloads"
      echo
      echo "After installation, ensure 'git' is on PATH and restart the shell."
      echo "=========================================================================="
      echo "Submit bug-reports here: https://github.com/serkor1/ta-lib-R"
      echo
      exit 1
    fi

  ## this part checks if its a git repository
  ## which most imply that the host have clone the 
  ## repository without --recursive
  ## so this part is "just" to give the developer a helpful
  ## message
  if git status > /dev/null 2>&1; then
      echo "Build-error: $TALIB not found. Either make a new clone, or initialize the submodule."
      echo 
      echo "Clone: \t\t git clone --recursive https://github.com/serkor1/ta-lib-R.git"
      echo "Initialize: \t git submodule update --init --recursive"
      echo
      exit 1
  
  ## if its NOT a git repository it is safe to assume that the development
  ## version is being installed via {pak} or other tools which does not 
  ## include submodules by default
  ##    NOTE: After many attempts to ease the installation of the development
  ##          version, this is the best solution I could come up with. If you 
  ##          are reading this, I am open to other and better solutions.
  else
    echo
    echo "TA-Lib (core) not found. Cloning https://github.com/TA-Lib/ta-lib.git"
    echo
    git clone https://github.com/TA-Lib/ta-lib.git ${TALIB} || {
        echo
        echo "Build-error: Could not clone https://github.com/TA-Lib/ta-lib.git. Check your internet connection, or submit a bug-report."
        echo
        exit 1
      }
  fi
fi

## 1.2.1) check if CMake is available
##        on the system
if ! command -v cmake >/dev/null 2>&1; then
  echo
  echo "CMake not found on PATH."
  echo "=========================================================================="
  echo "Install CMake:"
  echo "  Windows:"
  echo "    winget: winget install -e --id Kitware.CMake"
  echo "    Chocolatey: choco install cmake -y"
  echo "    Scoop: scoop install cmake"
  echo
  echo "Alternatively download the installer: https://cmake.org/download/"
  echo "After installation, restart the shell or add CMake to PATH"
  echo "=========================================================================="
  echo "Submit bug-reports here: https://github.com/serkor1/ta-lib-R"
  echo
  exit 1
fi

## 1.3) no clue what this does
##      but it works...
if [ -z "${Platform:-}" ]; then
  case "$(uname -m)" in x86_64|amd64) export Platform=x64 ;; aarch64) export Platform=arm64 ;; i*86) export Platform=x86 ;; *) export Platform=x64 ;; esac
fi

## 2) build TA-Lib in a temporary
##    folder to avoid NOTE on
##    CRAN machine about .a, .dll etc
##    files, see .Rbuildignore for more
##    details.
BUILDROOT=$(mktemp -d) || {
  echo
  echo "error: cannot create temporary directory"
  echo
  exit 1
}

OUT_PREFIX="$BUILDROOT/prefix"
GEN="MinGW Makefiles"; command -v ninja >/dev/null 2>&1 && GEN="Ninja"

## 2.1) build release version
##      with CMake using MinGW
##
##      NOTE: This fails on my own machine
##            and for (it seems) everyone else except
##            GHA workflows.
##
if ! cmake -S "$TALIB" -B "$BUILDROOT/cmake" -G "$GEN" \
  -Wno-dev --log-level=NOTICE \
  -DCMAKE_BUILD_TYPE=Release \
  -DCMAKE_INSTALL_PREFIX="$OUT_PREFIX" \
  -DBUILD_SHARED_LIBS=OFF \
  -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
  -DCMAKE_C_COMPILER="$R_CC" \
  -DCMAKE_CXX_COMPILER="$R_CXX" \
  -DCMAKE_C_FLAGS="$R_CC_EXTRA $R_CFLAGS $R_CPPFLAGS -w ${OPTFLAGS}" \
  -DCMAKE_CXX_FLAGS="$R_CXX_EXTRA $R_CXXFLAGS $R_CPPFLAGS -w ${OPTFLAGS}" \
  -DCMAKE_EXE_LINKER_FLAGS="$R_LDFLAGS" \
  -DCMAKE_SHARED_LINKER_FLAGS="$R_LDFLAGS" \
  -DBUILD_DEV_TOOLS=OFF
then
  rm -rf "$BUILDROOT/cmake"
  GEN="MSYS Makefiles"
  if ! cmake -S "$TALIB" -B "$BUILDROOT/cmake" -G "$GEN" \
    -Wno-dev --log-level=NOTICE \
    -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_INSTALL_PREFIX="$OUT_PREFIX" \
    -DBUILD_SHARED_LIBS=OFF \
    -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
    -DCMAKE_C_COMPILER="$R_CC" \
    -DCMAKE_CXX_COMPILER="$R_CXX" \
    -DCMAKE_C_FLAGS="$R_CC_EXTRA $R_CFLAGS $R_CPPFLAGS -w ${OPTFLAGS}" \
    -DCMAKE_CXX_FLAGS="$R_CXX_EXTRA $R_CXXFLAGS $R_CPPFLAGS -w ${OPTFLAGS}" \
    -DCMAKE_EXE_LINKER_FLAGS="$R_LDFLAGS" \
    -DCMAKE_SHARED_LINKER_FLAGS="$R_LDFLAGS" \
    -DBUILD_DEV_TOOLS=OFF
  then
    echo
    echo "Please check that RTools and the relevant PATH is correctly setup."
    echo "    - Or open a issue at https://github.com/serkor1/ta-lib-R      "
    echo
    exit 1
  fi
fi

## 2.2) build and install
##      as per TA-Lib instructions
cmake --build "$BUILDROOT/cmake" --config Release -- -j"$(nprocs)"
cmake --install "$BUILDROOT/cmake" --config Release --prefix "$OUT_PREFIX"

## 2.2.1) fix line-ending notes
##        NOTE: this might be a dangerous
##              step if pathing and naming
##              gets changed upstream.
if [ -f "$TALIB/include/ta_config.h" ]; then
  tmp="$(mktemp)"; tr -d '\r' < "$TALIB/include/ta_config.h" > "$tmp" && mv "$tmp" "$TALIB/include/ta_config.h"
fi

## 2.3) construct flags
##      for Makevars
ABS_INC="$OUT_PREFIX/include"
ABS_LIB="$OUT_PREFIX/lib"

PKG_CPPFLAGS="-I$ABS_INC -I$ABS_INC/ta-lib"
if   [ -f "$ABS_LIB/libta-lib-static.a" ]; then
  PKG_LIBS="$ABS_LIB/libta-lib-static.a"
elif [ -f "$ABS_LIB/libta-lib.a" ]; then
  PKG_LIBS="$ABS_LIB/libta-lib.a"
elif [ -f "$ABS_LIB/libta-lib.dll.a" ]; then
  PKG_LIBS="-L$ABS_LIB -lta-lib"
else
  echo "ERROR: TA-Lib library not found under $ABS_LIB" >&2
  exit 1
fi

## 2.3.1) check that TA-Lib
##        works with the vendored
##        library
##
## details:
##  link a real TA-Lib symbol (not just compile the header) so an
##  ABI mismatch between the static lib we just built and the
##  compiler R will use surfaces here, not at the much later
##  R CMD INSTALL link step where the error is harder to read.
cat > "$BUILDROOT/conftest.c" <<'EOF'
#include <ta_libc.h>
int main(void){ TA_Initialize(); TA_Shutdown(); return 0; }
EOF
"$CC_BIN" "$BUILDROOT/conftest.c" -o "$BUILDROOT/conftest" \
  -I"$ABS_INC" -I"$ABS_INC/ta-lib" $PKG_LIBS >/dev/null 2>&1 || {
  echo "ERROR: vendored TA-Lib link probe failed (header path: $ABS_INC, lib: $PKG_LIBS)" >&2
  exit 1
}

## Opt-in strict warnings for the R wrapper compile step.
## Triggered by 'make check' via TALIB_STRICT_WARNINGS=1.
if [ "${TALIB_STRICT_WARNINGS:-0}" = "1" ]; then
  WARN_FLAGS="-Wall -Wpedantic -Wextra -Wno-unused-parameter -Wno-cast-function-type"
  echo "Strict warnings enabled: $WARN_FLAGS"
else
  WARN_FLAGS=""
fi

## 2.4) write Makevars
##      and clean up
mkdir -p src
cat > src/Makevars.win <<EOF
# autogenerated by configure.win
PKG_CPPFLAGS = $PKG_CPPFLAGS
PKG_CFLAGS = $OPTFLAGS $WARN_FLAGS
PKG_LIBS = $PKG_LIBS
EOF

cat > cleanup.win <<EOF
#!/bin/sh
[ -d "$BUILDROOT" ] && rm -rf "$BUILDROOT"
EOF
chmod +x cleanup.win

echo "configure.win: done."
