#!/bin/bash
# Testing the installation of the dev tools
# camitk-extensiongenerator and devtools
# 
# echo $? get the last returned value of the script
#

# Exit immediately if a command exits with a non-zero status.
set -e

# Uncomment next line to debug
# set -x

#DEBUG
#To test with different library versions, you can use different install path, for instance:
# CMAKE_OPTIONS="-DVTK_DIR:PATH=/opt/vtk6/lib/cmake/vtk-6.3 -DITK_DIR:PATH=/opt/vtk6/lib/cmake/ITK-4.9 -DGDCM_DIR:PATH=/opt/vtk6/lib/gdcm-2.6"

# ---------------------- cleanup ----------------------
cleanup() {
    # cleanup on exit

    # backup the current exit status
    currentExitValue=$?
    echo 
    echo "Cleaning up before exiting..."
    echo 
    if [[ "$osName" != "Windows" ]]; then
        # kill the xfvb if still exist
        if [ -f /proc/$xvfbPid/status ]; then
            echo "Killing running xvfb server (pid $xvfbPid)..."
            kill $xvfbPid    
        fi        
    fi
    if [ "$currentExitValue" -ne "0" ]; then
        echo 
        echo "Test failed report..."
        echo 
        # output all possible files
        cd $workingDir
        # Test exist status of every file and directory otherwise the script abort and the temp directory is not removed
        if [ -f "./generated-$testDirName" ]; then
            echo "generated-$testDirName output:"
            cat ./generated-$testDirName
            echo "[FAIL] generation"
        else 
            echo "File $workingDir/generated-$testDirName not found"
        fi
        if [ -d "$testDirName/build" ]; then
            cd $testDirName/build
            if [ -f "$workingDir/cmake-log" ]; then
                echo "cmake log output:"
                cat $workingDir/cmake-log
            else
                echo "File $workingDir/cmake-log not found"
            fi
            if [ -f "$workingDir/cmake-error" ]; then
                echo "cmake error log output:"  
                cat $workingDir/cmake-error
                echo "[FAIL] CMake configuration"
            else
                echo "File $workingDir/cmake-error not found"
            fi
            if [ -f "$workingDir/make-log" ]; then
                echo "make log output:"
                cat $workingDir/make-log                
            else
                echo "File $workingDir/make-log not found"
            fi
            if [ -f "$workingDir/make-error" ]; then
                echo "make log error output:"  
                cat $workingDir/make-error
                echo "[FAIL] Build"
            else
                echo "File $workingDir/make-error not found"
            fi
        else
            echo "Directory $workingDir/$testDirName/build not found, nothing to clean"            
        fi
        cd
    fi

    # finally cleanup working dir
    # echo "Cleaning up working directory $workingDir..."
    # rm -rf $workingDir

    # use the backup value (otherwise the result of the "rm -rf" command above will
    # be used, and that's probably always 0 !)
    exit $currentExitValue
}

# ---------------------- checkCommand ----------------------
checkCommand() {
    # usage: checkCommand name 
    if [ "$inBuild" == "0" ] ; then
        # check if current build is on windows debug version
        if ! hash ${1} 2>/dev/null; then
            echo "[FAIL] Error: executable not found"
        else
            echo "[OK]"
        fi
    else
        if [ ! -x ${1} ] ; then 
            echo "[FAIL] Error: file not found or not executable"
        else
            echo "[OK]"
        fi
    fi
}

# ---------------------- initUsingInstalled ----------------------
initUsingInstalled() {
    echo "[OK] Test using installed CamiTK version"
    inBuild=0
    # for installed camitk, just run the corresponding executables
    camitkGenerator=camitk-extensiongenerator
    camitkConfig=camitk-config
    if [[ "$1" == "-python" ]]; then
        testPython=1
    else
        testPython=0
    fi
}

