#!/bin/bash
# =============================================================
#  SonoBus (patched) — build on your Mac
#
#  Run this in Terminal:
#      bash build-on-mac.sh
#
#  It fetches the SonoBus source, applies the connection fix,
#  builds a universal (Apple Silicon + Intel) app and plugins,
#  signs them, and installs the plugins for your DAW.
#
#  Nothing to edit. Nothing to configure.
# =============================================================
set -euo pipefail

PATCH_URL="https://vanini.space/static/downloads/sonobus/sonobus-macos-dns-fix.patch"
WORK="$HOME/sonobus-build"
BRANCH="develop"          # 10.13 deployment target; `main` targets 10.10 which modern Xcode rejects

say()  { printf "\n\033[1m==> %s\033[0m\n" "$1"; }
die()  { printf "\n\033[1;31m!! %s\033[0m\n\n" "$1" >&2; exit 1; }

# ---------- prerequisites ----------
say "Checking prerequisites"
[ "$(uname -s)" = "Darwin" ] || die "This script must run on a Mac."

if ! xcode-select -p >/dev/null 2>&1; then
  die "Xcode command line tools are missing.
   Run this, let it finish, then re-run this script:

       xcode-select --install"
fi
echo "    Xcode tools: $(xcode-select -p)"

# CMake. If it isn't installed we fetch Kitware's official self-contained
# macOS build into the work folder and use it from there. No Homebrew, no
# installer, no admin password, and nothing left behind on the system.
#
# Pinned to the last 3.x: SonoBus needs >=3.15 and JUCE >=3.22, but CMake 4
# changed policy defaults in ways JUCE has been known to trip over.
CMAKE_VER="3.31.12"
CMAKE_BIN=""
if command -v cmake >/dev/null 2>&1; then
  CMAKE_BIN="$(command -v cmake)"
elif [ -x /Applications/CMake.app/Contents/bin/cmake ]; then
  CMAKE_BIN="/Applications/CMake.app/Contents/bin/cmake"
elif [ -x /opt/homebrew/bin/cmake ]; then
  CMAKE_BIN="/opt/homebrew/bin/cmake"
elif [ -x /usr/local/bin/cmake ]; then
  CMAKE_BIN="/usr/local/bin/cmake"
else
  # A copy downloaded by an earlier run of this script? Reuse it.
  #
  # Never extract over an existing CMake.app: since macOS 13, Terminal may not
  # overwrite files inside a signed app bundle without the "App Management"
  # permission, so re-extracting fails with "Operation not permitted" on every
  # file and aborts the whole build. Fresh extractions go into a new folder.
  DL_CMAKE="$HOME/sonobus-build/cmake-$CMAKE_VER-macos-universal/CMake.app/Contents/bin/cmake"
  if [ -x "$DL_CMAKE" ] && "$DL_CMAKE" --version >/dev/null 2>&1; then
    CMAKE_BIN="$DL_CMAKE"
    echo "    reusing cmake downloaded earlier"
  else
    say "cmake not found — downloading it (about 76 MB, one time)"
    mkdir -p "$HOME/sonobus-build"
    TARBALL="$HOME/sonobus-build/cmake-$CMAKE_VER.tar.gz"
    [ -s "$TARBALL" ] || curl -fL --progress-bar \
      "https://github.com/Kitware/CMake/releases/download/v$CMAKE_VER/cmake-$CMAKE_VER-macos-universal.tar.gz" \
      -o "$TARBALL" || die "Could not download cmake."
    DEST="$(mktemp -d "$HOME/sonobus-build/cmake-dl.XXXXXX")"
    tar -xzf "$TARBALL" -C "$DEST" || die "Could not unpack cmake."
    CMAKE_BIN="$DEST/cmake-$CMAKE_VER-macos-universal/CMake.app/Contents/bin/cmake"
    [ -x "$CMAKE_BIN" ] || die "cmake download unpacked but the binary is missing."
    # macOS quarantines downloaded archives; clear it or cmake refuses to run.
    xattr -dr com.apple.quarantine "$DEST" 2>/dev/null || true
  fi
