#!/usr/bin/env bash

defaultcmd="mtgo"

image=docker.io/panard/mtgo:latest
docker_client=docker
limit_cpus=4
name=mtgo_running
cmd=$defaultcmd
cmdargs=""
opts=""
data="mtgo64-data-$USER"
do_sound=false
do_update=false

local_data="$HOME/.local/share/mtgo"
bind_documents="${local_data}/files"

usage() {
    echo "
Usage: $0 [opts] [image]

Options:
    -h, --help          display this message and exit
    --sound             enable sound (requires PulseAudio and Linux)
    --disable-sound     ensure that sound is disabled
    --cmd CMD           initial command (default: $cmd)
    --data-volume VOL   docker volume to store data (default: ${data})
    --reset             reset local data and docker volume
    --name NAME         container name (default: $name)
    --shell             start an interactive shell instead of MTGO
    --winecfg           run winecfg before calling MTGO
    --dry-run           simply print the docker run command
    --no-tz             disable timezone detection
    --debug             enable debug information
    --test              test environment
    --update            update docker image before run
    --limit-cpus N      limit number of CPUs (first N will be used)
    --bind PATH         bind given path to the user 'Documents' folder
    -e                  docker run option (environment)
    -v                  docker run option (mount volume)
    -u                  docker run option (change user)
"
}

lopts="dry-run,help,winecfg,shell,cmd:,name:"
lopts="${lopts},bind:"
lopts="${lopts},data-volume:,reset"
lopts="${lopts},sound,disable-sound"
lopts="${lopts},test"
lopts="${lopts},limit-cpus:"
lopts="${lopts},no-tz"
lopts="${lopts},update"
lopts="${lopts},debug"

getopt=getopt
if [[ $OSTYPE == darwin* ]]; then
    set -x
    getopt="$(brew ls --verbose gnu-getopt|grep '/bin/getopt')"
    if [ $? -ne 0 ]; then
        for d in "/opt/homebrew/opt/gnu-getopt/bin" "/usr/local/opt/gnu-getopt/bin"; do
            brew_getopt="${d}/getopt"
            if [ -x $brew_getopt ]; then
                getopt=$brew_getopt
                break
            fi
        done
    fi
fi

args="$($getopt -n "${0}" -o hv:u:e: --longoptions $lopts -- "${0}" "${@}")"
if [ $? -ne 0 ]; then
    usage
    exit 1
fi
eval set -- $args

do_run=true
do_reset=false
do_test=false

mytz=${TZ:-}
detect_tz=false
if [ -z "${mytz}" ]; then
    detect_tz=true
fi

while [ -n "${1:-}" ]; do
case "${1:-}" in
    --help|-h)  usage && exit 0 ;;
    --bind) shift
        bind_documents="$(realpath "$1")" ;;
    --data-volume) shift
        data="$1" ;;
    --debug)
        opts="${opts} -e WINEDEBUG=warn+all" ;;
    --dry-run)
        do_run=false ;;
    --winecfg)
        [[ "$cmd" == "mtgo" ]] && cmdargs="${cmdargs} $1";;
    --sound)
        [[ "$cmd" == "mtgo" ]] && cmdargs="${cmdargs} $1"
        image=panard/mtgo:sound
        do_update=true
        do_sound=true ;;
    --disable-sound)
        [[ "$cmd" == "mtgo" ]] && cmdargs="${cmdargs} $1" ;;
    --limit-cpus) shift
        limit_cpus="$1" ;;
    --reset)
        do_reset=true ;;
    --shell)
        cmd="bash"
        cmdargs=""
        opts="${opts} -it" ;;
    --test)
        local_data="$HOME/.local/share/mtgo-test"
        bind_documents="${local_data}/files"
        data="${data}-test"
        do_test=true ;;
    --cmd) shift;
        cmd="$1"
        cmdargs=""
        opts="${opts} -it" ;;
    --no-tz)
        detect_tz=false
        mytz="" ;;
     --name) shift;
        name="$1" ;;
     --update)
        do_update=true ;;
     -e|-v|-u)
        opts="${opts} $1 $2" ;;
     --) shift ; shift
        if [ -n "${1:-}" ]; then
            while [ -n "${2}" ]; do
                opts="${opts} ${1}"
                shift
            done
            image=$1
        fi ;;
