#!/bin/sh

set -eo pipefail

# snippet from https://github.com/mkropat/sh-realpath
realpath() {
    canonicalize_path "$(resolve_symlinks "$1")"
}
resolve_symlinks() {
    _resolve_symlinks "$1"
}
_resolve_symlinks() {
    _assert_no_path_cycles "$@" || return
    local dir_context path
    path=$(readlink -- "$1")
    if [ $? -eq 0 ]; then
        dir_context=$(dirname -- "$1")
        _resolve_symlinks "$(_prepend_dir_context_if_necessary "$dir_context" "$path")" "$@"
    else
        printf '%s\n' "$1"
    fi
}
_prepend_dir_context_if_necessary() {
    if [ "$1" = . ]; then
        printf '%s\n' "$2"
    else
        _prepend_path_if_relative "$1" "$2"
    fi
}
_prepend_path_if_relative() {
    case "$2" in
        /* ) printf '%s\n' "$2" ;;
         * ) printf '%s\n' "$1/$2" ;;
    esac
}
_assert_no_path_cycles() {
    local target path
    target=$1
    shift
    for path in "$@"; do
        if [ "$path" = "$target" ]; then
            return 1
        fi
    done
}
canonicalize_path() {
    if [ -d "$1" ]; then
        _canonicalize_dir_path "$1"
    else
        _canonicalize_file_path "$1"
    fi
}
_canonicalize_dir_path() {
    (cd "$1" 2>/dev/null && pwd -P)
}
_canonicalize_file_path() {
    local dir file
    dir=$(dirname -- "$1")
    file=$(basename -- "$1")
    (cd "$dir" 2>/dev/null && printf '%s/%s\n' "$(pwd -P)" "$file")
}

: ${DEFAULT_IMAGE:=ghcr.io/serverwentdown/env}
: ${DEFAULT_TAG:=fedora}
: ${CONTAIN_HOME:=/home/ambrose}
. ~/.config/contain

if [[ -f "$(which podman 2>/dev/null)" ]]; then
	runtime=podman
	runtime_extra_opts="$runtime_extra_opts--userns=keep-id "
elif [[ -f "$(which docker 2>/dev/null)" ]]; then
	runtime=docker
fi

if $runtime info | grep -iq selinux; then
	selinux_enabled=true
else
	selinux_enabled=false
fi

OPTIND=1

label="${0##*/}=true"
labels="--label $label "
env="--env TERM --env CONTAIN=true "
workdir=""
delete=""
name=""
ports=""
host=""
volumes="--volume /etc/localtime:/etc/localtime "
image="$DEFAULT_IMAGE"
tag="$DEFAULT_TAG"
caps=""

show_help() {
cat << EOF
Usage: ${0##*/} [-xwod] [-n NAME] [-p PORT]... [-v LIST]... [-i IMAGE] [-t TAG] [-c CAP]... [CMD]...
       ${0##*/} -l
       ${0##*/} -a NAME
       ${0##*/} -e NAME [CMD]...
       ${0##*/} -h
Starts an environment container. If CMD is specified, starts CMD instead of a shell.

   -x       delete Docker container after exit. will loose data
   -w       forward \$PWD into $CONTAIN_HOME/src, and start there
   -o       use host networking instead
   -d       desktop, forward wayland and xwayland
   -n NAME  give container a NAME
   -p PORT  forward host PORT to container PORT
   -v LIST  mount volume LIST. specify as LOCAL:MOUNT
   -c CAP   add capability CAP (example: SYS_PTRACE)
   -i IMAGE use IMAGE as container image instead
   -t TAG   use TAG as container tag instead

   -l       list running containers and exit
   -u       pull image and exit
   -a NAME  attach to a running container
   -e NAME  execute zsh or CMD on a running container
   -h       display this help and exit

EOF
}

list_running() {
	$runtime ps --all \
		--filter label="$label" \
		--format "table {{.Names}}\t{{.RunningFor}}\t{{.Ports}}\t{{.Command}}"
}

pull_image() {
	$runtime pull \
		$image:$tag
}

while getopts "xwodn:p:v:c:i:t:lua:e:h" opt; do
	case "$opt" in
	x)
		delete="--rm "
		;;
	w)
		workdir="--workdir $CONTAIN_HOME/src/ "
		if $selinux_enabled; then
			volume_opts=":z"
		fi
		mount="$(realpath "$PWD")"
		volumes="$volumes--volume \"$mount:$CONTAIN_HOME/src/$volume_opts\" "
		;;
	o)
		host="--net host "
		;;
	d)
		desktop=""
		if $selinux_enabled; then
			desktop="$desktop--security-opt label=disable "
		fi
		desktop="$desktop--env DISPLAY "
		desktop="$desktop--env WAYLAND_DISPLAY "
		# overwrites
		desktop="$desktop--env QT_QPA_PLATFORM=wayland "
		# xdg
		desktop="$desktop--env XDG_CURRENT_DESKTOP "
		desktop="$desktop--env XDG_SESSION_TYPE "
		desktop="$desktop--env XDG_SESSION_DESKTOP "
		desktop="$desktop--env XDG_RUNTIME_DIR=/run/container "
		# video and audio
		desktop="$desktop--volume \"$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY:/run/container/$WAYLAND_DISPLAY\" "
		desktop="$desktop--volume \"$XDG_RUNTIME_DIR/pipewire-0:/run/container/pipewire-0\" "
		desktop="$desktop--volume \"$XDG_RUNTIME_DIR/pulse:/run/container/pulse\" "
		desktop="$desktop--volume \"/tmp/.X11-unix:/tmp/.X11-unix\" "
		desktop="$desktop--device /dev/dri "
		desktop="$desktop--device /dev/snd "
		;;
	n)
		name="--name $OPTARG --hostname $OPTARG "
		;;
	p)
		ports="$ports--publish $OPTARG:$OPTARG "
		;;
	v)
		if $selinux_enabled; then
			volume_opts=":z"
		fi
		volumes="$volumes--volume $OPTARG$volume_opts "
		;;
	c)
		caps="$caps--cap-add $OPTARG "
		;;
	i)
		image="$OPTARG"
		;;
	t)
		tag="$OPTARG"
		;;
	l)
		list_running
		exit 0
		;;
	u)
		pull_image
		exit 0
		;;
	a)
		mode_attach="true"
		name="$OPTARG"
		;;
	e)
		mode_execute="true"
		name="$OPTARG"
		;;
	h)
		show_help
		exit 0
		;;
	:)
		echo "Option -$OPTARG requires an argument." >&2
		show_help
		exit 1
		;;
	\?)
		echo "Invalid option: -$OPTARG" >&2
		show_help
		exit 1
		;;
	esac
done

shift $((OPTIND-1))
[ "$1" = "--" ] && shift

if [ "$ports" = "\n\t" ]; then
	ports=""
fi

if [ "$mode_attach" = "true" ]; then
	$runtime attach \
		$name
	exit 0
fi

if [ "$mode_execute" = "true" ]; then
	cmd="$@"
	if [ -z "$@" ]; then
		cmd="/bin/zsh"
	fi
	$runtime exec --interactive --tty \
		$name \
		$cmd
	exit 0
fi

cmd="\
$runtime run --interactive --tty \
	$runtime_extra_opts\
	$labels$env\
	$delete\
	$name\
	$ports$host\
	$workdir$volumes\
	$caps\
	$desktop\
	$image:$tag\
	$@ \
"

echo Running: $cmd
exec sh -c "exec $cmd"
