#!/bin/sh

set -eu

ROOT=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
BUILD_ROOT=${BUILD_ROOT:-"$ROOT/.static-build"}
DIST_DIR=${DIST_DIR:-"$ROOT/dist"}
JOBS=${JOBS:-$(getconf _NPROCESSORS_ONLN 2>/dev/null || printf '1')}

TOOLCHAIN_RELEASE=20260823
SDL_VERSION=3.4.14
SDL_IMAGE_VERSION=3.4.4
SDL_IMAGE_COMMIT=bec9134a26c7d0f31b36d6083c25296e04cabff5
NASM_VERSION=3.02

SDL_ARCHIVE="SDL3-$SDL_VERSION.tar.gz"
SDL_SHA256=30d4aa2b3037718142b32dffd4e72f917ebb6cc5227150e7bb9c45efb2153aeb
SDL_URL="https://github.com/libsdl-org/SDL/releases/download/release-$SDL_VERSION/$SDL_ARCHIVE"
NASM_ARCHIVE="nasm-$NASM_VERSION.tar.xz"
NASM_SHA256=87336eba53b4acfe917424ab5d500d2b0054d9f5148d35c2273ccf2cfb712f0d
NASM_URL="https://www.nasm.us/pub/nasm/releasebuilds/$NASM_VERSION/$NASM_ARCHIVE"

log() {
    printf '%s\n' "compile_static: $*" >&2
}

die() {
    log "error: $*"
    exit 1
}

require_command() {
    command -v "$1" >/dev/null 2>&1 || die "required command not found: $1"
}

for command_name in cmake curl file find git make ninja paste pkg-config readelf sha256sum tar; do
    require_command "$command_name"
done

mkdir -p "$BUILD_ROOT/downloads" "$BUILD_ROOT/sources" \
         "$BUILD_ROOT/toolchains" "$BUILD_ROOT/targets" "$DIST_DIR"

download_checked() {
    url=$1
    destination=$2
    expected=$3
    temporary="$destination.part"

    if [ -f "$destination" ] &&
       printf '%s  %s\n' "$expected" "$destination" | sha256sum -c - >/dev/null 2>&1; then
        return
    fi

    log "downloading $(basename "$destination")"
    curl -fL --retry 3 --retry-delay 2 "$url" -o "$temporary"
    printf '%s  %s\n' "$expected" "$temporary" | sha256sum -c - >/dev/null ||
        die "checksum mismatch for $(basename "$destination")"
    mv -f "$temporary" "$destination"
}

