#!/usr/bin/env zsh

setopt rcquotes

# eprintf <format> [args...]
function eprintf {
    printf "${@}" >&2
}

# die <format> [args...]
function die {
    eprintf '\e[91mfatal\e[m: '"${1}\n" "${(@)@:2}"
    exit 1
}

# print_help <argv[0]>
function print_help {
    local exec_name="$(basename "${1}")"
    printf 'usage: %s [OPTIONS...] <GAME> [ARGS...]\n' "${exec_name}"
    printf 'Run a game inside an Xwayland session with a window manager. This\n'
    printf 'is very similar to gamescope, except that there is a window\n'
    printf 'involved. The default window manager is OpenBox.\n'
    printf '\n'
    printf '  -h   Print this message, then exit.\n'
    printf '  -w   Specify the window manager to use. This argument to this option will be\n'
    printf '       passed to "sh -c".\n'
    printf '  -r   Specify the resolution of the Xwayland display. This should in the form\n'
    printf '       of ''WxH''. By default, the resolution of the currently focused output\n'
    printf '       will be used.\n'
    printf '  -d   Specify the current desktop environment (or standalone Wayland\n'
    printf '       compositor). This is used if -r is not specified to find the resolution.\n'
    exit 0
}

# get_current_desktop
function get_current_desktop {
    [[ -z "${XDG_CURRENT_DESKTOP}" ]] &&
        die 'set $XDG_CURRENT_DESKTOP or pass -d top specify desktop'
    eprintf 'Detected desktop: "%s"\n' "${XDG_CURRENT_DESKTOP}"
    printf '%s' "${XDG_CURRENT_DESKTOP}"
}

# get_default_resolution [desktop]
function get_default_resolution {
    case "${1}" in
        'river')
            local output="$(ristate -o |
                            jq -r '.outputs | keys.[] as $names |
                                   {output: $names, focused: .[$names].focused} |
                                    select(.focused) | .output')"
            eprintf 'Detected output: %s\n' "${output}"
            local resolution="$(wlr-randr --json |
                                jq -r --arg output "${output}" \
                                 '.[] | select(.name == $output).modes.[] |
                                  select(.current) | "\(.width)x\(.height)"')"
            ;;
        *)
            die 'unknown desktop session: "%s"' "${1}"
            ;;
    esac
    eprintf 'Detected resolution: %s\n' "${resolution}"
    printf '%s\n' "${resolution}"
}

while getopts ':hw:r:d:' OPTOPT; do
    case "${OPTOPT}" in
         'h')
             print_help "${0}"
             ;;
         'w')
             window_manager="${OPTARG}"
             ;;
         'r')
             resolution="${OPTARG}"
             ;;
         'd')
             current_desktop="${OPTARG}"
             ;;
         ':')
             die 'option requires an argument: %s' "${OPTARG}"
             ;;
         '?')
             die 'unknown option: %s' "${OPTARG}"
             ;;
    esac
done

(( "${OPTIND}" > ${#} )) && print_help "${0}"
shift $(( "${OPTIND}" - 1))

{
    set -e
    [[ -v window_manager ]] ||
        window_manager='openbox'
    [[ -v current_desktop ]] ||
        current_desktop="$(get_current_desktop 2>&3)"
    [[ -v resolution ]] ||
        resolution="$(get_default_resolution "${current_desktop}" 2>&3)"
} 3>&2

function on_sigchld {
    eprintf 'Xwayland or window manager died! Exiting...\n'
    kill %sh %Xwayland 2>/dev/null
    wait ${cat_pid}
    exit 1
}

trap on_sigchld CHLD

coproc Xwayland -fullscreen -geometry "${resolution}" -displayfd 1
local xwayland_display
read <&p xwayland_display
export DISPLAY=":${xwayland_display}"
unset WAYLAND_DISPLAY
eprintf 'Xwayland display: %s\n' "${DISPLAY}"

cat <&p >&2
let cat_pid="${!}"

sh -c "exec -- ${window_manager}" &

sh -c "exec -- ${@}"
trap -
kill %sh %Xwayland

wait "${cat_pid}"

eprintf 'Child died! Exiting...\n'