esac
shift
done

echo "IMAGE=${image}"
if [ -n "${opts}" ]; then
    echo "CUSTOM DOCKER OPTS=${opts}"
fi

run() {
    echo "run(): ${@}" >&2
    if $do_run; then
        "${@}"
    fi
}

_host_uid=$(id -u)
if [[ ${_host_uid} == 0 ]]; then
    echo "Error: you should not run this script as root." >&2
    echo "Instead, consider adding your user to the 'docker' group:" >&2
    echo '    sudo usermod -aG docker \$USER' >&2
    echo "then restarting your session." >&2
    exit 1
fi

if $detect_tz; then
    if [ -f /etc/timezone ]; then
        mytz=$(</etc/timezone)
    elif [ -L /etc/localtime ]; then
        mytz=$(readlink /etc/localtime |cut -d/ -f 5-)
    elif [[ $OSTYPE == linux-gnu ]]; then
        _tz="$(timedatectl 2>/dev/null|grep "Time zone"|cut -d: -f2|cut -d' ' -f 2)"
        if [ -n ${_tz} ] && [[ "${_tz}" != "n/a" ]]; then
            mytz=$_tz
        fi
    fi
fi

run ${docker_client} info || exit 1
echo "UID=${UID}"

if $do_reset; then
    msg="You are about to wipe docker volume ${data}"
    echo "WARNING: $msg" >&2
    echo "Press Enter to continue, CTRL+C to abort" >&2
    read
    run ${docker_client} volume rm $data
fi

if $do_update; then
    echo "Updating image ${image}..." >&2
    run ${docker_client} pull ${image}
fi

${docker_client} volume inspect "$data" >/dev/null 2>&1
if [ $? -ne 0 ]; then
	set -e
	run ${docker_client} volume create "${data}"

	# migration
	old_data=mtgo-data
	set +e
	${docker_client} volume inspect ${old_data}>/dev/null 2>&1
	if [ $? -eq 0 ]; then
		set -e
		echo "Migrating from previous configuration..." >&2
		run ${docker_client} run --rm -v ${data}:/home/wine/.wine/host \
            -v ${old_data}:/data \
            -v ${local_data}:/ldata \
            -i ${image} bash<<EOF
