#!/bin/bash

declare -r CLIENT=$1
declare -r CLIENT_CONFIG_DIR_USR=/usr/share/gamescope-session-plus/sessions.d
declare -r CLIENT_CONFIG_DIR_ETC=/etc/gamescope-session-plus/sessions.d
declare -r CLIENT_CONFIG_DIR_LOCAL="${XDG_CONFIG_HOME:-$HOME/.config}/gamescope-session-plus/sessions.d"

# default empty session recovery function
short_session_recover () {
	:
}

# default empty function to run after gamescope is started
post_gamescope_start () {
	:
}

# Fix intel color corruption
# might come with some performance degradation but is better than a corrupted
# color image
export INTEL_DEBUG=norbc
export mesa_glthread=true

# Some environment variables by default (taken from Deck session)
export SDL_VIDEO_MINIMIZE_ON_FOCUS_LOSS=0

# Enable Mangoapp
export MANGOHUD_CONFIGFILE=$(mktemp /tmp/mangohud.XXXXXXXX)

export RADV_FORCE_VRS_CONFIG_FILE=$(mktemp /tmp/radv_vrs.XXXXXXXX)

# Plop GAMESCOPE_MODE_SAVE_FILE into $XDG_CONFIG_HOME (defaults to ~/.config).
export GAMESCOPE_MODE_SAVE_FILE="${XDG_CONFIG_HOME:-$HOME/.config}/gamescope/modes.cfg"
export GAMESCOPE_PATCHED_EDID_FILE="${XDG_CONFIG_HOME:-$HOME/.config}/gamescope/edid.bin"

# Make path to gamescope mode save file.
mkdir -p "$(dirname "$GAMESCOPE_MODE_SAVE_FILE")"
touch "$GAMESCOPE_MODE_SAVE_FILE"
#echo "Making Gamescope Mode Save file at \"$GAMESCOPE_MODE_SAVE_FILE\""

# Make path to Gamescope edid patched file.
mkdir -p "$(dirname "$GAMESCOPE_PATCHED_EDID_FILE")"
touch "$GAMESCOPE_PATCHED_EDID_FILE"
#echo "Making Gamescope patched edid at \"$GAMESCOPE_PATCHED_EDID_FILE\""

# There is no way to set a color space for an NV12
# buffer in Wayland. And the color management protocol that is
# meant to let this happen is missing the color range...
# So just workaround this with an ENV var that Remote Play Together
# and Gamescope will use for now.
export GAMESCOPE_NV12_COLORSPACE=k_EStreamColorspace_BT601

# Initially write no_display to our config file
# so we don't get mangoapp showing up before Steam initializes
# on OOBE and stuff.
mkdir -p "$(dirname "$MANGOHUD_CONFIGFILE")"
echo "no_display" > "$MANGOHUD_CONFIGFILE"

# Prepare our initial VRS config file
# for dynamic VRS in Mesa.
mkdir -p "$(dirname "$RADV_FORCE_VRS_CONFIG_FILE")"
echo "1x1" > "$RADV_FORCE_VRS_CONFIG_FILE"

# To expose vram info from radv
export WINEDLLOVERRIDES=dxgi=n

# Don't wait for buffers to idle on the client side before sending them to gamescope
export vk_xwayland_wait_ready=false

# To play nice with the short term callback-based limiter for now
export GAMESCOPE_LIMITER_FILE=$(mktemp /tmp/gamescope-limiter.XXXXXXXX)

# Temporary crutch until dummy plane interactions / etc are figured out
export GAMESCOPE_DISABLE_ASYNC_FLIPS=1

ulimit -n 524288

# Source device quirks if exists
if [ -f /usr/share/gamescope-session-plus/device-quirks ]; then
  . /usr/share/gamescope-session-plus/device-quirks
fi

# Source client app specific configuration files
set -a
for i in ${CLIENT_CONFIG_DIR_USR}/${CLIENT} ${CLIENT_CONFIG_DIR_ETC}/${CLIENT} ${CLIENT_CONFIG_DIR_LOCAL}/${CLIENT} ;
do
    [[ -f "${i}" ]] && . "${i}"
