#!/bin/bash
#---
# eval-pr
#
# Copyright (c) 2022 Freeciv21. This file is part of Freeciv21. Freeciv21 is free software: you can
# redistribute it and/or modify it under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the  License, or (at your option) any later version. You
# should have received a copy of the GNU General Public License along with Freeciv21. If not, see
# https://www.gnu.org/licenses/.
#
# Bash shell script to aid the process of evaluating a pull request
#---

version="1.2"
re='^[0-9]+$'
asan="false"

if [[ $2 ]]; then
  pull_request=$2
fi

help ()
{
  echo
  echo "Freeciv21: Evaluate a Pull Request as documented at:"
  echo "    https://longturn.readthedocs.io/en/latest/Contributing/eval-pull-request.html"
  echo
  echo "$0 Version: $version"
  echo
  echo "Should be executed from the root of the Freeciv21 source directory."
  echo
  echo "Syntax: ./scripts/eval-pr -h|s|a|c"
  echo
  echo "-h                Opens this help"
  echo "-s [PR Number]    Starts a new evaluation process compiled with Debug"
  echo "-a [PR Number]    Starts a new evaluation process compiled with the ASan Preset"
  echo "-c [PR Number]    Cleanup a previously run evaluation process"
  echo
  echo "If you wish to evaluate more than one pull request at a time, you can wrap the script"
  echo " in a \"for\" loop, such as:"
  echo
  echo "    for pr in pr#1 pr#2 pr#3; do ./scripts/eval-pr -s \$pr; done"
  echo
}

base_setup ()
{
  # Ensure we are on the master branch before starting.
  local branch=$(git rev-parse --abbrev-ref HEAD)
  if ! [[ "$branch" == "master" ]]; then
    echo "-- Error: Ensure you have stashed all work and have checked out the master branch before starting."
    echo "-- You are on branch: $branch. If this is a testing/pr_# branch, run a cleanup operation first or"
    echo "-- manually stash work and checkout master to evaluate another pull request."
    exit 255
  fi

  # Ensure we are in the root of the freeciv21 source tree before starting.
  if ! [[ -e CMakePresets.json ]]; then
    echo "-- Error: Ensure you are at the root of the Freeciv21 source tree before starting."
    exit 255
  fi

  echo "-- Fetching upstream"
  echo
  git fetch upstream


  echo "-- Creating a testing branch for PR $pull_request."
  echo
  git checkout -b testing/pr_$pull_request upstream/master

  echo
  echo "-- Getting a .diff of PR $pull_request."
  echo
  wget https://github.com/longturn/freeciv21/pull/$pull_request.diff -O pr$pull_request.diff

  echo "-- Applying the .diff via patch."
  echo
  git checkout -- .
  git apply pr$pull_request.diff
  rm pr$pull_request.diff

  # Always start with a empty build directory when testing
  if [[ -e build_$pull_request ]]; then
    rm -Rf build_$pull_request
  fi
}

close_message ()
{
  if [[ "$asan" == "false" ]]; then
    echo "-- You can open the client with this command in another terminal to test PR $pull_request:"
    echo
    echo "      $PWD/build_$pull_request/freeciv21-client"
    echo
    echo "-- If you wish to run the client through a debugger, such as gdb, then prefix \"gdb\" in front of"
    echo "--  the command above."
    echo ""
    echo "-- If you wish to run an autogame to test the code in PR $pull_request, then open another terminal"
    echo "--  and run this command:"
    echo
    echo "      $PWD/build_$pull_request/freeciv21-server -r $PWD/data/test-autogame.serv -t"
    echo
    echo "-- If you want to observe the autogame, open another terminal and run this command to open a client:"
    echo
    echo "      $PWD/build_$pull_request/freeciv21-client -a -p 5556 -s localhost"
    echo
    echo "-- Lastly, if you wish to run the autogame server or observing client through a debugger, then"
    echo "--  prefix the previous two command lines with \"gdb --args\"."
    echo
  elif [[ "$asan" == "true" ]]; then
    echo "-- You can open the client with this command in another terminal to test PR $pull_request:"
    echo
    echo "      $PWD/build_$pull_request/freeciv21-client"
    echo
    echo "-- If you wish to run an autogame to test code in PR $pull_request, then open another terminal and"
    echo "--  run this command:"
    echo
    echo "      export ASAN_OPTIONS=\"halt_on_error=0\" && $PWD/build_$pull_request/freeciv21-server -r $PWD/data/test-autogame.serv -t"
    echo
    echo "-- If you want to observe the autogame, open another terminal and run this command to open a client:"
    echo
    echo "      $PWD/build_$pull_request/freeciv21-client -a -p 5556 -s localhost"
    echo
    echo "-- Lastly, The ASan preset compiles with the \"RelWithDebInfo\" configuration instead of the"
    echo "--  \"Debug\" configuration. Running the code through a debugger, such as gdb, can have unintended"
    echo "--  effects. If you wish to run the autogame server or observing client through a debugger, then"
    echo "--  prefix the previous two command lines with \"gdb --args\"."
    echo
  fi
}

