#!/bin/bash
me=pipeasio-register

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

wine=${WINE:-wine}

# 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/x86_64-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.
wine_libdir=""
wine_cmd=$(command -v "${wine}" 2>/dev/null || true)
if [ -n "${wine_cmd}" ]; then
    wine_root=$(dirname "$(dirname "$(readlink -f "${wine_cmd}")")")
    # 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/x86_64-linux-gnu/wine"; do
        if [ -e "${d}/x86_64-unix/ntdll.so" ] || [ -e "${d}/x86_64-windows/ntdll.dll" ]; then
            wine_libdir=$(readlink -f "${d}")
            break
        fi
    done
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
    if [ -e "${p}/x86_64-unix/pipeasio64.dll.so" ] \
       && [ -e "${p}/x86_64-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."
    >&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}/x86_64-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}/x86_64-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}