fi
echo "    cmake: $("$CMAKE_BIN" --version | head -1)"
echo "    cpu cores: $(sysctl -n hw.logicalcpu)"

# ---------- source ----------
say "Fetching SonoBus source (branch: $BRANCH)"
mkdir -p "$WORK"
rm -rf "$WORK/src"
cd "$WORK"
git clone --depth 1 -b "$BRANCH" https://github.com/sonosaurus/sonobus.git src
cd src
echo "    at commit: $(git log -1 --oneline)"

say "Downloading and applying the fix"
curl -fsSL "$PATCH_URL" -o ../sonobus.patch || die "Could not download the patch from $PATCH_URL"
git apply --verbose ../sonobus.patch || die "The patch did not apply cleanly."
# prove it actually landed rather than trusting the exit code
grep -q getaddrinfo deps/aoo/lib/src/client.cpp   || die "Patch verification failed (getaddrinfo missing)."
grep -q inet_pton   deps/aoo/lib/src/net_utils.hpp || die "Patch verification failed (inet_pton missing)."
echo "    fix applied and verified"

# ---------- build ----------
say "Configuring"
"$CMAKE_BIN" -DCMAKE_BUILD_TYPE=Release -B build

say "Building — this takes 15-40 minutes. It is meant to be slow."
"$CMAKE_BIN" --build build --config Release -j "$(sysctl -n hw.logicalcpu)"

ART="$WORK/src/build/SonoBus_artefacts/Release"
[ -d "$ART" ] || die "Build finished but no artefacts at $ART"
# SonoBus is TWO plugins: "SonoBus" (effect) and "SonoBus Instrument". The
# instrument variant is a separate build target with its own artefact folder.
# Forgetting it means the DAW keeps loading an old system-wide copy of it.
ART_INST="$WORK/src/build/SonoBusInst_artefacts/Release"

# ---------- sign ----------
say "Signing (ad-hoc — required for arm64, no Apple account needed)"
for b in "$ART/VST3/SonoBus.vst3" "$ART_INST/VST3/SonoBusInstrument.vst3" "$ART/AU/SonoBus.component" "$ART/Standalone/SonoBus.app"; do
  [ -e "$b" ] && { codesign --force --deep -s - "$b" >/dev/null 2>&1 && echo "    signed $(basename "$b")"; }
done

# ---------- verify ----------
say "Verifying architectures"
for b in "$ART/VST3/SonoBus.vst3" "$ART_INST/VST3/SonoBusInstrument.vst3" "$ART/AU/SonoBus.component" "$ART/Standalone/SonoBus.app"; do
  f="$b/Contents/MacOS/$(basename "${b%.*}")"
  [ -e "$f" ] && { printf "    %-26s " "$(basename "$b")"; lipo -info "$f" 2>/dev/null | sed 's/.*are: //;s/.*is architecture: //'; }
done

# ---------- install ----------
say "Installing plugins"
mkdir -p "$HOME/Library/Audio/Plug-Ins/VST3" "$HOME/Library/Audio/Plug-Ins/Components"
PERM_HINT="macOS blocked replacing it (App Management protection).
   Fix: System Settings > Privacy & Security > App Management > enable Terminal,
   then re-run this script."
if [ -e "$ART/VST3/SonoBus.vst3" ]; then
  rm -rf "$HOME/Library/Audio/Plug-Ins/VST3/SonoBus.vst3" 2>/dev/null || true
  cp -R "$ART/VST3/SonoBus.vst3" "$HOME/Library/Audio/Plug-Ins/VST3/" \
    || die "Could not install the VST3. $PERM_HINT"
  echo "    VST3 -> ~/Library/Audio/Plug-Ins/VST3/  (SonoBus)"
fi
if [ -e "$ART_INST/VST3/SonoBusInstrument.vst3" ]; then
  rm -rf "$HOME/Library/Audio/Plug-Ins/VST3/SonoBusInstrument.vst3" 2>/dev/null || true
  cp -R "$ART_INST/VST3/SonoBusInstrument.vst3" "$HOME/Library/Audio/Plug-Ins/VST3/" \
    || die "Could not install the Instrument VST3. $PERM_HINT"
  echo "    VST3 -> ~/Library/Audio/Plug-Ins/VST3/  (SonoBus Instrument)"
