#!/usr/bin/env bash
set -e

# Base directories
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
BINARIES_DIR="$PROJECT_ROOT/Binaries"
SUBMODULES_DIR="$PROJECT_ROOT/submodules"
FAISS_PREFIX="$BINARIES_DIR/faiss-prefix"

# CLEAN=1 forces a full rebuild of FAISS (otherwise we skip if already built).
CLEAN="${CLEAN:-0}"

mkdir -p "$BINARIES_DIR"

echo "====================================="
echo "Building FAISS (arm64 + Accelerate.framework)"
echo "====================================="
# We build FAISS from source instead of using the Homebrew bottle because the
# Homebrew formula links against OpenBLAS. We need Apple's Accelerate.framework
# so vector similarity routes through the AMX coprocessor on M-series CPUs.
# BLA_VENDOR=Apple makes CMake's FindBLAS pick up Accelerate.

if [ "$CLEAN" != "1" ] && [ -f "$FAISS_PREFIX/lib/libfaiss.dylib" ]; then
    echo "FAISS already built at $FAISS_PREFIX (set CLEAN=1 to rebuild)."
else
    cd "$SUBMODULES_DIR/faiss"
    rm -rf build
    mkdir build && cd build

    # OpenMP via Homebrew's libomp — AppleClang doesn't ship it. Same flags
    # we use for the COLMAP build below; FAISS's CMake also calls find_package(OpenMP).
    cmake .. \
        -DFAISS_ENABLE_GPU=OFF \
        -DFAISS_ENABLE_PYTHON=OFF \
        -DFAISS_ENABLE_C_API=OFF \
        -DFAISS_ENABLE_MKL=OFF \
        -DBLA_VENDOR=Apple \
        -DBUILD_TESTING=OFF \
        -DBUILD_SHARED_LIBS=ON \
        -DCMAKE_OSX_ARCHITECTURES=arm64 \
        -DCMAKE_OSX_DEPLOYMENT_TARGET=26.0 \
        -DCMAKE_BUILD_TYPE=Release \
        -DCMAKE_INSTALL_PREFIX="$FAISS_PREFIX" \
        -DOpenMP_C_FLAGS="-Xpreprocessor -fopenmp -I/opt/homebrew/opt/libomp/include" \
        -DOpenMP_C_LIB_NAMES="omp" \
        -DOpenMP_CXX_FLAGS="-Xpreprocessor -fopenmp -I/opt/homebrew/opt/libomp/include" \
        -DOpenMP_CXX_LIB_NAMES="omp" \
        -DOpenMP_omp_LIBRARY="/opt/homebrew/opt/libomp/lib/libomp.dylib"

    make -j"$(sysctl -n hw.ncpu)"
    make install

    if [ ! -f "$FAISS_PREFIX/share/faiss/faiss-config.cmake" ]; then
        echo "ERROR: FAISS install did not produce $FAISS_PREFIX/share/faiss/faiss-config.cmake"
        exit 1
    fi
    echo "FAISS installed to $FAISS_PREFIX"
fi

echo "====================================="
echo "Building msplat (Metal Gaussian Splat)"
echo "====================================="
MSPLAT_COMMIT=$(git -C "$PROJECT_ROOT" ls-tree HEAD submodules/msplat | awk '{print $3}')
MSPLAT_SRC="$(mktemp -d)/msplat"
# Build the commit the app records, never whatever branch the submodule has
# checked out; msplat and its default.metallib always come from one build.
git -C "$SUBMODULES_DIR/msplat" worktree add --detach "$MSPLAT_SRC" "$MSPLAT_COMMIT"
cmake -S "$MSPLAT_SRC" -B "$MSPLAT_SRC/build" -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_DEPLOYMENT_TARGET=26.0
cmake --build "$MSPLAT_SRC/build" -j"$(sysctl -n hw.ncpu)"
MSPLAT_BIN=$(find "$MSPLAT_SRC/build" -maxdepth 2 -name msplat -type f -perm +111 | head -1)
MSPLAT_LIB="$MSPLAT_SRC/build/default.metallib"
if [ -z "$MSPLAT_BIN" ] || [ ! -f "$MSPLAT_LIB" ]; then
    echo "error: msplat or default.metallib not built" >&2
    exit 1
fi
cp "$MSPLAT_BIN" "$MSPLAT_LIB" "$BINARIES_DIR/"
# The provenance pins follow the new build (Scripts/licenses/provenance.py checks them).
python3 - "$BINARIES_DIR" "$MSPLAT_COMMIT" "${MANUAL_JSON:-$PROJECT_ROOT/Scripts/licenses/manual.json}" <<'PY'
import hashlib, json, sys
b, commit, p = sys.argv[1], sys.argv[2][:7], sys.argv[3]
m = json.load(open(p))
for f in ("msplat", "default.metallib"):
    m["pinned_files"][f]["sha256"] = hashlib.sha256(open(f"{b}/{f}", "rb").read()).hexdigest()
m["components"]["msplat"]["version"] = commit
json.dump(m, open(p, "w"), indent=2)
PY
git -C "$SUBMODULES_DIR/msplat" worktree remove --force "$MSPLAT_SRC"
echo "Packaged msplat + default.metallib from $MSPLAT_COMMIT (minos $(vtool -show-build "$BINARIES_DIR/msplat" | awk '/minos/{print $2}'))"

