#!/usr/bin/env bash
# PhoneMic receiver for Linux.
#
#   curl -fsSL https://phonemicapp.com/iphone-mic-pc/linux/install.sh | bash
#   curl -fsSL https://phonemicapp.com/iphone-mic-pc/linux/install.sh | bash -s -- --headless
#   curl -fsSL https://phonemicapp.com/iphone-mic-pc/linux/install.sh | bash -s -- --uninstall
#
# Installs ~/.local/bin/phonemic-receiver and opens UDP 45333 + mDNS in firewalld
# or ufw. From a desktop session it installs the PhoneMic app (menu entry, tray
# icon, starts at sign-in); over SSH, or with --headless, it runs as a systemd
# --user service instead. --app forces the app. PHONEMIC_NO_FIREWALL=1 skips the
# firewall step, PHONEMIC_GAIN=4 starts it louder.
set -euo pipefail

VERSION=0.2.0
BASE=${PHONEMIC_BASE:-https://phonemicapp.com/iphone-mic-pc/linux}
BIN_DIR=$HOME/.local/bin
BIN=$BIN_DIR/phonemic-receiver
PORT=45333

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

as_root() {
    if [ "$(id -u)" -eq 0 ]; then "$@"
    elif command -v sudo >/dev/null; then sudo "$@"
    else return 1
    fi
}

missing_pipewire_hint() {
    . /etc/os-release 2>/dev/null || true
    case " ${ID:-} ${ID_LIKE:-} " in
        *" fedora "*|*" rhel "*) echo "sudo dnf install pipewire-libs" ;;
        *" debian "*|*" ubuntu "*) echo "sudo apt install libpipewire-0.3-0" ;;
        *" arch "*) echo "sudo pacman -S pipewire" ;;
        *" opensuse"*|*" suse "*) echo "sudo zypper install libpipewire-0_3-0" ;;
        *) echo "install your distro's PipeWire client library (libpipewire-0.3.so.0)" ;;
    esac
}

DATA_DIR=${XDG_DATA_HOME:-$HOME/.local/share}
CONFIG_DIR=${XDG_CONFIG_HOME:-$HOME/.config}

uninstall() {
    say "removing PhoneMic"
    if [ -x "$BIN" ]; then
        "$BIN" --quit >/dev/null 2>&1 || true
        "$BIN" --autostart off || true
    fi
    rm -f "$BIN" "$DATA_DIR/applications/phonemic.desktop" "$CONFIG_DIR/autostart/phonemic.desktop" \
        "$DATA_DIR/icons/hicolor/256x256/apps/phonemic.png"
    note "removed $BIN and the menu entry"
    note "firewall rules were left alone; to close them:"
    note "  firewalld: sudo firewall-cmd --permanent --remove-port=$PORT/udp && sudo firewall-cmd --reload"
    note "  ufw:       sudo ufw delete allow $PORT/udp"
    exit 0
}