# ---------------------- initUsingInbuild ----------------------
initUsingInbuild() {
    echo "[OK] Test using inbuild directory \"$1\" (source code in \"$2\")"
    # if -inbuild option is specified, then the next argument should be the build dir (as set by ${PROJECT_BINARY_DIR} by cmake
    inBuild=1
    # for in build test, use specific path for executables
    camitkGenerator=$1/bin/camitk-extensiongenerator
    camitkConfig=$1/bin/camitk-config
    # specify CamiTK dir 
    # Although it is not an install dir, it is better to use the build dir than to temporary installed, test and uninstall
    export CAMITK_DIR=$1
    # Add extra cmake module path to find the CMake macros that are indeed not installed in the build dir
    CMAKE_OPTIONS="$CMAKE_OPTIONS -DCMAKE_MODULE_PATH:PATH=$2/sdk/cmake/modules;$2/sdk/cmake/modules/macros"
    
    if [[ $# -eq 3 && "$3" == "-python" ]]; then
        testPython=1
    else
        testPython=0
    fi
}

# ---------------------- init ----------------------
init() {
    echo 
    echo "Checking configuration..."
    echo 
    exitStatus=0 # nothing bad. By convention exit 0 indicates success
    checkValueId=1 # test id starts at 1

    echo "Test script directory..."
    scriptDir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
    echo "[OK] Test script directory is $scriptDir"

    echo
    echo "Creating temporary test directory..."
    workingDir=$(mktemp --tmpdir -d camitk-test-tmp.XXXXXXXXXX)
    echo "[OK] Temporary test directory set to $workingDir"
    
    echo
    echo "Checking test parameters..."
    echo "[OK] Test launched with $# parameters"

    echo
    echo "CamiTK environment..."
    if [[ $# -eq 0 ]]; then
        initUsingInstalled
    elif [[ $# -eq 1 ]]; then
        initUsingInstalled "$1"
    elif [[ $# -ge 3 ]]; then    
        initUsingInbuild "$2" "$3" "${@:4}"
    else
        echo "Usage: $0 [-inbuild <build directory> <source directory>] [-python]"
        exit 1
    fi

    echo
    echo "Testing Python..."
    if [[ "$testPython" == "1" ]]; then
        echo "[OK] Python extension test is on"
    else
        echo "[OK] Python extension test is off"
    fi

    echo
    echo "Checking OS..."
    unameOS=$(uname)
    if [[ "$unameOS" =~ ^MINGW64.* || "$unameOS" =~ ^MSYS_NT.* ]]; then
        osName="Windows"
    else
        osName="Linux"
    fi
    echo "[OK] uname is $unameOS → Test on OS $osName"

    if [[ "$osName" != "Windows" ]]; then
        echo
        echo "Configuring xvfb..."
        displayID=15
        # Starts the server first (to avoid a distracting warning output due to OpenGL context)
        Xvfb :$displayID -screen 0 1600x1200x24 -ac +extension GLX +render -noreset -v -fbdir $workingDir/ > /dev/null 2>&1 &
        xvfbPid=$!
        echo "[OK] PID of Xvfb: $xvfbPid"
        export DISPLAY=:$displayID
        export XAUTHORITY=/dev/null
    fi

    # Checking if current build is on windows debug version
    echo "Checking camitk-config..."
    checkCamiTKConfig=$(checkCommand $camitkConfig)
    if [ "$checkCamiTKConfig" != "[OK]" ] ; then
        if [[ "$osName" == "Windows" ]]; then
            camitkConfig=$camitkConfig-debug
            echo "Checking camitk-config-debug..."
            checkCamiTKConfig=$(checkCommand $camitkConfig)
            echo "$checkCamiTKConfig"
            if [ "$checkCamiTKConfig" != "[OK]" ] ; then            
                echo "[FAIL] Error: cannot find camitk-config nor camitk-config-debug"
            else
                buildType="Debug"
            fi
        fi
    else
        echo "[OK] executable camitk-config found"
        buildType="Release"
    fi

    if [[ "$osName" == "Windows" ]]; then
        echo "Windows build type..."
        echo "[OK] $buildType"
    fi

    echo
    echo "camitk-config configuration..."
    echo "[OK] using $camitkConfig on $osName"

    echo
    echo "Checking camitk-generator..."
    checkCamiTKGenerator=$(checkCommand $camitkGenerator)
    if [ "$checkCamiTKGenerator" != "[OK]" ] ; then
        if [[ "$osName" == "Windows" ]]; then
            camitkGenerator=$camitkGenerator-debug
            echo "Checking camitk-generator-debug..."
            checkCamiTKGenerator=$(checkCommand $camitkGenerator)
            echo "$checkCamiTKGenerator"
        fi
    else
        echo "[OK] executable camitk-generator found"
    fi
    echo 
    echo "camitk-generator configuration..."
    echo "[OK] using $camitkGenerator on $osName"

    if [[ "$osName" == "Windows" ]]; then
        echo
        echo "Checking Windows path..."
        # on windows add the path to the private library dir to access the extensiongenerator DLL
        camitkShortVersion=$($camitkConfig --short-version)
        pathToPrivateLib=$2/lib/$camitkShortVersion
        bashPathToPrivateLib="${pathToPrivateLib/C:\//\/c/}"
        pathToPublicLib=$2/bin
        bashPathToPublicLib="${pathToPublicLib/C:\//\/c/}"
        export PATH=$bashPathToPrivateLib:$bashPathToPublicLib:$PATH
        echo "[OK] on Windows using PATH=$PATH"
    fi

    echo
}

# ---------------------- getConfig ----------------------
getConfig() {
  $camitkConfig --config 2>/dev/null # | sed "s/QStandardPaths.*'.*'//"
}

# ---------------------- getWorkingDirExtensionCount ----------------------
getWorkingDirExtensionCount() {
  echo $(getConfig | grep "^  - \[W\] " | wc -l)
}

# ---------------------- generate ----------------------
generate() {
  # generate
  if [ $# -eq 1 ]; then
    testDirName=$(basename $1 .camitk)
  else
    testDirName="cep"
  fi

  echo 
  echo "$checkValueId.1 Generate $testDirName..."
  echo 

  echo "Generating source code from $testDirName.camitk JSON in directory $workingDir/$testDirName..."
  cd $workingDir
  rm -rf $testDirName
  mkdir $testDirName
  if [ $# -eq 1 ]; then
    cp $1 $testDirName
    cd $testDirName
    $camitkGenerator -f $testDirName.camitk -d $workingDir/$testDirName > ../generated-$testDirName
  else
    cd $testDirName
    generateCompleteCEP $camitkGenerator > ../generated-$testDirName
  fi

  if [ -s ./generated-$testDirName ] ; then
    cat ../generated-$testDirName
  fi

  # get the created dir name
  srcDirName=$(ls)
}

# ---------------------- configure ----------------------
configure() {
  echo 
  echo "$checkValueId.2 Configure $testDirName..."
  echo 

  if [[ "$osName" == "Windows" ]]; then
    # choose the default msvc version and config from current install
    cmake $CMAKE_OPTIONS -Wno-dev -S . -B build > ../cmake-log 2> ../cmake-error
  else
    cmake $CMAKE_OPTIONS -S . -B build > ../cmake-log 2> ../cmake-error
  fi
  if [ -s ../cmake-log ] ; then
    echo "cmake log output:"
    cat ../cmake-log
  fi
  if [ -s ../cmake-error ] ; then
    echo
    echo "cmake error log output:"  
    cat ../cmake-error
  fi
}

# ---------------------- build ----------------------
build() {
  echo 
  echo "$checkValueId.3 Build $testDirName..."
  echo 

  if [[ "$osName" == "Windows" ]]; then
    cmake --build build --parallel --config $buildType > ../make-log 2> ../make-error
  else
    # build (parallel)
    cmake --build build --parallel > ../make-log 2> ../make-error
  fi
  if [ -s ../make-log ] ; then
    echo "make log output:"
    cat ../make-log
  fi
  if [ -s ../make-error ] ; then
    echo "make error log output:"  
    cat ../make-error
  fi

  # go to the build dir before the check
  cd build
}

# ---------------------- testGeneratorPython ----------------------
# @param camitkFile
testGeneratorPython() {
    echo 
    echo "Testing a Python standalone extension ($1)..."
    generate $1
        
    echo 
    echo "$checkValueId.2 Checking $1 installation..."
    echo 

    # test using config -e option
    echo "Checking hotplug extensions for $1..."
    $camitkConfig -e $testDirName.camitk
    configTestExitValue=$?

    if [ "$configTestExitValue" -ne "0" ]; then
        echo "[FAIL] Error: checking hotplug extension failed"
        exitStatus=$checkValueId
    else
        echo "[OK]"
    fi
    echo

    # increase id
    checkValueId=$((checkValueId+1))
}

# ---------------------- testGenerator ----------------------
# [@param camitkFile] (optional)
# @param expected number of created extensions
testGenerator() {
  if [ $# -eq 1 ]; then
    echo 
    echo "Testing whole CEP..."
    generate
    configure
    build
    expectedValue="$1"
 elif [ $# -eq 2 ]; then
    echo 
    echo "Testing a standalone extension ($1)..."
    generate $1
    configure
    build
    expectedValue="$2"
  else
    echo "[FAIL] Error: unexpected number of arguments"
  fi

  echo 
  echo "$checkValueId.4 Checking $1 installation..."
  echo 

  # check if everything is compiled and can be loaded
  value=$(getWorkingDirExtensionCount)
  echo "Number of extensions for $1: $value found ($expectedValue expected)"
  echo "$(getConfig | grep "^  - \[W\] ")"
  if [ "$value" -ne "$expectedValue" ]; then
    echo "[FAIL] Error: unexpected number of extensions installed in the working directory ($value != $expectedValue)"
    exitStatus=$checkValueId
  else
    echo "[OK]"
  fi
  echo

  # increase id
  checkValueId=$((checkValueId+1))
}


# --------------------------------------------------------------------------
#
# All tests are here
#
# --------------------------------------------------------------------------

# if a problem occurs, call the clean method
trap "cleanup" 0 INT QUIT ABRT PIPE TERM EXIT

init $*
cd $workingDir

# -- 1. Test generation from individual camitk extension files

# -- 1.1 Generate .camitk files
$scriptDir/generate-camitk-files.sh

# -- 1.2 Test Python if required
if [[ "$testPython" == "1" ]]; then
    testGeneratorPython pyActions.camitk
fi

# -- 1.3 Test C++ using testgenerator testfile.camitk nrOfExpectedNewExtensions
testGenerator stdActions.camitk 1
testGenerator hotplugActions.camitk 0 # as hot plug do not change camitk-config output

# -- 2. Test a whole CEP that has 2 standard actions extensions, 1 hotplug action extension and 1 lib

# -- 2.1 Generate directory structure and .camitk
source $scriptDir/generate-cep.sh

# -- 2.2 Test C++ extensions (only the 2 standard C++ extensions are visible from camitk-config output)
testGenerator 2

exit $exitStatus