done
set +a

# Source user configuration from ~/.config/environment.d
set -a
for i in "${HOME}"/.config/environment.d/*.conf ;
do
    [[ -f "${i}" ]] && . "${i}"
done
set +a

# Setup socket for gamescope
# Create run directory file for startup and stats sockets
tmpdir="$([[ -n ${XDG_RUNTIME_DIR+x} ]] && mktemp -p "$XDG_RUNTIME_DIR" -d -t gamescope.XXXXXXX)"
socket="${tmpdir:+$tmpdir/startup.socket}"
stats="${tmpdir:+$tmpdir/stats.pipe}"
# Fail early if we don't have a proper runtime directory setup
if [[ -z $tmpdir || -z ${XDG_RUNTIME_DIR+x} ]]; then
	echo >&2 "!! Failed to find run directory in which to create stats session sockets (is \$XDG_RUNTIME_DIR set?)"
	exit 0
fi

export GAMESCOPE_STATS="$stats"
mkfifo -- "$stats"
mkfifo -- "$socket"

# Attempt to claim global session if we're the first one running (e.g. /run/1000/gamescope)
linkname="gamescope-stats"
#   shellcheck disable=SC2031 # (broken warning)
sessionlink="${XDG_RUNTIME_DIR:+$XDG_RUNTIME_DIR/}${linkname}" # Account for XDG_RUNTIME_DIR="" (notfragileatall)
lockfile="$sessionlink".lck
exec 9>"$lockfile" # Keep as an fd such that the lock lasts as long as the session if it is taken
if flock -n 9 && rm -f "$sessionlink" && ln -sf "$tmpdir" "$sessionlink"; then
	# Took the lock.  Don't blow up if those commands fail, though.
	echo >&2 "Claimed global gamescope stats session at \"$sessionlink\""
else
	echo >&2 "!! Failed to claim global gamescope stats session"
fi

CURSOR=""
# Use specified cursor if file exists
if [ -f "$CURSOR_FILE" ] ; then
    CURSOR="--cursor ${CURSOR_FILE}"
fi

if [ -z "$GAMESCOPECMD" ] ; then
    RESOLUTION=""
    if [ -n "$SCREEN_WIDTH" ] && [ -n "$SCREEN_HEIGHT" ] ; then
        RESOLUTION="-W $SCREEN_WIDTH -H $SCREEN_HEIGHT"
    fi
    
    if [ -z $VULKAN_ADAPTER ]; then
        GAMESCOPECMD="/usr/bin/gamescope \
        $CURSOR \
        -e \
        $RESOLUTION \
        --xwayland-count 2 \
        -O *,eDP-1 \
        --default-touch-mode 4 \
        --hide-cursor-delay 3000 \
        --fade-out-duration 200 \
        -R $socket -T $stats"
     else
        GAMESCOPECMD="/usr/bin/gamescope \
        $CURSOR \
        -e \
        $RESOLUTION \
        --xwayland-count 2 \
        -O *,eDP-1 \
        --default-touch-mode 4 \
        --hide-cursor-delay 3000 \
        --fade-out-duration 200 \
        --prefer-vk-device $VULKAN_ADAPTER \
        -R $socket -T $stats"
     fi

else
    # Add socket and stats read
    if [ -z $VULKAN_ADAPTER ]; then
        GAMESCOPECMD+=" -R $socket -T $stats"
    else
        GAMESCOPECMD+=" --prefer-vk-device $VULKAN_ADAPTER -R $socket -T $stats"
    fi
    
fi

# Attempt to fallback to a desktop session if something goes wrong too many times
short_session_tracker_file="/tmp/chimeraos-short-session-tracker"
short_session_duration=60
short_session_count_before_reset=3
SECONDS=0

short_session_count=$(< "$short_session_tracker_file" wc -l)

if [[ "$short_session_count" -ge "$short_session_count_before_reset" ]]; then
    echo >&2 "gamescope-session-plus: detected broken ${CLIENT} or gamescope failure, will try to reset the session"

    short_session_recover

    # rearm
    rm "$short_session_tracker_file"
    exit 1
fi

# Log rotate the last session
if [ -f "${HOME}"/.${CLIENT}-stdout.log ]; then
    cp "${HOME}"/.${CLIENT}-stdout.log "${HOME}"/.${CLIENT}-stdout.log.old
fi
if [ -f "${HOME}"/.gamescope-stdout.log ]; then
    cp "${HOME}"/.gamescope-stdout.log "${HOME}"/.gamescope-stdout.log.old
fi
if [ -f "${HOME}"/.gamescope-cmd.log ]; then
    cp "${HOME}"/.gamescope-cmd.log "${HOME}"/.gamescope-cmd.log.old
fi

# Start gamescope compositor, log it's output and background it
echo $GAMESCOPECMD > "${HOME}"/.gamescope-cmd.log
$GAMESCOPECMD > "${HOME}"/.gamescope-stdout.log 2>&1 &
gamescope_pid="$!"

if read -r -t 3 response_x_display response_wl_display <> "$socket"; then
	export DISPLAY="$response_x_display"
	export GAMESCOPE_WAYLAND_DISPLAY="$response_wl_display"
	# We're done!
else
	echo "gamescope failed" >> "$short_session_tracker_file"
	kill -9 "$gamescope_pid"
	wait
	exit 1
	# Systemd or Session manager will have to restart session
fi

# Run session defined hook
post_gamescope_start

# Input method support if present
if command -v /usr/bin/ibus-daemon > /dev/null; then
    /usr/bin/ibus-daemon -d -r --panel=disable --emoji-extension=disable
fi

# If we have mangoapp binary start it
if command -v mangoapp > /dev/null; then
	(while true; do
		mangoapp > "${HOME}"/.mangoapp-stdout.log 2>&1
	done) &
fi

# For compatibility with older user configuration overrides
if [ -n "$STEAMCMD" ] ; then
	CLIENTCMD=$STEAMCMD
fi

# Start client application
$CLIENTCMD > "${HOME}/.${CLIENT}-stdout.log" 2>&1

if [[ "$SECONDS" -lt "$short_session_duration" ]]; then
	echo "${CLIENT} failed" >> "$short_session_tracker_file"
else
	rm "$short_session_tracker_file"
fi

# When the client exits, kill gamescope nicely
kill $gamescope_pid

# Start a background sleep for five seconds because we don't trust it
sleep 5 &
sleep_pid="$!"

# Catch reboot and powerof sentinels here
if [[ -e "$REBOOT_SENTINEL" ]]; then
	rm -f "$REBOOT_SENTINEL"
	# rearm short session tracker
	rm "$short_session_tracker_file"
	reboot
fi
if [[ -e "$SHUTDOWN_SENTINEL" ]]; then
	rm -f "$SHUTDOWN_SENTINEL"
	# rearm short session tracker
	rm "$short_session_tracker_file"
	poweroff
fi

# Wait for gamescope or the sleep to finish for timeout purposes
ret=0
wait -n $gamescope_pid $sleep_pid || ret=$?

# If we get a SIGTERM/etc while waiting this happens.  Proceed to kill -9 everything but complain
if [[ $ret = 127 ]]; then
	echo >&2 "gamescope-session-plus: Interrupted while waiting on teardown, force-killing remaining tasks"
fi

# Kill all remaining jobs and warn if unexpected things are in there (should be just sleep_pid, unless gamescope failed
# to exit in time or we hit the interrupt case above)
for job in $(jobs -p); do
	# Warn about unexpected things
	if [[ $ret != 127 && $job = "$gamescope_pid" ]]; then
		echo >&2 "gamescope-session-plus: gamescope timed out while exiting, killing"
	elif [[ $ret != 127 && $job != "$sleep_pid" ]]; then
		echo >&2 "gamescope-session-plus: unexpected background pid $job at teardown: "
		# spew some debug about it
		ps -p "$job" >&2
	fi
	kill -9 "$job"
done

