#!/bin/sh
#
# Build a local rustdesk .deb, mirroring the packaging path used by the official
# CI (build.py build_flutter_deb: cargo --features flutter + flutter build linux
# + dpkg-deb). Intended for Debian/Ubuntu hosts.
#
# Flutter is downloaded into build/ inside this repo when it is not on PATH, and
# all other scratch dirs (vcpkg pkg-config shims, ...) live in build/ as well.
#
# Usage:
#   ./build_deb.sh [options] [build.py args...]
#
# Options (must come before build.py args):
#   --install-deps       apt-get install the system build dependencies (needs sudo)
#   --setup-vcpkg        clone + bootstrap vcpkg and install its dependencies
#   --linux-pkg-config   build against system pkg-config libs instead of vcpkg
#                        (needs libvpx-dev libopus-dev libaom-dev libyuv-dev)
#   --arch ARCH          DEB_ARCH for the deb (amd64, arm64, ...)
#   -h, --help           show this help
#
# Everything after the options is forwarded to ./build.py --flutter (e.g. --drm).

set -eu

REPO_ROOT=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
cd "$REPO_ROOT"

usage() {
  cat <<EOF
Usage: $0 [options] [build.py args...]

Builds a rustdesk .deb on this Debian/Ubuntu host. The Rust lib, the Flutter
bundle and the deb packaging all happen locally via ./build.py --flutter.

Options (must come before build.py args):
  --install-deps       apt-get install the system build dependencies (needs sudo)
  --setup-vcpkg        clone + bootstrap vcpkg and install its dependencies
  --linux-pkg-config   build against system pkg-config libs instead of vcpkg
                       (needs libvpx-dev libopus-dev libaom-dev libyuv-dev)
  --arch ARCH          DEB_ARCH for the deb (e.g. amd64, arm64)
  -h, --help           show this help

Remaining arguments are passed through to build.py (e.g. --drm).

Environment:
  VCPKG_ROOT       vcpkg checkout with installed/<triplet>/ populated (required
                   unless --setup-vcpkg or --linux-pkg-config is used)
EOF
}

install_deps=0
setup_vcpkg=0
linux_pkg_config=0
deb_arch=""

while [ "$#" -gt 0 ]; do
  case "$1" in
    --install-deps) install_deps=1; shift ;;
    --setup-vcpkg) setup_vcpkg=1; shift ;;
    --linux-pkg-config) linux_pkg_config=1; shift ;;
    --arch)
      if [ "$#" -lt 2 ]; then
        echo "error: --arch needs a value" >&2
        exit 1
      fi
      deb_arch="$2"; shift 2
      ;;
    -h|--help) usage; exit 0 ;;
    *) break ;;
  esac
done

if ! command -v dpkg-deb >/dev/null 2>&1; then
  echo "error: dpkg-deb not found - this script only builds .deb on Debian/Ubuntu-like hosts" >&2
  exit 1
fi

missing=0
for tool in python3 cargo git; do
  if ! command -v "$tool" >/dev/null 2>&1; then
    echo "error: missing required tool: $tool" >&2
    missing=1
  fi
done
if [ "$missing" -eq 1 ]; then
  echo "  cargo:   curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh" >&2
  exit 1
fi

# libs/hbb_common and friends are git submodules; the codegen and cargo build
# need them checked out.
git submodule update --init --recursive

# Install Flutter into a build dir inside this repo when it is not on PATH, so a
# bare Debian/Ubuntu host needs no system-wide Flutter. Pinned to the same 3.24.5
# CI uses; its dropdown_menu fix is applied to the SDK exactly like CI does.
FLUTTER_VERSION=${FLUTTER_VERSION:-3.24.5}
if ! command -v flutter >/dev/null 2>&1; then
  FLUTTER_DIR="$REPO_ROOT/build/flutter"
  if [ ! -x "$FLUTTER_DIR/bin/flutter" ]; then
    echo "Flutter not found on PATH; installing $FLUTTER_VERSION into $FLUTTER_DIR"
    mkdir -p "$REPO_ROOT/build"
    curl -fsSL --retry 3 \
      "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_VERSION}-stable.tar.xz" \
      -o "$REPO_ROOT/build/flutter.tar.xz"
    tar -xf "$REPO_ROOT/build/flutter.tar.xz" -C "$REPO_ROOT/build"
    rm -f "$REPO_ROOT/build/flutter.tar.xz"
    if [ "$FLUTTER_VERSION" = "3.24.5" ]; then
      git -C "$FLUTTER_DIR" apply "$REPO_ROOT/.github/patches/flutter_3.24.4_dropdown_menu_enableFilter.diff"
    fi
  fi
  export PATH="$FLUTTER_DIR/bin:$PATH"
  flutter config --no-analytics --enable-linux-desktop
fi
flutter --version

apt_pkgs="build-essential gcc g++ clang libclang-dev cmake ninja-build pkg-config
git curl wget unzip xz-utils zip nasm yasm tree rpm python3 libssl-dev
libgtk-3-dev libayatana-appindicator3-dev
libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
libasound2-dev libpulse-dev
libxcb-randr0-dev libxcb-shape0-dev libxcb-xfixes0-dev
libxdo-dev libxfixes-dev libva-dev"

if [ "$linux_pkg_config" -eq 1 ]; then
  apt_pkgs="$apt_pkgs libvpx-dev libopus-dev libaom-dev libyuv-dev"
fi

if [ "$install_deps" -eq 1 ]; then
  run_apt() {
    if [ "$(id -u)" -eq 0 ]; then apt-get "$@"; else sudo apt-get "$@"; fi
  }
  run_apt update
  # shellcheck disable=SC2086
  run_apt install -y $apt_pkgs
