#!/bin/bash


#---- Configuration ------------------------------------------------------------

# paths to compilers
CXX=g++      # c++
CC=gcc       # c
FC=gfortran  # fortran
JAVAC=javac  # java

# absolute path to folder containing RSTT libraries
RSTT_ROOT=${RSTT_ROOT:-${RSTT_HOME:-${SLBM_ROOT:-${SLBM_HOME:-$(realpath $PWD/..)}}}}
LIBDIR=$RSTT_ROOT/lib

# os flag
OS=$(uname -s | tr "[:upper:]" "[:lower:]")

# arch/cpu flags
case $(uname -m) in
    arm64|aarch64)
        ARCH=64
        CXX_FLAGS=("-mcpu=native")
        ;;
    x86_64)
        ARCH=64
        CXX_FLAGS=("-march=native" "-m${ARCH}")
        ;;
    *)
        ARCH=32
        CXX_FLAGS=("-march=native" "-m${ARCH}")
        ;;
esac

# specific LD_FLAGS on Mac
if [ $OS == "darwin" ]; then

    # if using clang instead of gnu compiler, set additional LD_FLAGS
    CLANG=$($CXX -v 2>&1 | grep -qi clang; echo $?)
    if [ $CLANG -eq 0 ]; then
        OTHER_LDFLAGS=("-undefined dynamic_lookup")
    fi

fi


#---- Explanation of some common compiling parameters --------------------------

# -D<arch>
# Defines a symbol for the preprocessor which may adjust some code for different
# operating systems.

# -m<32|64>
# Tells the compiler whether we're on a 32 or 64-bit system.

# -O<1-3>
# Optimize the code, which makes compilation take longer but improves runtime
# performance and filesize. Level 3 (-O3) is the maximum, but newer version of
# gcc don't recommend above level 2 (-O2), and RSTT seems to throw some
# compilation errors when level 2 is used.

# -I
# Include directories for various headers.

# -L
# Location of the libraries (*.so or *.dylib) that we load with "-l" commands.

# -Wl
# Defines some parameters for the linker.

# -Wl,-rpath,<path>
# Tells the linker to search the <path> for linked libraries at runtime. In this
# shell script, we are pointing to the absolute path of the directory containing
# the RSTT library files ($LIBDIR); however, we could also specify relative
# paths by using "$$ORIGIN" on Linux or "@executable_path" on MacOS to mean
# "the directory containing the application executable". For example,
# -Wl,-rpath,'$$ORIGIN/../lib'


#---- C++ compile --------------------------------------------------------------

compile_cpp() {

    (
        set -x  # echo commands
        $CXX ${CXX_FLAGS[@]} -D${OS} -O1 -std=c++11 -I../GeoTessCPP/include -I../SLBM/include -o cpp_example.o -c cpp_example.cc
        $CXX ${CXX_FLAGS[@]} -D${OS} ${OTHER_LDFLAGS[@]}  -O1 -std=c++11 -Wl,-rpath,"$LIBDIR" -o cpp_example cpp_example.o -lm -lstdc++ -L"${LIBDIR}" -lgeotesscpp -lslbm
    )

    # clean up
    rm cpp_example.o

}


#---- C compile ----------------------------------------------------------------

compile_c() {

    (
        set -x  # echo commands
        $CC ${CXX_FLAGS[@]} -D${OS} -O1 -I../GeoTessCPP/include -I../SLBM/include -I../SLBM_C_shell/include -o c_example.o -c c_example.c
        $CC ${CXX_FLAGS[@]} -D${OS} -O1 -Wl,-rpath,"$LIBDIR" ${OTHER_LDFLAGS[@]} -o c_example c_example.o -lm -lstdc++ -L"${LIBDIR}" -lgeotesscpp -lslbm -lslbmCshell
    )

    # clean up
    rm c_example.o

}


#---- Fortran compile ----------------------------------------------------------

compile_fortran() {

    (
        set -x  # echo commands
        $FC ${CXX_FLAGS[@]} -D${OS} -O1 -fno-underscoring -cpp -ffree-line-length-none -I../GeoTessCPP/include -I../SLBM/include -I../SLBM_Fort_shell/include -o fortran_example.o -c fortran_example.f90
        $FC ${CXX_FLAGS[@]} -D${OS} ${OTHER_LDFLAGS[@]}  -O1 -fno-underscoring -cpp -ffree-line-length-none -Wl,-rpath,"${LIBDIR}" -o fortran_example fortran_example.o -lm -lstdc++ -L"$LIBDIR" -lgeotesscpp -lslbm -lslbmFshell
    )

    # clean up
    rm fortran_example.o constants.mod error_checking.mod

}


#---- Java compile -------------------------------------------------------------

compile_java() {

    (
        set -x  # echo commands
        $JAVAC -cp "$LIBDIR/slbmjni.jar" java_example.java
    )

}


#---- run code -----------------------------------------------------------------

run_cpp() {
    (
        set -x  # echo commands
        ./cpp_example
    )
}

run_c() {
    (
        set -x  # echo commands
        ./c_example
    )
}

run_fortran() {
    (
        set -x  # echo commands
        ./fortran_example
    )
}

run_java() {
    (
        set -x  # echo commands
        java -Djava.library.path="$LIBDIR" -cp .:"$LIBDIR/slbmjni.jar" java_example
    )
}

run_python() {
    (
        set -x  # echo commands
        RSTT_ROOT=$RSTT_ROOT ./python_example.py
    )
}


#---- main ---------------------------------------------------------------------

# if no arguments are given
if [ $# -eq 0 ]; then

    # show usage example
    echo "Usage: $0 [ all | cpp | c | fortran | java | python ]"
    exit

fi

# if arguments are given, loop through them
while [ $# -gt 0 ]; do

    # turn off case matching of `case` statements
    shopt -s nocasematch

    # check the current argument 
    case "${1}" in

        cpp|cc|c++)
            compile_cpp && run_cpp
            ;;

        c)
            compile_c && run_c
            ;;

        fortran)
            compile_fortran && run_fortran
            ;;

        java)
            compile_java && run_java
            ;;

        python)
            run_python
            ;;

        all)
            compile_cpp     && run_cpp
            compile_c       && run_c
            compile_fortran && run_fortran
            compile_java    && run_java
            run_python
            ;;

        *)
            echo "Usage: $0 [ all | cpp | c | fortran | java | python ]"
            exit
            ;;

    esac

    # remove the current argument from the stack and move onto the next one
    shift

done
