#!/usr/bin/env bash
set -euo pipefail

# JS7 Controller starter

JS7_HOME="$(cd "${0%/*}/../bin/.." && pwd)"
export JS7_HOME
. "$JS7_HOME/bin/internal/set-context.sh"
declare classpathString java  # returned by set-context.sh
declare -a standardJavaOptions

config=/var/opt/js7/controller/config
data=/var/opt/js7/controller/data
httpPort=4444
httpsPort=4443
controllerOptions=()
javaOptions=()
execOptions=()
processorLimit=

for arg in "$@"; do :
  case "$arg" in
    --arg0=*)
      a="${arg#*=}"
      execOptions=("-a" "${arg#*=}")
      ;;
    -J*)
      a="${arg#-J}"
      javaOptions+=("$a")
      ;;
    --java-option=*)
      a="${arg#*=}"
      javaOptions+=("$a")
      ;;
    --directory=*)
      config="${arg#*=}"/config
      data="${arg#*=}"/data
      ;;
    --config-directory=*)
      config="${arg#*=}"
      ;;
    --data-directory=*)
      data="${arg#*=}"
      ;;
    --http-port=*)
      httpPort="${arg#*=}"
      ;;
    --https-port=*)
      httpsPort="${arg#*=}"
      ;;
    --rmx-port=*)
      a="${arg#*=}"
      javaOptions+=(
        "-Dcom.sun.management.jmxremote"
        "-Dcom.sun.management.jmxremote.ssl=false"
        "-Dcom.sun.management.jmxremote.authenticate=false"
        "-Dcom.sun.management.jmxremote.port=$a")
      ;;
    --debug-port=*)
      a="${arg#*=}"
      javaOptions+=("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=$a")
      ;;
    --processor-limit=*)
      processorLimit="${arg#*=}"
      ;;
    *)
      controllerOptions+=("$arg")
  esac
done

if [ ! -d "$config" ]; then :
  echo "No such directory: --config-directory=$config"
  exit 1
fi
config="$(toSystemPath "$config")"

if [ ! -d "$data" ]; then :
  echo "No such directory: --data-directory=$data"
  exit 1
fi
data="$(toSystemPath "$data")"

logs="$data/logs"
[ -d "$logs" ] || mkdir "$logs"

stateDir="$data/state"
[ -d "$stateDir" ] || mkdir "$stateDir"

workDir="$data/work"
[ -d "$workDir" ] || mkdir "$workDir"

# Copy stdout and stderr to stdouterr.log
if [ -e "$logs/stdouterr.log" ]; then
  echo >>"$logs/stdouterr.log" \
    -e "\n\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
fi
exec &> >(exec tee -a "$logs"/stdouterr.log)

javaOptions+=("-Djs7.log4j.directory=$logs")  # Used in logging configuration
if [ -f "$config/log4j2.xml" ]; then :
  javaOptions+=("-Dlog4j2.configurationFile=classpath:js7/log4j2.xml,$config/log4j2.xml")
fi

if [ -n "$processorLimit" ]; then
  if grep -q "/" <<< "$processorLimit"; then
    # processorLimit is a quotient like 1/4
    processorLimit=$(((1000 * $processorLimit * $(nproc) + 999) / 1000))
  fi
  if [ "$processorLimit" -le "$(nproc)" ]; then
    [ "$processorLimit" -gt 0 ] || processorLimit=1
    javaOptions+=(-XX:ActiveProcessorCount="$processorLimit")
  fi
fi

[ -d "$data/cache" ] || mkdir "$data/cache"
if [[ "$JAVA_VERSION_MAJOR" -ge 19 ]]; then
  standardJavaOptions+=(
    -XX:SharedArchiveFile="$data/cache/js7.jsa"
    -XX:+AutoCreateSharedArchive
    -Xlog:cds=error)
fi

controllerOptions+=("--config-directory=$config" "--data-directory=$data")
[ -z "$httpPort" ] || controllerOptions+=("--http-port=$httpPort")
[ -z "$httpsPort" ] || controllerOptions+=("--https-port=$httpsPort")

while true; do
  execute=(
    "$java"
    "${javaOptions[@]}"
    "${standardJavaOptions[@]}"
    -classpath "$classpathString"
    js7.controller.ControllerMain
    "${controllerOptions[@]}")

  ## Start Controller ##
  log "${execute[@]}"
  exec "${execOptions[@]}" "${execute[@]}" &

  pid=$!
  log "PID $pid"

  trapped=false
  trapSignal() {
    trapped=true
    log "❌ $1: Shutdown JS7 Controller"
    t=$(monotonicMillis)
    if [ "$SHELL" = "/bin/bash" ]; then
      "$JS7_HOME/bin/js7-shutdown" --data-directory="$data" &
    else
      "$java" \
        -Xmx50m \
        "${javaOptions[@]}" \
        "${standardJavaOptions[@]}" \
        -classpath "$classpathString" \
        js7.agent.client.main.SubagentClientMain \
        --data-directory="$data" \
        '{ "TYPE": "ShutDown", "processSignal": "SIGTERM" }' &
    fi
    wait $pid
    exitCode=$?
    log "Shutdown took $(( $(monotonicMillis) - "$t" ))ms"
  }

  trap "trapSignal SIGTERM" SIGTERM
  trap "trapSignal SIGINT" SIGINT

  exitCode=
  while wait $pid; exitCode=$?; $trapped; do
    trapped=false
  done

  trap - SIGTERM SIGINT

  if [[ $exitCode -eq 0 ]] || [[ $exitCode -eq 97 ]]; then
    log "JS7 Controller has been shut down"
  else
    log "JS7 Controller exited with exit code $exitCode"
  fi

  if [[ $exitCode -eq 0 ]] || [[ $exitCode -ge 97 ]] && [[ $exitCode -le 99 ]]; then
    log "Clear $workDir"
    find "$workDir" -mindepth 1 -delete ! -type d -printf "Removed %p\n"
  fi

  if [[ $exitCode -eq 97 ]] || [[ $exitCode -eq 98 ]]; then
    log "Starting again"
  else
    exit $exitCode
  fi
done