remove_cache_directory() {
    target=$1
    case "$target" in
        "$BUILD_ROOT"/*) rm -rf -- "$target" ;;
        *) die "refusing to remove path outside build cache: $target" ;;
    esac
}

extract_checked() {
    archive=$1
    destination=$2
    expected=$3
    stamp="$destination/.archive-sha256"
    temporary="$destination.tmp.$$"

    if [ -f "$stamp" ] && [ "$(sed -n '1p' "$stamp")" = "$expected" ]; then
        return
    fi

    remove_cache_directory "$temporary"
    mkdir -p "$temporary"
    tar -xf "$archive" -C "$temporary" --strip-components=1
    printf '%s\n' "$expected" >"$temporary/.archive-sha256"
    remove_cache_directory "$destination"
    mv "$temporary" "$destination"
}

download_checked "$SDL_URL" "$BUILD_ROOT/downloads/$SDL_ARCHIVE" "$SDL_SHA256"
extract_checked "$BUILD_ROOT/downloads/$SDL_ARCHIVE" \
                "$BUILD_ROOT/sources/SDL-$SDL_VERSION" "$SDL_SHA256"

download_checked "$NASM_URL" "$BUILD_ROOT/downloads/$NASM_ARCHIVE" "$NASM_SHA256"
extract_checked "$BUILD_ROOT/downloads/$NASM_ARCHIVE" \
                "$BUILD_ROOT/sources/nasm-$NASM_VERSION" "$NASM_SHA256"

NASM_PREFIX="$BUILD_ROOT/host-tools/nasm-$NASM_VERSION"
if [ ! -x "$NASM_PREFIX/bin/nasm" ]; then
    NASM_BUILD="$BUILD_ROOT/host-tools/build-nasm-$NASM_VERSION"
    remove_cache_directory "$NASM_BUILD"
    mkdir -p "$NASM_BUILD" "$NASM_PREFIX"
    log "building local NASM $NASM_VERSION"
    (
        cd "$NASM_BUILD"
        "$BUILD_ROOT/sources/nasm-$NASM_VERSION/configure" \
            --prefix="$NASM_PREFIX"
    )
    make -C "$NASM_BUILD" -j "$JOBS"
    make -C "$NASM_BUILD" install
fi
PATH="$NASM_PREFIX/bin:$PATH"
export PATH

SDL_IMAGE_SOURCE="$BUILD_ROOT/sources/SDL_image-$SDL_IMAGE_VERSION"
if [ ! -d "$SDL_IMAGE_SOURCE/.git" ]; then
    remove_cache_directory "$SDL_IMAGE_SOURCE"
    log "cloning SDL_image $SDL_IMAGE_VERSION and its pinned codec submodules"
    git clone --recursive --branch "release-$SDL_IMAGE_VERSION" \
        https://github.com/libsdl-org/SDL_image.git "$SDL_IMAGE_SOURCE"
fi

actual_image_commit=$(git -C "$SDL_IMAGE_SOURCE" rev-parse HEAD)
[ "$actual_image_commit" = "$SDL_IMAGE_COMMIT" ] ||
    die "cached SDL_image checkout is not the pinned commit; remove $SDL_IMAGE_SOURCE"
git -C "$SDL_IMAGE_SOURCE" submodule update --init --recursive

build_target() {
    triple=$1
    output_arch=$2
    processor=$3
    archive_sha=$4
    expected_machine=$5

    archive="$triple.tar.xz"
    archive_url="https://github.com/cross-tools/musl-cross/releases/download/$TOOLCHAIN_RELEASE/$archive"
    archive_path="$BUILD_ROOT/downloads/$archive"
    toolchain_root="$BUILD_ROOT/toolchains/$triple-$TOOLCHAIN_RELEASE"
    target_root="$BUILD_ROOT/targets/$triple"
    prefix="$target_root/prefix"
    toolchain_file="$target_root/toolchain.cmake"
    sdl_build="$target_root/build-sdl"
    image_build="$target_root/build-sdl-image"

    download_checked "$archive_url" "$archive_path" "$archive_sha"
    extract_checked "$archive_path" "$toolchain_root" "$archive_sha"

    cc=$(find "$toolchain_root" -path "*/bin/$triple-gcc" -print -quit)
    cxx=$(find "$toolchain_root" -path "*/bin/$triple-g++" -print -quit)
    [ -n "$cc" ] && [ -x "$cc" ] || die "compiler missing from $archive"
    [ -n "$cxx" ] && [ -x "$cxx" ] || die "C++ compiler missing from $archive"
    tool_bin=$(dirname "$cc")

    mkdir -p "$target_root"
    cat >"$toolchain_file" <<EOF
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR $processor)
set(CMAKE_C_COMPILER "$cc")
set(CMAKE_CXX_COMPILER "$cxx")
set(CMAKE_AR "$tool_bin/$triple-ar")
set(CMAKE_RANLIB "$tool_bin/$triple-ranlib")
set(CMAKE_STRIP "$tool_bin/$triple-strip")
set(CMAKE_FIND_ROOT_PATH "$prefix")
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
EOF

    log "building static SDL $SDL_VERSION for $output_arch"
    PKG_CONFIG_PATH= PKG_CONFIG_LIBDIR="$prefix/lib/pkgconfig" \
    cmake -S "$BUILD_ROOT/sources/SDL-$SDL_VERSION" -B "$sdl_build" -G Ninja \
        -DCMAKE_TOOLCHAIN_FILE="$toolchain_file" \
        -DCMAKE_BUILD_TYPE=Release \
        -DCMAKE_INSTALL_PREFIX="$prefix" \
        -DCMAKE_INSTALL_LIBDIR=lib \
        -DSDL_SHARED=OFF \
        -DSDL_STATIC=ON \
        -DSDL_INSTALL=ON \
        -DSDL_UNIX_CONSOLE_BUILD=ON \
        -DSDL_AUDIO=OFF \
        -DSDL_VIDEO=OFF \
        -DSDL_GPU=OFF \
        -DSDL_RENDER=OFF \
        -DSDL_CAMERA=OFF \
        -DSDL_JOYSTICK=OFF \
        -DSDL_HAPTIC=OFF \
        -DSDL_HIDAPI=OFF \
        -DSDL_POWER=OFF \
        -DSDL_SENSOR=OFF \
        -DSDL_DIALOG=OFF \
        -DSDL_TRAY=OFF \
        -DSDL_TEST_LIBRARY=OFF \
        -DSDL_TESTS=OFF \
        -DSDL_EXAMPLES=OFF
    cmake --build "$sdl_build" --parallel "$JOBS"
    cmake --install "$sdl_build"

    log "building static SDL_image $SDL_IMAGE_VERSION for $output_arch"
    log "SDL_image source=$SDL_IMAGE_SOURCE build=$image_build prefix=$prefix"
    PKG_CONFIG_PATH= PKG_CONFIG_LIBDIR="$prefix/lib/pkgconfig" \
    cmake -S "$SDL_IMAGE_SOURCE" -B "$image_build" -G Ninja \
        -DCMAKE_TOOLCHAIN_FILE="$toolchain_file" \
        -DCMAKE_BUILD_TYPE=Release \
        -DCMAKE_PREFIX_PATH="$prefix" \
        -DCMAKE_INSTALL_PREFIX="$prefix" \
        -DCMAKE_INSTALL_LIBDIR=lib \
        -DBUILD_SHARED_LIBS=OFF \
        -DSDLIMAGE_INSTALL=ON \
        -DSDLIMAGE_VENDORED=ON \
        -DSDLIMAGE_DEPS_SHARED=OFF \
        -DSDLIMAGE_STRICT=ON \
        -DSDLIMAGE_SAMPLES=OFF \
        -DSDLIMAGE_TESTS=OFF \
        -DDAV1D_ASM=OFF \
        -DSDL_UNIX_CONSOLE_BUILD=ON \
        -DSDL_AUDIO=OFF \
        -DSDL_VIDEO=OFF \
        -DSDL_GPU=OFF \
        -DSDL_RENDER=OFF \
        -DSDL_CAMERA=OFF \
        -DSDL_JOYSTICK=OFF \
        -DSDL_HAPTIC=OFF \
        -DSDL_HIDAPI=OFF \
        -DSDL_POWER=OFF \
        -DSDL_SENSOR=OFF \
        -DSDL_DIALOG=OFF \
        -DSDL_TRAY=OFF
    cmake --build "$image_build" --parallel "$JOBS"
    cmake --install "$image_build"

    pc_dirs=$(find "$prefix" -type d -name pkgconfig -print | paste -sd: -)
    [ -n "$pc_dirs" ] || die "no target pkg-config metadata installed for $output_arch"

    sdl_cflags=$(PKG_CONFIG_PATH= PKG_CONFIG_LIBDIR="$pc_dirs" \
        pkg-config --static --cflags sdl3 sdl3-image)
    sdl_platform_libs=$(PKG_CONFIG_PATH= PKG_CONFIG_LIBDIR="$pc_dirs" \
        pkg-config --static --libs-only-other sdl3)

    # SDL_image 3.4.4's generated .pc file leaves CMake generator expressions
    # in Libs.private for some vendored targets. Link the known, installed
    # static codec archives explicitly and group them to resolve circular
    # references deterministically.
    image_libs="-L$prefix/lib -Wl,--start-group -lSDL3_image -lavif -ldav1d -laom -lpng16 -ltiff -lwebpdemux -lwebpmux -lwebp -lsharpyuv -lz -lSDL3 -Wl,--end-group"

    output="$DIST_DIR/accent-extract-linux-$output_arch"
    test_output="$target_root/accent-extract-tests"

    log "linking $output"
    # Intentional field splitting: pkg-config returns compiler argument lists.
    # shellcheck disable=SC2086
    "$cc" -O2 -Wall -Wextra -Werror -std=c11 -ffunction-sections \
        -fdata-sections -I"$ROOT/src" -I"$ROOT/vendor" $sdl_cflags \
        "$ROOT/src/main.c" "$ROOT/src/image.c" "$ROOT/src/colour.c" \
        "$ROOT/src/candidates.c" "$ROOT/src/arena.c" \
        -static -Wl,--gc-sections -o "$output" $image_libs \
        $sdl_platform_libs -lm

    # shellcheck disable=SC2086
    "$cc" -O2 -Wall -Wextra -Werror -std=c11 -I"$ROOT/src" \
        -I"$ROOT/vendor" $sdl_cflags "$ROOT/tests/test.c" \
        "$ROOT/src/image.c" "$ROOT/src/colour.c" \
        "$ROOT/src/candidates.c" "$ROOT/src/arena.c" \
        -static -Wl,--gc-sections -o "$test_output" $image_libs \
        $sdl_platform_libs -lm

    "$tool_bin/$triple-strip" "$output"

    file "$output" | grep -F "$expected_machine" >/dev/null ||
        die "$output has the wrong architecture"
    file "$output" | grep -F 'statically linked' >/dev/null ||
        die "$output is not statically linked"
    if readelf -l "$output" | grep -q 'INTERP'; then
        die "$output contains a dynamic interpreter"
    fi
    if readelf -d "$output" 2>/dev/null | grep -q '(NEEDED)'; then
        die "$output contains dynamic dependencies"
    fi

    case "$output_arch" in
        amd64)
            log "running native amd64 tests"
            "$test_output"
            ;;
        arm64)
            if command -v qemu-aarch64 >/dev/null 2>&1; then
                log "running arm64 tests with qemu-aarch64"
                qemu-aarch64 "$test_output"
            else
                log "qemu-aarch64 not found; arm64 binary was link-verified only"
            fi
            ;;
    esac
}

build_target x86_64-unknown-linux-musl amd64 x86_64 \
    9752ecb10bafc0fc2ea75b3ed864a78137f3e5ba9b1579f1f16923d444c48096 \
    'x86-64'
build_target aarch64-unknown-linux-musl arm64 aarch64 \
    0fc483607d9ed83bdf75e7539bacc66721d7e37ca606377aed6a90cef82e45da \
    'ARM aarch64'

log "finished"
file "$DIST_DIR/accent-extract-linux-amd64" \
     "$DIST_DIR/accent-extract-linux-arm64"