main() {
    MODE=auto
    case "${1:-}" in
        --uninstall) uninstall ;;
        --headless) MODE=headless ;;
        --app) MODE=app ;;
        "") ;;
        *) die "unknown option ${1}. Use --app, --headless or --uninstall" ;;
    esac
    if [ "$MODE" = auto ]; then
        if [ -n "${WAYLAND_DISPLAY:-}${DISPLAY:-}" ]; then MODE=app; else MODE=headless; fi
    fi

    [ "$(uname -s)" = Linux ] || die "this installer is for Linux. Windows: https://phonemicapp.com/iphone-mic-pc/"
    case "$(uname -m)" in
        x86_64|amd64) ARCH=x86_64 ;;
        aarch64|arm64) ARCH=aarch64 ;;
        *) die "no build for $(uname -m) yet. Build from source: https://phonemicapp.com/iphone-mic-pc/linux/" ;;
    esac
    [ "$(id -u)" -ne 0 ] || die "run this as your normal user, not root. The mic has to live in your own audio session."

    if command -v curl >/dev/null; then fetch() { curl -fsSL "$1" -o "$2"; }
    elif command -v wget >/dev/null; then fetch() { wget -qO "$2" "$1"; }
    else die "needs curl or wget"
    fi

    say "downloading phonemic-receiver $VERSION ($ARCH)"
    tmp=$(mktemp -d)
    trap 'rm -rf "$tmp"' EXIT
    tarball=phonemic-receiver-$VERSION-linux-$ARCH.tar.gz
    fetch "$BASE/$tarball" "$tmp/$tarball" || die "download failed: $BASE/$tarball"
    fetch "$BASE/$tarball.sha256" "$tmp/$tarball.sha256" || die "download failed: $BASE/$tarball.sha256"
    (cd "$tmp" && sha256sum -c --quiet "$tarball.sha256") || die "checksum mismatch, not installing"
    tar -xzf "$tmp/$tarball" -C "$tmp"
    note "checksum ok"

    if ldd "$tmp/phonemic-receiver" 2>/dev/null | grep -q "libpipewire.*not found"; then
        die "PipeWire's client library is missing: $(missing_pipewire_hint)"
    fi
    "$tmp/phonemic-receiver" --version >/dev/null 2>&1 \
        || die "the binary does not run here (it needs glibc 2.35 or newer: Fedora 36+, Ubuntu 22.04+, Debian 12+)"

    say "installing"
    if [ -x "$BIN" ]; then
        "$BIN" --quit >/dev/null 2>&1 || true
        "$BIN" --autostart off >/dev/null 2>&1 || true
    fi
    install -Dm755 "$tmp/phonemic-receiver" "$BIN"
    note "$BIN"

    say "firewall"
    if [ -n "${PHONEMIC_NO_FIREWALL:-}" ]; then
        note "skipped (PHONEMIC_NO_FIREWALL set)"
    elif systemctl is-active --quiet firewalld 2>/dev/null; then
        note "firewalld is on: opening $PORT/udp and mdns (asks for your password)"
        if as_root firewall-cmd --permanent --add-port=$PORT/udp >/dev/null 2>&1 \
            && as_root firewall-cmd --permanent --add-service=mdns >/dev/null 2>&1 \
            && as_root firewall-cmd --reload >/dev/null; then
            note "opened"
        else
            note "could not change firewalld. Run these yourself, or the phone gets no audio through:"
            note "  sudo firewall-cmd --permanent --add-port=$PORT/udp"
            note "  sudo firewall-cmd --permanent --add-service=mdns"
            note "  sudo firewall-cmd --reload"
        fi
    elif systemctl is-active --quiet ufw 2>/dev/null; then
        note "ufw is on: allowing $PORT/udp and 5353/udp (asks for your password)"
        if as_root ufw allow $PORT/udp comment PhoneMic >/dev/null \
            && as_root ufw allow 5353/udp comment mDNS >/dev/null; then
            note "allowed"
        else
            note "could not change ufw. Run these yourself, or the phone gets no audio through:"
            note "  sudo ufw allow $PORT/udp"
            note "  sudo ufw allow 5353/udp"
        fi
    else
        note "no firewalld or ufw running, nothing to open"
    fi

    if [ ! -S "${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/pipewire-0" ]; then
        note "warning: PipeWire does not seem to be running in this session."
        note "Phone Mic needs a PipeWire desktop (Fedora, Ubuntu 22.10+, Arch and most 2023+ distros)."
    fi

    if [ "$MODE" = app ]; then
        say "app"
        "$BIN" --setup-desktop
        note "menu entry: PhoneMic (starts at sign-in, lives in the tray)"
        args=(--gui)
        [ -n "${PHONEMIC_GAIN:-}" ] && args+=(--gain "$PHONEMIC_GAIN")
        if [ -n "${WAYLAND_DISPLAY:-}${DISPLAY:-}" ]; then
            setsid "$BIN" "${args[@]}" >/dev/null 2>&1 < /dev/null &
            note "opened the PhoneMic window"
        else
            note "open PhoneMic from your app menu"
        fi
    else
        say "autostart"
        if systemctl --user show-environment >/dev/null 2>&1; then
            args=(--autostart on)
            [ -n "${PHONEMIC_GAIN:-}" ] && args+=(--gain "$PHONEMIC_GAIN")
            "$BIN" "${args[@]}" | sed 's/^/    /'
            sleep 2
            if systemctl --user is-active --quiet phonemic-receiver; then
                note "running"
            else
                note "the service did not stay up: journalctl --user -u phonemic-receiver -n 30"
            fi
        else
            note "no systemd user session; start it by hand: $BIN"
        fi
    fi

    case ":$PATH:" in
        *":$BIN_DIR:"*) ;;
        *) note "note: $BIN_DIR is not on your PATH (PhoneMic does not need it)" ;;
    esac

    host=$(cat /proc/sys/kernel/hostname 2>/dev/null || hostname)
    printf '\n\033[1mDone.\033[0m\n'
    printf '  1. Open PhoneMic on your phone (same Wi-Fi as this computer) and pick "%s".\n' "$host"
    printf '  2. In Zoom, Meet, Discord or OBS choose "Phone Mic" as the microphone.\n\n'
    printf '  Check:     %s --doctor\n' "$BIN"
    if [ "$MODE" = app ]; then
        printf '  Settings:  the PhoneMic window (app menu or tray icon)\n'
    else
        printf '  Logs:      journalctl --user -u phonemic-receiver -f\n'
    fi
    printf '  Uninstall: curl -fsSL %s/install.sh | bash -s -- --uninstall\n' "$BASE"
}

main "$@"