else
  missing_pkgs=""
  for pkg in $apt_pkgs; do
    if ! dpkg-query -W -f='${Status}' "$pkg" 2>/dev/null | grep -q "install ok installed"; then
      missing_pkgs="$missing_pkgs $pkg"
    fi
  done
  if [ -n "$missing_pkgs" ]; then
    echo "warning: some system packages seem to be missing:" >&2
    for pkg in $missing_pkgs; do
      echo "  $pkg" >&2
    done
    echo "re-run with --install-deps to install them" >&2
  fi
fi

if [ "$linux_pkg_config" -eq 1 ]; then
  for pkg in libvpx-dev libopus-dev libaom-dev libyuv-dev; do
    if ! dpkg-query -W -f='${Status}' "$pkg" 2>/dev/null | grep -q "install ok installed"; then
      echo "error: $pkg is required for --linux-pkg-config (install with --install-deps)" >&2
      exit 1
    fi
  done

  # Debian/Ubuntu's libyuv-dev ships no libyuv.pc; provide a minimal one so the
  # linux-pkg-config build can link the system library.
  if ! pkg-config --exists libyuv 2>/dev/null; then
    pcdir="$REPO_ROOT/build/pkgconfig"
    mkdir -p "$pcdir"
    libdir=$(dpkg -L libyuv-dev 2>/dev/null | grep '/libyuv\.so$' | head -n1 | xargs dirname)
    if [ -z "$libdir" ]; then
      libdir="/usr/lib/x86_64-linux-gnu"
    fi
    printf 'prefix=/usr\nexec_prefix=${prefix}\nlibdir=%s\nincludedir=${prefix}/include\n\nName: libyuv\nDescription: YUV scaling library\nVersion: 0.0\nLibs: -L${libdir} -lyuv\nCflags: -I${includedir}\n' "$libdir" > "$pcdir/libyuv.pc"
    export PKG_CONFIG_PATH="$pcdir${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}"
    echo "generated $pcdir/libyuv.pc (libdir=$libdir)"
  fi
fi

if [ "$setup_vcpkg" -eq 1 ]; then
  VCPKG_ROOT=${VCPKG_ROOT:-$REPO_ROOT/build/vcpkg}
  if [ ! -x "$VCPKG_ROOT/vcpkg" ]; then
    echo "vcpkg not found at $VCPKG_ROOT; cloning into $VCPKG_ROOT"
    mkdir -p "$REPO_ROOT/build"
    if [ -d "$VCPKG_ROOT" ]; then
      if [ ! -d "$VCPKG_ROOT/.git" ]; then
        echo "error: $VCPKG_ROOT exists but is not a vcpkg checkout; remove it or set VCPKG_ROOT" >&2
        exit 1
      fi
    else
      git clone https://github.com/microsoft/vcpkg "$VCPKG_ROOT"
    fi
  fi
  (cd "$VCPKG_ROOT" && ./bootstrap-vcpkg.sh)
  export VCPKG_ROOT
  "$VCPKG_ROOT/vcpkg" install --x-install-root="$VCPKG_ROOT/installed"
fi

if [ -z "${VCPKG_ROOT:-}" ] && [ -x "$REPO_ROOT/build/vcpkg/vcpkg" ]; then
  VCPKG_ROOT="$REPO_ROOT/build/vcpkg"
  export VCPKG_ROOT
fi

if [ "$linux_pkg_config" -eq 0 ] && { [ -z "${VCPKG_ROOT:-}" ] || [ ! -d "$VCPKG_ROOT/installed/x64-linux" ]; }; then
  echo "error: VCPKG_ROOT must point to a vcpkg checkout with installed/x64-linux populated" >&2
  echo "       set VCPKG_ROOT, run with --setup-vcpkg, or use --linux-pkg-config" >&2
  exit 1
fi

# generate flutter/lib/generated_bridge.dart from src/flutter_ffi.rs (build.py does
# not run the codegen itself). The codegen needs libclang.so, which newer distros
# ship outside its default search paths (only /usr/lib/llvm-9..14 are scanned).
LLVM_PATH=""
for d in /usr/lib/llvm-*; do
  if [ -f "$d/lib/libclang.so" ]; then
    LLVM_PATH="$d"
    break
  fi
done

rustup component add rustfmt 2>/dev/null || true
FRB=flutter_rust_bridge_codegen
if ! command -v "$FRB" >/dev/null 2>&1; then
  FRB="$HOME/.cargo/bin/flutter_rust_bridge_codegen"
fi
if [ ! -x "$FRB" ]; then
  cargo install flutter_rust_bridge_codegen --version 1.80.1 --features uuid --locked
  FRB="$HOME/.cargo/bin/flutter_rust_bridge_codegen"
fi
if [ -n "$LLVM_PATH" ]; then
  (cd flutter && flutter pub get && "$FRB" --llvm-path "$LLVM_PATH" \
    --rust-input ../src/flutter_ffi.rs --dart-output ./lib/generated_bridge.dart)
else
  (cd flutter && flutter pub get && "$FRB" \
    --rust-input ../src/flutter_ffi.rs --dart-output ./lib/generated_bridge.dart)
fi

if [ -n "$deb_arch" ]; then
  export DEB_ARCH="$deb_arch"
fi

if [ "$linux_pkg_config" -eq 1 ]; then
  # build the lib against system pkg-config libs, then package with --skip-cargo
  # (build.py has no flag for the linux-pkg-config feature)
  cargo build --locked --features flutter,linux-pkg-config --lib --release
  python3 build.py --flutter --skip-cargo "$@"
else
  python3 build.py --flutter "$@"
fi

echo
echo "Built:"
ls -1 rustdesk-*.deb