fi
if [ -e "$ART/AU/SonoBus.component" ]; then
  rm -rf "$HOME/Library/Audio/Plug-Ins/Components/SonoBus.component" 2>/dev/null || true
  cp -R "$ART/AU/SonoBus.component" "$HOME/Library/Audio/Plug-Ins/Components/" \
    || die "Could not install the AU. $PERM_HINT"
  echo "    AU   -> ~/Library/Audio/Plug-Ins/Components/"
fi
if [ -e "$ART/Standalone/SonoBus.app" ]; then
  mkdir -p "$HOME/Applications"
  # An existing .app is exactly what App Management protects. Try to remove it;
  # if macOS refuses, move it out of the way instead; if even that fails, keep
  # the plugins (the part that matters for the DAW) and say so.
  if [ -e "$HOME/Applications/SonoBus.app" ]; then
    rm -rf "$HOME/Applications/SonoBus.app" 2>/dev/null \
      || mv "$HOME/Applications/SonoBus.app" "$HOME/.Trash/SonoBus-old-$$.app" 2>/dev/null \
      || true
  fi
  if [ -e "$HOME/Applications/SonoBus.app" ]; then
    echo "    App  -> SKIPPED: could not replace ~/Applications/SonoBus.app ($PERM_HINT)"
    echo "            The new app is still at: $ART/Standalone/SonoBus.app"
  else
    cp -R "$ART/Standalone/SonoBus.app" "$HOME/Applications/" \
      && echo "    App  -> ~/Applications/SonoBus.app" \
      || echo "    App  -> SKIPPED: could not copy ($PERM_HINT)"
  fi
fi

# Locally built files carry no quarantine flag, but a stray one costs nothing to clear.
xattr -dr com.apple.quarantine "$HOME/Library/Audio/Plug-Ins/VST3/SonoBus.vst3" 2>/dev/null || true
xattr -dr com.apple.quarantine "$HOME/Library/Audio/Plug-Ins/Components/SonoBus.component" 2>/dev/null || true
xattr -dr com.apple.quarantine "$HOME/Applications/SonoBus.app" 2>/dev/null || true

# An older official install in the SYSTEM-WIDE plug-in folders shadows the
# patched copies in ~/Library — the DAW may load the old one and the bug
# looks like it is "back". Moving them needs sudo, so print the exact lines.
STALE=""
for s in /Library/Audio/Plug-Ins/VST3/SonoBus.vst3 \
         /Library/Audio/Plug-Ins/VST3/SonoBusInstrument.vst3 \
         /Library/Audio/Plug-Ins/Components/SonoBus.component; do
  [ -e "$s" ] && STALE="$STALE$s"$'\n'
done
if [ -n "$STALE" ]; then
  STALE_MSG="  !! OLD system-wide copies found. Your DAW may load THESE instead of the
     patched ones. Move them aside (asks for your password), then rescan:

       sudo mkdir -p /Library/Audio/Plug-Ins/_disabled
"
  while IFS= read -r s; do
    [ -n "$s" ] && STALE_MSG="$STALE_MSG       sudo mv \"$s\" /Library/Audio/Plug-Ins/_disabled/
"
  done <<< "$STALE"
else
  STALE_MSG="  No old system-wide SonoBus copies found. Good."
fi

say "Done"
cat <<EOF

  Installed:
    ~/Library/Audio/Plug-Ins/VST3/SonoBus.vst3
    ~/Library/Audio/Plug-Ins/VST3/SonoBusInstrument.vst3
    ~/Library/Audio/Plug-Ins/Components/SonoBus.component
    ~/Applications/SonoBus.app

  In your DAW these appear as "SonoBus" and "SonoBus Instrument".
  Both are now the patched build.

$STALE_MSG

  Now quit your DAW completely and reopen it so it rescans plugins.

  Built from source on this Mac, so there is no quarantine flag and no
  Gatekeeper warning to work around.

  If SonoBus still fails to connect, the error message is now accurate --
  send it over and it will say what is actually being blocked.

  Build tree left at: $WORK  (delete it whenever you like)

EOF
