#!/usr/bin/env bash

set -e

export WINEDEBUG=-all

##################
## exec_verbose ##
##################

function exec_verbose {
    echo $@
    $@
}

#####################
## yes/no function ##
#####################

# The assumed answer (-y and -n flags)
assume=

function ask_yes_no {
    local answer=
    if [[ -z "$assume" ]]; then
        read answer
    else
        answer=$assume
    fi
    echo "$answer"
}

###########################################################
## Check for wine and wine-development and warn the user ##
## returns the wine binary path that we should use       ##
###########################################################

# $1 = wine binary path
function check_or_get_wine_bin_path {
    local wine_bin_path=$1

    if [[ -z "$wine_bin_path" ]]; then

        # if both wine-stable and wine-development are installed,
        # the user must specify which one to use
        if [[ -f "/usr/bin/wine-stable" ]] && [[ -f "/usr/bin/wine-development" ]]; then
            echo "Found both wine-stable and wine-development, please specify which one to use with --stable or --development." > /dev/stderr
            exit 1
        fi

        local path
        for path in /usr/lib/wine /usr/lib/wine-development /opt/wine-devel/bin /opt/wine-staging/bin; do
            if [ -d $path ]; then
                echo $path
                exit 0
            fi
        done

        echo "No wine installation could be detected"
        exit 1
    fi

    echo $wine_bin_path
}

############################################
## Check for WINEPREFIX and warn the user ##
############################################

# $1 = wine prefix
function check_wine_prefix {
    local wine_prefix=$1
    if [[ -z "$wine_prefix" ]]; then
        echo "WINEPREFIX is not set. Proceeding will use Wine's default prefix location, creating it if it does not exist. Continue? (y/n)"
        local continue=$(ask_yes_no)
        if [[ "$continue" != "y" ]] && [[ "$continue" != "Y" ]]; then
            exit 1
        fi
    else
        if ! [[ -f "$wine_prefix/system.reg" ]]; then
            echo "WINEPREFIX does not point to an existing wine installation. Proceeding will create a new one, continue? (y/n)"
            local continue=$(ask_yes_no)
            if [[ "$continue" != "y" ]] && [[ "$continue" != "Y" ]]; then
                exit 1
            fi
        fi
    fi
}

##################
## Install DXVK ##
##################

# $1 = action (install/uninstall)
# $2 = win / wine
# $3 = 32 / 64
function setup_dxvk {
    local action=$1
    local build_bits=$2

    local build_name=wine$build_bits
    local build_path="/usr/lib/dxvk/$build_name"

    local debian_package_name=dxvk-$build_name

    if [[ ! -d "$build_path" ]]; then
        echo "$debian_package_name not installed, skipping..."
        return
    fi

    # Default to system32.
    #
    # 64bit builds always go to system32. (Thank you Microsoft)
    #
    # We have to be careful here because winepath's redirect behavior is very tricky.
    # Avoid all confusion by never passing system32 or syswow64 to it.
    #
    local unix_windows_path=$( "$testwinebin" winepath -0 -u c:\\windows 2> /dev/null )
    local unix_dlls_path=$unix_windows_path/system32
    if [[ $build_bits == 32 ]]; then
        # 32bit builds go to syswow64 if it is present.
        if [[ -d $unix_windows_path/syswow64 ]]; then
            unix_dlls_path=$unix_windows_path/syswow64
        fi
    fi

    echo "installing $debian_package_name in the wine prefix..."

    "$action"_dll "$build_bits" "$build_path" "$unix_dlls_path" dxgi
    "$action"_dll "$build_bits" "$build_path" "$unix_dlls_path" d3d10
    "$action"_dll "$build_bits" "$build_path" "$unix_dlls_path" d3d9
    "$action"_dll "$build_bits" "$build_path" "$unix_dlls_path" d3d10_1
    "$action"_dll "$build_bits" "$build_path" "$unix_dlls_path" d3d10core
    "$action"_dll "$build_bits" "$build_path" "$unix_dlls_path" d3d11
}

# $1 = (64 / 32)
# $2 = /usr/lib/dxvk/<build-name>
# $3 = path to prefix dlls in the unix system
# $4 = dll name, for example dxgi
function install_dll {
    local build_bits=$1
    local build_path=$2
    local unix_dlls_path=$3
    local dll_name=$4

    local winebin=
    if [[ $build_bits = 64 ]]; then
        winebin=$wine$build_bits
    else
        winebin=$wine
        # 32 libs are always setup by this script but wine32 might not be installed
        if [ ! -e $winebin ]; then
            winebin=${wine}64
        fi
    fi

    echo "    [1/2] Creating registry override for $dll_name"
    echo -n "        "
    "$winebin" reg add 'HKEY_CURRENT_USER\Software\Wine\DllOverrides' /v "$dll_name" /d native /f

    echo "    [2/2] Creating file override for $dll_name"
    if [ -f "$unix_dlls_path/$dll_name.dll.old" ]; then
        echo "    override already in place"
    else
        mv "$unix_dlls_path/$dll_name.dll" "$unix_dlls_path/$dll_name.dll.old"
        ln -s "$build_path/$dll_name.dll.so" "$unix_dlls_path/$dll_name.dll"
    fi
}

