#!/usr/bin/env bash
# PhoneMic receiver for Linux.
#
#   curl -fsSL https://serdaroztetik.com/iphone-mic-pc/linux/install.sh | bash
#   curl -fsSL https://serdaroztetik.com/iphone-mic-pc/linux/install.sh | bash -s -- --uninstall
#
# Installs ~/.local/bin/phonemic-receiver, opens UDP 45333 + mDNS in firewalld
# or ufw, and starts it as a systemd --user service. PHONEMIC_NO_FIREWALL=1
# skips the firewall step, PHONEMIC_GAIN=4 starts it louder.
set -euo pipefail

VERSION=0.1.1
BASE=${PHONEMIC_BASE:-https://serdaroztetik.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
}

uninstall() {
    say "removing PhoneMic receiver"
    if [ -x "$BIN" ]; then "$BIN" --autostart off || true; fi
    rm -f "$BIN"
    note "removed $BIN"
    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() {
    [ "${1:-}" = "--uninstall" ] && uninstall

    [ "$(uname -s)" = Linux ] || die "this installer is for Linux. Windows: https://serdaroztetik.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://serdaroztetik.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.34 or newer: Fedora 35+, Ubuntu 22.04+, Debian 12+)"

    say "installing"
    if [ -x "$BIN" ]; then "$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

    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

    case ":$PATH:" in
        *":$BIN_DIR:"*) ;;
        *) note "note: $BIN_DIR is not on your PATH (the service does not care)" ;;
    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"
    printf '  Logs:      journalctl --user -u phonemic-receiver -f\n'
    printf '  Uninstall: curl -fsSL %s/install.sh | bash -s -- --uninstall\n' "$BASE"
}

main "$@"
