#!/usr/bin/env bash

set -u

if [[ $# -lt 2 ]]; then
    echo "Usage: $0 <Steam AppID> <process name>" >&2
    echo "Example: $0 108800 Crysis2.exe" >&2
    exit 2
fi

APPID="$1"
PROCESS_NAME="$2"

START_TIMEOUT="${START_TIMEOUT:-120}"
EXIT_STABLE_TIME="${EXIT_STABLE_TIME:-3}"

SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
LOG_FILE="${SCRIPT_DIR}/steam-game.log"

WRAPPER_PID="$$"

log() {
    printf '[%s] [STEAM:%s] %s\n' \
        "$(date '+%Y-%m-%d %H:%M:%S')" \
        "${APPID}" \
        "$*" >> "${LOG_FILE}"
}

#
# Retourne les PID dont la ligne de commande contient littéralement
# le nom de processus demandé.
#
game_pids() {
    local proc
    local pid
    local cmdline
    local -a argv

    for proc in /proc/[0-9]*; do
        pid="${proc##*/}"

        # Ne jamais matcher le wrapper principal.
        [[ "${pid}" == "${WRAPPER_PID}" ]] && continue

        # game_pids() tourne dans un sous-shell à cause de :
        #
        #   mapfile -t pids < <(game_pids)
        #
        # Il faut donc également ignorer ce sous-shell.
        [[ "${pid}" == "${BASHPID}" ]] && continue

        [[ -r "${proc}/cmdline" ]] || continue

        argv=()

        mapfile -d '' -t argv < "${proc}/cmdline" 2>/dev/null || true

        [[ ${#argv[@]} -gt 0 ]] || continue

        cmdline="${argv[*]}"

        if [[ "${cmdline}" == *"${PROCESS_NAME}"* ]]; then
            printf '%s\n' "${pid}"
        fi
    done
}

#
# Termine le ou les processus correspondant au jeu.
#
terminate_game() {
    local -a pids

    mapfile -t pids < <(game_pids)

    if [[ ${#pids[@]} -eq 0 ]]; then
        log "No game process to terminate"
        return 0
    fi

    log "Stopping game processes: ${pids[*]}"

    kill -TERM "${pids[@]}" 2>/dev/null || true

    sleep 3

    mapfile -t pids < <(game_pids)

    if [[ ${#pids[@]} -gt 0 ]]; then
        log "Force stopping remaining processes: ${pids[*]}"
        kill -KILL "${pids[@]}" 2>/dev/null || true
    fi
}

#
# Si Sunshine termine le wrapper, terminer également le jeu.
#
on_termination() {
    log "Wrapper received termination signal"

    terminate_game

    exit 143
}

trap on_termination TERM INT HUP

#
# Vérifications.
#
if ! command -v steam >/dev/null 2>&1; then
    log "ERROR: steam command not found"
    exit 127
fi

log "Wrapper started with PID ${WRAPPER_PID}"
log "Launching Steam AppID ${APPID}"
log "Process name: ${PROCESS_NAME}"

#
# Demander à Steam de lancer le jeu.
#
steam -applaunch "${APPID}" \
    >> "${LOG_FILE}" 2>&1 &

#
# Attendre l'apparition du véritable processus du jeu.
#
start_time="${SECONDS}"

while true; do
    mapfile -t pids < <(game_pids)

    if [[ ${#pids[@]} -gt 0 ]]; then
        log "Game detected: ${pids[*]}"
        break
    fi

    if (( SECONDS - start_time >= START_TIMEOUT )); then
        log "ERROR: Game did not appear within ${START_TIMEOUT} seconds"
        exit 124
    fi

    sleep 1
done

#
# Rester vivant aussi longtemps que le jeu.
#
missing_since=""

while true; do
    mapfile -t pids < <(game_pids)

    if [[ ${#pids[@]} -gt 0 ]]; then
        missing_since=""
    else
        if [[ -z "${missing_since}" ]]; then
            missing_since="${SECONDS}"
            log "Game process disappeared, waiting ${EXIT_STABLE_TIME}s"
        fi

        if (( SECONDS - missing_since >= EXIT_STABLE_TIME )); then
            log "Game exited"
            break
        fi
    fi

    sleep 1
done

log "Wrapper exiting normally"

exit 0