cp -av /data/* /home/wine/.wine/host/
cp -v /ldata/*.reg /home/wine/.wine/host/
chown -R wine: /home/wine/.wine/host/
EOF
        run ${docker_client} volume rm ${old_data}
	else
		set -e
		run ${docker_client} run --rm -v ${data}:/home/wine/.wine/host ${image} true
	fi
fi
set -e

if [ ! -d "${local_data}" ]; then
    mkdir -p "${local_data}"
fi
if [ ! -d "${bind_documents}" ]; then
    mkdir -p "${bind_documents}"
fi

opts="${opts} -v ${data}:/home/wine/.wine/host/"
opts="${opts} -v ${data}:/home/wine/.wine/drive_c/users/"

reset_xhost=false
if [[ ${OSTYPE} == darwin* ]]; then
    iface=$(route -n get 0.0.0.0 2>/dev/null | awk '/interface: / {print $2}')
    echo "Using network interface '${iface}'" >&2
    ip=$(ifconfig $iface | grep inet | awk '$1=="inet" {print $2}')
    if [ -z ${ip} ]; then
        echo "FATAL: Cannot find your local IP address (iface=$iface)." >&2
        exit 1
    fi
    run open -a XQuartz
    echo "socat on $ip forwarding to $DISPLAY" >&2
    socat TCP4-LISTEN:6000,bind=$ip,range=$ip/32,reuseaddr,fork UNIX-CLIENT:\"$DISPLAY\" &
    socat_pid=$!
    sleep 2
    export DISPLAY=$ip:0
    opts="${opts} -e WINE_X11_NO_MITSHM=1"
    WEBBROWSER=open
else
    if [ ${_host_uid} -ne 1000 ]; then
        run xhost +local:1000
        reset_xhost=true
    fi
    XSOCK="/tmp/.X11-unix"
    XAUTH="${local_data}/Xauthority"
    > ${XAUTH}
    xauth nlist ${DISPLAY} | sed -e 's/^..../ffff/' | xauth -f ${XAUTH} nmerge -
    opts="${opts} -v ${XSOCK}:${XSOCK}:rw"
    opts="${opts} -v ${XAUTH}:/home/wine/.Xauthority:ro"
    WEBBROWSER=xdg-open
fi

if $do_sound; then
    if [[ ${OSTYPE} == darwin* ]]; then
        set +e
        pulseaudio --version >/dev/null 2>&1
        if [ $? -ne 0 ]; then
            echo "PulseAudio does not seem to be installed.  Attempting install..." >&2
            run brew install pulseaudio
        fi
        set -e
        pascript="$(pulseaudio --dump-conf|grep '^default-script-file = '|sed 's/^.*= *//')"
        echo "PAScript is $pascript" >&2
        pa_modified=false
        set +e
        pa_has_esound=$(grep "^load-module module-esound-protocol-tcp" "${pascript}")
        pa_has_native=$(grep "^load-module module-native-protocol-tcp" "${pascript}")
        set -e
        pa_enable_module() {
            echo "PulseAudio: enabling $1 module" >&2
            echo "load-module module-$1" | sudo tee -a "${pascript}"
        }
        if [ -z "${pa_has_esound}" ]; then
            pa_enable_module "esound-protocol-tcp"
            pa_modified=true
        fi
        if [ -z "${pa_has_native}" ]; then
            pa_enable_module "native-protocol-tcp"
            pa_modified=true
        fi
        pulseaudio --check && pulseaudio -k || true
        run pulseaudio -D
        opts="${opts} -v $HOME/.config/pulse:/home/wine/.config/pulse"
        opts="${opts} -e PULSE_SERVER=$ip"
    else
        _host_pulse="/run/user/${_host_uid}/pulse/native"
        if [ ! -S $_host_pulse ]; then
            echo "PulseAudio does not seem active (${_host_pulse} not found)" >&2
            run pulseaudio --start
        fi
        opts="${opts} -i -v ${_host_pulse}:/run/user/1000/pulse/native"
    fi
fi

opts="${opts} --net=host --ipc=host"
if [ -n "${mytz}" ]; then
    opts="${opts} -e TZ=/usr/share/zoneinfo/${mytz}"
fi

if [ -n "${name}" ]; then
    opts="${opts} --name ${name}"
fi

if [ -n "${bind_documents}" ]; then
    opts="${opts} -v ${bind_documents}:/home/wine/.wine/drive_c/users/wine/Documents"
fi
if [ ${limit_cpus} -gt 0 ]; then
    opts="${opts} --cpuset-cpus 0-$(( ${limit_cpus} - 1 ))"
fi

docker_mtgo() {
    #run ${docker_client} run --privileged --rm -e DISPLAY \
    run ${docker_client} run --rm -e DISPLAY \
        ${opts} ${image} ${cmd} ${cmdargs}
}
watch_openurl() {
    while read -r line; do
        echo "$line"
        if [[ "${line:0:8}" == "OPENURL " ]]; then
            url="${line:8}"
            run $WEBBROWSER "${url}"
        fi
    done
}

if [[ "${cmd}" == "mtgo" ]]; then
    docker_mtgo 2>&1 | watch_openurl
else
    docker_mtgo
fi

if $reset_xhost; then
    run xhost -local:1000
fi

if [[ ${OSTYPE} == darwin* ]]; then
    kill ${socat_pid}
fi