#!/bin/bash
me=pipeasio-register

WINEPREFIX=${WINEPREFIX:=~/.wine}

wine=${WINE:-wine}

# The 64-bit arch whose <arch>-unix/<arch>-windows pair is registered: the
# host's, which is what the unixlib was built for (aarch64 on ARM64, #23).
arch=$(uname -m)
case ${arch} in
    amd64) arch=x86_64 ;;
    arm64) arch=aarch64 ;;
esac
arch=${PIPEASIO_ARCH:-${arch}}

# Host wine's first process in a prefix owned by another Wine (a Proton
# prefix, a Bottles bottle) migrates it to the host build (#22, #24).
if [ -z "${WINE:-}" ] \
   && { [ -e "${WINEPREFIX}/tracked_files" ] || [ -e "${WINEPREFIX}/../tracked_files" ]; }; then
    >&2 echo "[${me}] ${WINEPREFIX} is a Proton prefix; not running host wine in it."
    >&2 echo "[${me}] Host wine would migrate the prefix to its own build. Register through the runner:"
    >&2 echo "[${me}]   WINE=umu-run PROTONPATH=<runner dir> GAMEID=umu-<name> WINEPREFIX=${WINEPREFIX} ${me}"
    exit 1
fi
if [ -z "${WINE:-}" ] && [ -e "${WINEPREFIX}/bottle.yml" ]; then
    >&2 echo "[${me}] ${WINEPREFIX} is a Bottles bottle; not running host wine in it."
    >&2 echo "[${me}] Host wine would migrate the bottle to its own build. Register through Bottles:"
    >&2 echo "[${me}]   bottles-cli shell -b <bottle> -i \"regsvr32 /s pipeasio64.dll\"  (README: Bottles)"
    exit 1
fi

if [ ! -d "${WINEPREFIX}" ]; then
    "${wine}" wineboot -u || exit $?
fi

# Candidate install roots, in preference order. PIPEASIO_REGISTER_CANDIDATES
# replaces the built-in list (colon-separated); PIPEASIO_PREFIX is always
# searched first.
candidates=(
    "${PIPEASIO_PREFIX:+${PIPEASIO_PREFIX}/lib/wine}"
)
if [ -n "${PIPEASIO_REGISTER_CANDIDATES:-}" ]; then
    IFS=':' read -r -a extra_candidates <<< "${PIPEASIO_REGISTER_CANDIDATES}"
    candidates+=("${extra_candidates[@]}")
else
    candidates+=(
        "${HOME}/.local/lib/wine"
        "/usr/local/lib/wine"
        "/usr/lib/wine"
        "/usr/lib64/wine"
        "/usr/lib/${arch}-linux-gnu/wine"
        "/opt/wine-devel/lib/wine"
        "/opt/wine-devel/lib64/wine"
        "/opt/wine-stable/lib/wine"
        "/opt/wine-stable/lib64/wine"
        "/opt/wine-staging/lib/wine"
        "/opt/wine-staging/lib64/wine"
    )
fi

# Wine resolves a builtin's Unix half only in its own library dir and in
# WINEDLLPATH; derive that dir from the wine binary so a mismatched install
# root can be flagged instead of registering a driver that never loads.
# <root>/lib/wine is that dir only when <root>/bin holds a wine binary. With
# WINE naming a runner (umu-run, bottles-cli, flatpak run) command -v resolves
# the wrapper, so the lib/wine beside it belongs to an unrelated host Wine the
# runner never loads, and deriving it would outrank the user's own
# PIPEASIO_PREFIX (#26). A WINE that names a real binary (a Proton
# files/bin/wine, a self-built root) still resolves, both names checked
# because distributions ship either as the symlink or as its target.
wine_libdir=""
wine_cmd=$(command -v "${wine}" 2>/dev/null || true)
if [ -n "${wine_cmd}" ]; then
    wine_real=$(readlink -f "${wine_cmd}")
    is_wine=0
    for n in "${wine_cmd##*/}" "${wine_real##*/}"; do
        case ${n} in
            wine|wine32|wine64|wine-stable|wine-devel|wine-staging) is_wine=1 ;;
        esac
    done
    if ((is_wine)); then
        wine_root=$(dirname "$(dirname "${wine_real}")")
        # Older WineHQ builds keep 32-bit in lib/wine and 64-bit in lib64/wine,
        # so only accept a dir that carries the 64-bit layout. A PipeASIO-only
        # install creates those arch dirs bare, so require a real Wine payload.
        for d in "${wine_root}/lib/wine" "${wine_root}/lib64/wine" \
                 "${wine_root}/lib/${arch}-linux-gnu/wine"; do
            if [ -e "${d}/${arch}-unix/ntdll.so" ] || [ -e "${d}/${arch}-windows/ntdll.dll" ]; then
                wine_libdir=$(readlink -f "${d}")
                break
            fi
        done
    fi