# $1 = (64 / 32)
# $2 = /usr/lib/dxvk/<build-name>
# $3 = path to prefix dlls in the unix system
# $4 = dll name, for example dxgi
function uninstall_dll {
    local build_bits=$1
    local build_path=$2
    local unix_dlls_path=$3
    local dll_name=$4

    local winebin=
    if [[ $build_bits = 64 ]]; then
        winebin=$wine$build_bits
    else
        winebin=$wine
        # 32 libs are always setup by this script but wine32 might not be installed
        if [ ! -e $winebin ]; then
            winebin=${wine}64
        fi
    fi

    echo "    [1/2] Removing registry override for $dll_name"
    "$winebin" reg add 'HKEY_CURRENT_USER\Software\Wine\DllOverrides' /v "$dll_name" /d builtin /f 2>&1

    echo "    [2/2] Removing file override for $dll_name"
    if [ -f "$unix_dlls_path/$dll_name.dll.old" ]; then
        mv "$unix_dlls_path/$dll_name.dll.old" "$unix_dlls_path/$dll_name.dll"
    else
        echo "    override already removed"
    fi
}

###########################
## Check for the command ##
###########################

# $1 = the command to parse
function parse_cmd {
    local cmd=
    case "$1" in
    i|install)
        cmd=install
        ;;
    u|uninstall)
        cmd=uninstall
        ;;
    esac
    echo $cmd
}

################
## Print help ##
################

function print_help {
    echo "Unrecognized command."
    echo ""
    echo "Usage: $0 [command] [options]"
    echo ""
    echo "Commands:"
    echo "    i, install         install DXVK in $WINEPREFIX"
    echo "    u, uninstall       uninstall DXVK from $WINEPREFIX"
    echo ""
    echo "Options:"
    echo "    -s, --stable:              use wine-stable (Debian package)"
    echo "    -d, --development:         use wine-development (Debian package)"
    echo "    -u, --upstream:            use wine-devel (upstream package)"
    echo "    -u, --upstream-staging:    use wine-staging (upstream package)"
    echo "    -y, --yes:                 assume yes"
    echo "    -n, --no:                  assume no"
    exit 1
}

##########################################################################
############################## BEGIN SCRIPT ##############################
##########################################################################

# Get the command
cmd=$(parse_cmd $1)
if [[ -z "$cmd" ]]; then
    print_help
    exit 1
fi

################
## Parse args ##
################

wine_bin_path=

POSITIONAL=()
while [[ $# -gt 0 ]]; do

    case $1 in
    -y|--yes)
        assume='y'
        shift
        ;;
    -n|--no)
        assume='n'
        shift
        ;;
    -s|--stable)
        wine_bin_path=/usr/lib/wine
        shift
        ;;
    -d|--development)
        wine_bin_path=/usr/lib/wine-development
        shift
        ;;
    -u|--upstream)
        wine_bin_path=/opt/wine-devel/bin
        shift
        ;;
    -v|--upstream-staging)
        wine_bin_path=/opt/wine-staging/bin
        shift
        ;;
    *)
        POSITIONAL+=("$1")
        shift
        ;;
    esac
done
set -- "${POSITIONAL[@]}"

###################################
## Validate the wine binary path ##
###################################

wine_bin_path=$(check_or_get_wine_bin_path "$wine_bin_path")
wine=$wine_bin_path/wine

#########################
## Validate WINEPREFIX ##
#########################

check_wine_prefix "$WINEPREFIX"

##################
## Install DXVK ##
##################

completed=

# Find the architectures supported by the prefix
# and install/uninstall the corresponding builds.

# it does not seem to matter if 32 or 64 bit version is used for tests
# but they might not be both installed
testwinebin=$wine
if [ ! -e $testwinebin ]; then
    testwinebin=${wine}64
fi

if WINEARCH=win64 "$testwinebin" wineboot; then
    setup_dxvk "$cmd" 64
    completed=y
fi

# Removing this check for now as we should always be able to install
# the 32bit build. Even if wine64-development is installed without
# wine32-development, syswow64 should be present.
#
#if [[ -f "/usr/lib/$wine/wine" ]]; then
    setup_dxvk "$cmd" 32
    completed=y
#fi

if [[ -z "$completed" ]]; then
    echo "no action was completed"
    exit 1
fi