echo "====================================="
echo "Building COLMAP (colmap-metal fork)"
echo "====================================="
# colmap-metal fork: GPU-accelerated SIFT extraction via Metal (no CUDA needed).
# Built against our locally-compiled FAISS so matching uses Accelerate, not OpenBLAS.
# ONNX off: MacSplat only runs SIFT; ONNX served ALIKED/LightGlue alone and
# pulled in onnxruntime, protobuf, abseil and re2 (~25 MB in the app).
cd "$SUBMODULES_DIR/colmap"
# The shipped colmap is the commit in Scripts/licenses/manual.json; building
# another one ships an untested SfM. NEW_COLMAP=1 builds the checkout anyway
# (then update manual.json and rerun Scripts/licenses/provenance.py).
COLMAP_HEAD=$(git rev-parse --short=8 HEAD)
COLMAP_SHIPPED=$(python3 -c "import json,sys;print(json.load(open(sys.argv[1]))['components']['colmap']['version'])" \
    "$PROJECT_ROOT/Scripts/licenses/manual.json")
if [ "$COLMAP_HEAD" != "$COLMAP_SHIPPED" ] && [ "${NEW_COLMAP:-0}" != "1" ]; then
    echo "error: submodules/colmap is at $COLMAP_HEAD, the shipped colmap is $COLMAP_SHIPPED." >&2
    echo "       git -C submodules/colmap checkout $COLMAP_SHIPPED, or set NEW_COLMAP=1." >&2
    exit 1
fi
rm -rf build
mkdir build && cd build

cmake .. -DCMAKE_BUILD_TYPE=Release \
    -DTESTS_ENABLED=OFF \
    -DCUDA_ENABLED=OFF \
    -DGUI_ENABLED=OFF \
    -DOPENGL_ENABLED=OFF \
    -DMETAL_ENABLED=ON \
    -DFETCH_FAISS=OFF \
    -DONNX_ENABLED=OFF \
    -DFETCH_ONNX=OFF \
    -Dfaiss_DIR="$FAISS_PREFIX/share/faiss" \
    -DCMAKE_OSX_ARCHITECTURES="arm64" \
    -DCMAKE_OSX_DEPLOYMENT_TARGET=26.0 \
    -DCMAKE_EXE_LINKER_FLAGS="-framework CoreML -framework Foundation" \
    -DOpenMP_C_FLAGS="-Xpreprocessor -fopenmp -I/opt/homebrew/opt/libomp/include" \
    -DOpenMP_C_LIB_NAMES="omp" \
    -DOpenMP_CXX_FLAGS="-Xpreprocessor -fopenmp -I/opt/homebrew/opt/libomp/include" \
    -DOpenMP_CXX_LIB_NAMES="omp" \
    -DOpenMP_omp_LIBRARY="/opt/homebrew/opt/libomp/lib/libomp.dylib"

make -j"$(sysctl -n hw.ncpu)"

if [ -f "src/colmap/exe/colmap" ]; then
    cp src/colmap/exe/colmap "$BINARIES_DIR/"
    echo "Successfully packaged colmap to $BINARIES_DIR"
elif [ -f "src/exe/colmap" ]; then
    cp src/exe/colmap "$BINARIES_DIR/"
    echo "Successfully packaged colmap to $BINARIES_DIR"
elif [ -f "colmap" ]; then
    cp colmap "$BINARIES_DIR/"
    echo "Successfully packaged colmap to $BINARIES_DIR"
else
    echo "Warning: Couldn't find colmap binary artifact."
fi

# The patched SiftMetal loader looks for sift.metallib next to the colmap
# binary first. The compile-time absolute path is unreachable when colmap is
# launched as a subprocess of a TCC-restricted parent (a macOS app launched
# via Xcode), so the metallib MUST also be packaged alongside the binary.
SIFT_METALLIB="src/thirdparty/SiftMetal/sift.metallib"
if [ -f "$SIFT_METALLIB" ]; then
    cp "$SIFT_METALLIB" "$BINARIES_DIR/"
    echo "Successfully packaged sift.metallib to $BINARIES_DIR"
else
    echo "Warning: $SIFT_METALLIB not found; SIFT Metal init will fail at runtime."
fi

echo "====================================="
echo "Making colmap self-contained (bundle dylibs, @loader_path, re-sign)"
echo "====================================="
# The freshly linked colmap references Homebrew and faiss-prefix by absolute
# path / @rpath — dead on any machine that isn't this one. Rewrite it before
# anything ships (see Scripts/make_colmap_selfcontained.sh).
bash "$PROJECT_ROOT/Scripts/make_colmap_selfcontained.sh"

echo "====================================="
echo "Dependencies built successfully."
echo "Binaries: $BINARIES_DIR"
echo "FAISS prefix (build artifact, gitignored): $FAISS_PREFIX"
echo "====================================="
echo "Verify Accelerate linkage:"
echo "  otool -L $BINARIES_DIR/colmap | grep -iE 'blas|accelerate|faiss'"
echo "  otool -L $FAISS_PREFIX/lib/libfaiss.dylib | grep -iE 'blas|accelerate'"
echo "Expect: Accelerate.framework present, no libopenblas anywhere."