fi

# Scan the derived library dir too: a custom Wine root (Bottles runners,
# self-built) is in no built-in candidate, yet wine loads from it before any
# WINEDLLPATH entry, so its PipeASIO copy must not stay invisible.
if [ -n "${wine_libdir}" ]; then
    candidates+=("${wine_libdir}")
fi

# Aliases (lib vs lib64 symlinks, PIPEASIO_PREFIX naming a literal candidate)
# must not register the same install twice.
found=()
for p in "${candidates[@]}"; do
    [ -z "${p}" ] && continue
    # Unix half: pipeasio64.so (PE + unixlib) or pipeasio64.dll.so (hybrid).
    if { [ -e "${p}/${arch}-unix/pipeasio64.so" ] || [ -e "${p}/${arch}-unix/pipeasio64.dll.so" ]; } \
       && [ -e "${p}/${arch}-windows/pipeasio64.dll" ]; then
        canon=$(readlink -f "${p}")
        dup=0
        for q in ${found[@]+"${found[@]}"}; do
            if [ "${q}" = "${canon}" ]; then
                dup=1
                break
            fi
        done
        ((dup)) || found+=("${canon}")
    fi
done
prefix=${found[0]:-}

# Wine searches its own library dir before every WINEDLLPATH entry, so when
# that dir also holds PipeASIO it is the copy that loads, whatever candidate
# order said. Use it, or the script would register one build while claiming
# another.
if [ -n "${wine_libdir}" ] && ((${#found[@]} > 1)); then
    for i in "${!found[@]}"; do
        if [ "${found[$i]}" = "${wine_libdir}" ] && ((i > 0)); then
            found=("${found[$i]}" "${found[@]:0:$i}" "${found[@]:$((i + 1))}")
            prefix=${found[0]}
            break
        fi
    done
fi

if [ -z "${prefix}" ]; then
    >&2 echo "[${me}] PipeASIO install not found in any candidate root (${arch}-unix/pipeasio64.so + ${arch}-windows/pipeasio64.dll)."
    >&2 echo "[${me}] Searched: ${candidates[*]}"
    >&2 echo "[${me}] Set PIPEASIO_PREFIX=<install-prefix> or run 'cmake --install'."
    exit 1
fi

if ((${#found[@]} > 1)); then
    >&2 echo "[${me}] warning: multiple PipeASIO installs found; using ${prefix}."
    >&2 echo "[${me}] ignored: ${found[*]:1}"
    >&2 echo "[${me}] remove the stale copies so an old build cannot win registration."
fi

>&2 echo "[${me}] using PipeASIO install at ${prefix}"

# Stage the PE half where regsvr32 can LoadLibrary it by name.
cp -v --no-preserve mode "${prefix}/${arch}-windows/pipeasio64.dll" \
                         "${WINEPREFIX}/drive_c/windows/system32/" || exit $?

# Let Wine find the ELF half next to the PE half.
export WINEDLLPATH="${prefix}${WINEDLLPATH:+:${WINEDLLPATH}}"
if [ "${PIPEASIO_REGISTER_WITHOUT_LOADING:-0}" = 1 ]; then
    "${wine}" reg add \
        'HKCR\CLSID\{2D3CA9E2-1193-4C5D-B5FD-38798F3DC074}' \
        /ve /t REG_SZ /d 'PipeASIO Object' /f /reg:64 &&
    "${wine}" reg add \
        'HKCR\CLSID\{2D3CA9E2-1193-4C5D-B5FD-38798F3DC074}\InprocServer32' \
        /ve /t REG_SZ /d 'pipeasio64.dll' /f /reg:64 &&
    "${wine}" reg add \
        'HKCR\CLSID\{2D3CA9E2-1193-4C5D-B5FD-38798F3DC074}\InprocServer32' \
        /v ThreadingModel /t REG_SZ /d Apartment /f /reg:64 &&
    "${wine}" reg add \
        'HKCU\Software\Wine\DllOverrides' \
        /v pipeasio64 /t REG_SZ /d builtin /f /reg:64 &&
    "${wine}" reg add \
        'HKLM\Software\ASIO\PipeASIO' \
        /v CLSID /t REG_SZ /d '{2D3CA9E2-1193-4C5D-B5FD-38798F3DC074}' /f /reg:64 &&
    "${wine}" reg add \
        'HKLM\Software\ASIO\PipeASIO' \
        /v Description /t REG_SZ /d 'PipeASIO Driver' /f /reg:64
    code=$?
else
    # /s: no modal success dialog - an undismissed box would block unattended
    # runs (fresh test prefixes, AFK registrations) forever.
    "${wine}" regsvr32 /s pipeasio64.dll
    code=$?
fi

# Optional 32-bit WoW64 registration.
if [ ${code} -eq 0 ] \
   && [ -e "${prefix}/i386-windows/pipeasio32.dll" ] \
   && [ -e "${prefix}/${arch}-unix/pipeasio32.so" ]; then
    >&2 echo "[${me}] registering experimental WoW64 32-bit front end"
    mkdir -p "${WINEPREFIX}/drive_c/windows/syswow64" || exit $?
    cp -v --no-preserve mode "${prefix}/i386-windows/pipeasio32.dll" \
                             "${WINEPREFIX}/drive_c/windows/syswow64/" || exit $?
    "${wine}" reg add \
        'HKCR\CLSID\{2D3CA9E2-1193-4C5D-B5FD-38798F3DC074}\InprocServer32' \
        /ve /t REG_SZ /d 'pipeasio32.dll' /f /reg:32 || exit $?
    "${wine}" reg add \
        'HKCR\CLSID\{2D3CA9E2-1193-4C5D-B5FD-38798F3DC074}\InprocServer32' \
        /v ThreadingModel /t REG_SZ /d Apartment /f /reg:32 || exit $?
    "${wine}" reg add \
        'HKCU\Software\Wine\DllOverrides' \
        /v pipeasio32 /t REG_SZ /d builtin /f /reg:32 || exit $?
    "${wine}" reg add \
        'HKLM\Software\ASIO\PipeASIO' \
        /v CLSID /t REG_SZ /d '{2D3CA9E2-1193-4C5D-B5FD-38798F3DC074}' /f /reg:32 || exit $?
    "${wine}" reg add \
        'HKLM\Software\ASIO\PipeASIO' \
        /v Description /t REG_SZ /d 'PipeASIO Driver' /f /reg:32 || exit $?
    >&2 echo "[${me}] 32-bit WoW64 front end registered"
fi

if [ ${code} -eq 0 ]; then
    >&2 echo "[${me}] registered in ${WINEPREFIX}"
    if [ "${prefix#"${HOME}"/}" != "${prefix}" ]; then
        >&2 echo "[${me}] for Proton/Faugus, set WINEDLLPATH=${prefix} in the launcher env."
    elif [ -n "${wine_libdir}" ] && [ "${prefix}" != "${wine_libdir}" ]; then
        # Registration succeeded, but the host will launch without this
        # script's WINEDLLPATH and wine will not look here for the Unix half.
        >&2 echo "[${me}] warning: ${prefix} is outside wine's library dir (${wine_libdir})."
        >&2 echo "[${me}] hosts will not load the driver unless WINEDLLPATH=${prefix}"
        >&2 echo "[${me}] is set at launch, or the driver is installed into ${wine_libdir}."
    fi
else
    >&2 echo "[${me}] regsvr32 failed (status ${code})"
fi
exit ${code}
