#!/bin/bash

# where is the source tree located by default relative to here
srcdir=$(dirname "$(dirname "$(dirname "$0")")")

# display help text
usage () {
  cat <<EOF
Installation directories:
  --prefix=PREFIX         install architecture-independent files in PREFIX
                          [/usr/local]. Note: set DESTDIR=PATH when doing
                          \`make install' to install to a different sysroot.

Optional Features:
  --disable-FEATURE       do not include FEATURE
  --disable-gxx11check    do not try flag -std=c++11 to enable C++11 features
  --enable-shared         build a shared library [default=yes]
  --enable-static         build a static library [default=no]. Note: only one
                          of the options shared and static may be built.
  --enable-debug          build a non-optimized version of the library
                          [default=no]
  --disable-runpath       do not use RUNPATH in installed library [default=yes]
  --enable-lto            use whole program optimization [default=no]
  --disable-tests         do not compile and enable unit tests [default=yes]
  --disable-examples      do not compile example programs [default=yes]
  --disable-pch           do not use precompiled headers (if buggy compiler)
  --disable-silent-rules  print every compilation statement as executed
  --enable-system-debug   put .debug files in global GDB debug dir
                          [default=yes if prefix=/usr, no otherwise]
  --enable-parallel       process in parallel using MPI [default=no]
  --enable-openmp         activate experimental support for OpenMP
  --disable-option-checking  ignore unrecognized --enable/--with options
  --enable-underscoring   assume Fortran routines have _ suffix [default=no]
  --enable-ninja          use Ninja build generator [default=no]
                          (automatically implies --enable-underscoring)
  --config-cache          Reuse build configuration cache from a previous run

Optional Packages:
  --with-ug=PATH          use the UG libraries from a specified location
  --with-alugrid=PATH     use the ALUGrid library from a specified location
  --with-metis=PATH       use the METIS graph partitioning library from a specified location
  --with-boost=PATH       use Boost library from a specified location
  --with-dune=PATH        specify parent of all DUNE modules not specified
  --with-dune-MODULE=PATH use given DUNE module from a specified location
  --with-opm=PATH         specify parent of all OPM modules not specified
  --with-opm-MODULE=PATH  use given OPM module from a specified location
  --with-superlu=PATH     user defined path to SuperLU library
  --with-umfpack=PATH     use UMFPACK/SuiteSparse from a specified location
  --with-ert=PATH         Use ERT libraries
  --with-tinyxml=PATH     use TinyXML library from a specified location
                          (Note: if not found, then a bundled library will
                           be used)
  --with-cmake=PROGRAM    use this program instead of \`cmake' to configure
  --with-buildname=TEXT   description passed to the CDash configuration
  --with-site=TEXT        site passed to the CDash configuration

Some influential environment variables:
  CC          C compiler command
  CFLAGS      C compiler flags
  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
              nonstandard directory <lib dir>
  LIBS        libraries to pass to the linker, e.g. -l<library>
  CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
              you have headers in a nonstandard directory <include dir>
  CPP         C preprocessor
  CXX         C++ compiler command
  CXXFLAGS    C++ compiler flags
  CXXCPP      C++ preprocessor
  F77         Fortran 77 compiler command
  FFLAGS      Fortran 77 compiler flags
  FC          Fortran compiler command
  FCFLAGS     Fortran compiler flags
  CMAKE_COMMAND  Executable used to run cmake scripts

Use these variables to override the choices made by \`configure' or to help
it to find libraries and programs with nonstandard names/locations.
EOF
}

# report an error regarding the arguments
invalid_arg () {
  cat <<EOF
configure: error: unrecognized option: \`$1'
Try \`$0 --help' for more information
EOF
}

# notify the user that this argument is not known
unknown_arg () {
  cat <<EOF
configure: warning: unrecognized option: \`$1'
EOF
}

# warn only if option checking is enabled
invalid_opt () {
  if [ "${option_check}" = "yes" ]; then
    unknown_arg "$@"
  fi
}

# default values
prefix=/usr/local
#c_compiler=" -DCMAKE_C_COMPILER=cc"
c_compiler=
c_opts=
#cxx_compiler=" -DCMAKE_CXX_COMPILER=c++"
cxx_compiler=
cxx_opts=
#fort_compiler=" -DCMAKE_Fortran_COMPILER=fc"
fort_compiler=
fort_opts=
#buildtype=" -DCMAKE_BUILD_TYPE=Debug"
buildtype=
#pch_use=" -DPRECOMPILE_HEADERS:BOOL=ON"
pch_use=
#use_openmp=" -DUSE_OPENMP=OFF"
use_openmp=
use_mpi=
#silent_rules=" -DCMAKE_VERBOSE_MAKEFILE=OFF"
silent_rules=
#debug_loc=" -DSYSTEM_DEBUG=OFF"
debug_loc=
#use_lto=" -DWHOLE_PROG_OPTIM=OFF"
use_lto=
#use_runpath=" -DUSE_RUNPATH=OFF"
use_runpath=
#use_tests=" -DBUILD_TESTING=ON"
use_tests=
#use_samples=" -DBUILD_EXAMPLES=ON"
use_samples=
#use_ninja="-G\"Unix Makefiles\" "
use_ninja=
#use_underscoring=" -DUSE_UNDERSCORING=OFF"
use_underscoring=
# boost_root=""
boost_root=
boost_libdir=
boost_opts=
# configuration that is passed on to CTest/CDash
buildname=
site=
# if set, this prevents the previous CMake cache from being deleted
config_cache=

# default is to warn for unknown options, but this can be disabled
option_check=yes

# this variable will get feature options
FEATURES=

# this array will get all variable assignments from command-line
VARS=()

# command that launches cmake; look for 2.8 if available
if [ "${CMAKE_COMMAND}" = "" ]; then
  if [ -x "$(command -v cmake28)" ]; then
    CMAKE_COMMAND=cmake28
  else
    CMAKE_COMMAND=cmake
  fi
fi

# helper routine
uppercase () {
  echo "$@" | tr "a-z-" "A-Z_"
}

for OPT in "$@"; do
  case "$OPT" in
    --*)
      OPTARG=${OPT#--}
      # OPTARG now contains everything after double dashes
      case "${OPTARG}" in
        config-cache|cache-file=*)
          # prevent the previous CMake cache from being deleted. The
          # second option is only here for Dune/autotools compatibility
          config_cache="1"
          ;;
        src-dir=*)
          # allow the user to use these build macros for another
          # project (so source-dir is not relative to us)
          srcdir=${OPTARG#*=}
          ;;
        prefix=*)
          # remove prefix consisting of everything up to equal sign
          prefix=${OPTARG#*=}
          ;;
        help)
          usage
          exit 0
          ;;
        with-*)
          # get the name of the package; everything before equal sign
          pkgname=${OPTARG%=*}
          pkgname=${pkgname#with-}
          # get the location of the package; everyhing after equal sign
          test -n "${OPTARG#with-${pkgname}}" && pkgloc=${OPTARG#*=} || pkgloc=""
          # the parameter to this option is an executable program, so
          # skip the directory test in that case. if we match any of
          # these special options, then stop further processing (the
          # argument is not a directory anyway)
          case "${pkgname}" in
            cmake)
              CMAKE_COMMAND="${pkgloc}"
              continue
              ;;
            buildname)
              buildname=" -DBUILDNAME=\"${pkgloc}\""
              continue
              ;;
            site)
              site=" -DSITE=\"${pkgloc}\""
              continue
              ;;
          esac
          # tilde expansion; quote safely before running eval on it
          eval pkgloc=$(printf "%q" "${pkgloc}")
          # expand to full path since CMake changes to source directory (!)
          # this also normalize the path name wrt. not having a trailing slash
          test -d "${pkgloc}" && pkgloc=$(sh -c "cd \"${pkgloc}\"; pwd")
          # special aliases
          case "${pkgname}" in
            umfpack)
              pkgname="SuiteSparse"
              ;;
            tinyxml)
              pkgname="TinyXML"
              ;;
          esac
          # packages need different suffix for their root (sic)
          case "${pkgname}" in
            pch)
              pch_use=" -DPRECOMPILE_HEADERS:BOOL=ON"
              rootvar=""
              ;;
            mpi         |\
            mpi-prefix)
              # specifying path implies use of package
              use_mpi=" -DUSE_MPI=ON"
              # only set prefix if specified, i.e. setting doubles as flag
              test -n "${pkgloc}" && rootvar="_MPI_PREFIX_PATH" || rootvar=""
              ;;
            boost)
              # special handling of this package, see further below
              boost_root="${pkgloc}"
              rootvar=""
              ;;
            boost-libdir)
              boost_libdir="${pkgloc}"
              rootvar=""
              ;;
            alugrid     |\
            eigen3      |\
            ert         |\
            metis       |\
            superlu     |\
            SuiteSparse |\
            TinyXML     |\
            ug          |\
            opm         |\
            opm-*       |\
            dune        |\
            dune-*      |\
            zlib)
              rootvar="$(uppercase ${pkgname})_ROOT"
              rootvar="${rootvar/-/_}"
              ;;
            *)
              invalid_opt --with-${pkgname}
              rootvar=""
              ;;
          esac
          # add this to the list of existing features
          test -n "${rootvar}" && \
          FEATURES="${FEATURES} \"-D${rootvar}=${pkgloc}\""
          ;;
        without-* | \
        disable-*)
          # get the name of the package
          pkgname=$OPTARG
          pkgname=${pkgname#disable-}
          pkgname=${pkgname#without-}
          # casing is of course different
          case "${pkgname}" in
            option-checking)
              option_check=no
              # special flag: don't disable any particular package
              pkgname=""
              ;;
            debug)
              buildtype=" -DCMAKE_BUILD_TYPE=Release"
              # special flag: don't disable any particular package
              pkgname=""
              ;;
            pch)
              pch_use=" -DPRECOMPILE_HEADERS:BOOL=OFF"
              pkgname=""
              ;;
            runpath)
              use_runpath=" -DUSE_RUNPATH=OFF"
              pkgname=""
              ;;
            silent-rules)
              silent_rules=" -DCMAKE_VERBOSE_MAKEFILE=ON"
              pkgname=""
              ;;
            system-debug)
              debug_loc=" -DSYSTEM_DEBUG=OFF"
              pkgname=""
              ;;
            wpo  |\
            lto  )
              use_lto=" -DWHOLE_PROG_OPTIM=OFF"
              pkgname=""
              ;;
            openmp)
              use_openmp=" -DUSE_OPENMP=OFF"
              pkgname=""
              ;;
            mpi  | \
            parallel)
              use_mpi=" -DUSE_MPI=OFF"
              pkgname=""
              ;;
            tests)
              use_tests=" -DBUILD_TESTING=OFF"
              pkgname=""
              ;;
            examples)
              use_samples=" -DBUILD_EXAMPLES=OFF"
              pkgname=""
              ;;
            ninja)
              # just for symmetry with the --enable-ninja option
              use_ninja=""
              pkgname=""
              ;;
            ert  |\
            superlu)
              pkgname="$(uppercase ${pkgname})"
              ;;
            openmp)
              pkgname="OpenMP"
              ;;
            gxx11check)
              pkgname="CXX11Features"
              ;;
            umfpack)
              pkgname="SuiteSparse"
              ;;
            tinyxml)
              pkgname="TinyXML"
              ;;
            *)
              invalid_opt --disable-${pkgname}
              pkgname=""
              ;;
          esac
          # only disable packages if the flag refers to a proper one
          test -n "${pkgname}" && \
          FEATURES="${FEATURES} -DCMAKE_DISABLE_FIND_PACKAGE_${pkgname}=TRUE"
          ;;
        enable-*)
          # what kind of library are we building; shared or static?
          kind=${OPTARG#enable-}
          case "${kind}" in
            system-debug)
              debug_loc=" -DSYSTEM_DEBUG=ON"
              # special flag; don't set shared/static
              shared=""
              ;;
            openmp)
              use_openmp=" -DUSE_OPENMP=ON"
              # special flag; don't set shared/static
              shared=""
              ;;
            mpi  | \
            parallel)
              use_mpi=" -DUSE_MPI=ON"
              # special flag; don't set shared/static
              shared=""
              ;;
            debug)
              buildtype=" -DCMAKE_BUILD_TYPE=Debug"
              shared=""
              ;;
            pch)
              pch_use=" -DPRECOMPILE_HEADERS:BOOL=ON"
              shared=""
              ;;
            runpath)
              use_runpath=" -DUSE_RUNPATH=ON"
              shared=""
              ;;              
            lto)
              use_lto=" -DWHOLE_PROG_OPTIM=ON"
              shared=""
              ;;
            tests)
              use_tests=" -DBUILD_TESTING=ON"
              pkgname=""
              ;;
            examples)
              use_samples=" -DBUILD_EXAMPLES=ON"
              pkgname=""
              ;;
            underscoring)
              use_underscoring=" -DUSE_UNDERSCORING=ON"
              pkgname=""
              ;;
            ninja)
              # Ninja doesn't support using the Fortran compiler, so
              # we'll have to resort to making this assumption
              use_underscoring=" -DUSE_UNDERSCORING=ON"
              use_ninja="-GNinja "
              pkgname=""
              ;;
            # this flag is just for compatibility with the deprecation
            # flag in DUNE, so we can build without warnings
            fieldvector-size-is-method)
              shared=""
              ;;              
            shared)
              shared="ON"
              ;;
            static)
              shared="OFF"
              ;;
            *)
              invalid_opt "--enable-${kind}"
              shared=""
              ;;
          esac
          test -n "${shared}" && \
          FEATURES="${FEATURES} -DBUILD_SHARED_LIBS:BOOL=${shared}"
          # once we have added this, reset so we don't add again for next opt
          shared=""
          ;;
        *)
          # remove everything *after* the equal sign
          arg=${OPTARG%=*}
          invalid_arg "--$arg"
          exit 1
          ;;
      esac
      ;;
    [A-Za-z0-9_]*=*)
      # collect for further processing later
      VARS+=("$OPT")
      ;;
    *)
      invalid_arg "$OPT"
      exit 1
      ;;
  esac
done
# remove all arguments processed by getopts
shift $((OPTIND-1))

# special handling of Boost: if --with-boost-libdir has been used,
# then the --with-boost turns into specifying the header directory
# and not the search root. this mirrors the functionality in the
# Autotools ax_boost_base.m4. necessary because FindBoost in CMake
# uses two different variables if you want to specify them separately
if [ -n "${boost_libdir}" ]; then
    boost_opts=" -DBOOST_LIBRARYDIR=\"${boost_libdir}\""
    if [ -n "${boost_root}" ]; then
        boost_opts="${boost_opts} -DBOOST_INCLUDEDIR=\"${boost_root}\""
    fi
else
    if [ -n "${boost_root}" ]; then
        boost_opts=" -DBOOST_ROOT=\"${boost_root}\""
    else
        boost_opts=""
    fi
fi

# notice the usage of a quoted array: each element will be returned
# even with spaces.
for a in "${VARS[@]}"; do
  case "$a" in
    ACLOCAL_*=*)
      # remove Autotools-specific variables. 
      ;;
    CC=*)
      # special processing for compiler options
      a=${a#CC=}
      [ -x "$(command -v "$a")" ] && a=$(command -v "$a")
      c_compiler=" -DCMAKE_C_COMPILER=\"${a/\"/\\\"}\""
      ;;
    CXX=*)
      a=${a#CXX=}
      [ -x "$(command -v "$a")" ] && a=$(command -v "$a")
      cxx_compiler=" -DCMAKE_CXX_COMPILER=\"${a/\"/\\\"}\""
      ;;
    CFLAGS=*)
      a=${a#CFLAGS=}
      c_opts=" -DCMAKE_C_FLAGS=\"${a/\"/\\\"}\""
      ;;
    CXXFLAGS=*)
      a=${a#CXXFLAGS=}
      cxx_opts=" -DCMAKE_CXX_FLAGS=\"${a/\"/\\\"}\""
      ;;
    FC=*)
      a=${a#FC=}
      [ -x "$(command -v "$a")" ] && a=$(command -v "$a")
      fort_compiler=" -DCMAKE_Fortran_COMPILER=\"${a/\"/\\\"}\""
      ;;
    FFLAGS=*)
      a=${a#FFLAGS=}
      fort_opts=" -DCMAKE_Fortran_FLAGS=\"${a/\"/\\\"}\""
      ;;
    *)
      ENVVARS="$ENVVARS \"${a/\"/\\\"}\""
      ;;
  esac
done

# only wrap in env command if any variable were actually passed
[ -n "${ENVVARS}" ] && ENVVARS="env ${ENVVARS} "

# delete the previous 'CMakeFiles' directory. this prevents an endless
# loop if variables that require a full regeneration of the cache are
# set (most notably 'CXX' and 'CXX_FLAGS').
# For more details, see http://www.cmake.org/Bug/view.php?id=14119
if test "$config_cache" = ""; then
    echo "--- deleting previous CMake files ---"
    rm -rf CMakeFiles
    rm -f CMakeCache.txt
elif test "$c_compiler$c_opts$cxx_compiler$cxx_opts$fort_compiler$fort_opts" != ""; then
    echo "--- WARNING '--config-cache' option specified but a compiler was set"
    echo "--- from the command line. This may lead to an infinite loop!"
fi

# pass everything on to CMake
CMDLINE="${ENVVARS}${CMAKE_COMMAND} \"${srcdir}\" ${use_ninja}\"-DCMAKE_INSTALL_PREFIX=$prefix\"${buildtype}${pch_use}${silent_rules}${debug_loc}${use_openmp}${use_mpi}${use_lto}${use_runpath}${use_tests}${use_samples}${use_underscoring}${c_compiler}${c_opts}${cxx_compiler}${cxx_opts}${fort_compiler}${fort_opts}${boost_opts}${buildname}${site} ${FEATURES}"
echo --- calling CMake ---
echo "${CMDLINE}"
eval exec "${CMDLINE}"