debug_eval ()
{
  echo
  echo "-- Starting a Debug Eval Process for PR $pull_request."
  echo

  base_setup

  echo
  echo "-- Building Debug."
  echo
  # We ask for a debug build in order to run the code through a debugger while evaluating
  cmake -S . -B build_$pull_request -G Ninja \
    -DCMAKE_INSTALL_PREFIX=$PWD/build_$pull_request/install \
    -DCMAKE_BUILD_TYPE=Debug
  cmake --build build_$pull_request

  # Run tests except on MSYS2 (they fail)
  [[ $MSYSTEM ]] || cmake --build build_$pull_request --target test

  # Always test the installer
  cmake --build build_$pull_request --target install

  # Only run on Debian based linux or MSYS2
  if which dpkg &>/dev/null || [[ $MSYSTEM ]]; then
    cmake --build build_$pull_request --target package
  fi

  echo
  echo "-- PR $pull_request is ready to be evaluated. Run $0 -c $pull_request to cleanup."
  echo
  close_message
}

asan_eval ()
{
  echo
  echo "-- Starting an ASan Eval Process for PR $pull_request."
  echo

  asan="true"

  if [[ $MSYSTEM ]]; then
    echo "-- Error: ASan not supported on MSYS2."
    exit 255
  fi

  base_setup

  echo
  echo "-- Building ASan."
  echo
  if ! [[ -e build_$pull_request ]]; then
    mkdir build_$pull_request
    cmake --preset ASan -S . -B build_$pull_request
  fi
  cmake --build build_$pull_request
  cmake --build build_$pull_request --target test

  echo
  echo "-- PR $pull_request is ready to be evaluated. Run $0 -c $pull_request to cleanup."
  echo
  close_message
}

cleanup ()
{
  echo
  echo "-- Cleanup Eval Process for PR# $pull_request."
  echo

  local branch=$(git rev-parse --abbrev-ref HEAD)
  if ! [[ "$branch" == "testing/pr_$pull_request" ]]; then
    echo "-- Error: Attempting cleanup on incorrect pull request number."
    echo "-- The current branch is: $branch" and pull request: $pull_request
    echo ""
    exit 255
  fi

  echo "-- Stashing Changes and checking out master branch."
  echo
  git stash
  git checkout master

  echo
  echo "-- Removing testing branch for PR $pull_request."
  echo
  git branch -D testing/pr_$pull_request

  if [[ "$ASAN_OPTIONS" ]]; then
    export ASAN_OPTIONS=
  fi

  if [[ -e build_$pull_request ]]; then
    rm -Rf build_$pull_request
  fi

  echo
  echo "-- Cleanup complete."
  echo
}

# If nothing is passed in, then open the help
if [[ -z "$1" ]]; then
  help
fi

# If the second paramter is missing, throw an error
if [[ "$1" == "-s" || "$1" == "-a" || "$1" == "-c" ]]; then
  if [[ -z "$2" ]]; then
    echo "-- Error: You must pass a pull request number to the script when starting or cleaning up an eval"
    echo "--  process."
    echo
    help
    exit 255
  fi
  if ! [[ $2 =~ $re ]] ; then
    echo "-- Error: A Pull Request number has to be numeric"
    echo
    exit 255
  fi
fi

while getopts ":hsac:" option; do
  case $option in
    h) help
       exit 0;;
    s) debug_eval
       exit 0;;
    a) asan_eval
       exit 0;;
    c) cleanup
       exit 0;;
    \?) echo "-- Error: Invalid option"
        echo ""
        help
        exit 255;;
  esac
done
