#!/usr/bin/env bash
# Install + build the W65816 toolchain on top of llvm-mos:
#   1. Clone llvm-mos source.
#   2. Download the prebuilt llvm-mos-sdk (reference baseline).
#   3. Apply our W65816 backend INTO the clone (symlinks + patches).
#   4. Configure + build clang + llc + llvm-mc with the W65816 target.
#   5. Build link816 + omfEmit (the linker).
#   6. Build the runtime (libc.o, crt0.o, libgcc.o, etc.).
#
# Flags:
#   --build   (no-op; retained for backward compat — we always build)

set -euo pipefail
source "$(dirname "$0")/common.sh"

for arg in "$@"; do
    case "$arg" in
        --build) ;;  # no-op; we always build now (see step 4)
        *) die "unknown flag: $arg" ;;
    esac
done

LLVM_SRC="$TOOLS_DIR/llvm-mos"
LLVM_BUILD="$TOOLS_DIR/llvm-mos-build"
LLVM_SDK="$TOOLS_DIR/llvm-mos-sdk"
SDK_URL="https://github.com/llvm-mos/llvm-mos-sdk/releases/latest/download/llvm-mos-linux.tar.xz"

# 1. Source tree for backend development.
#
# IMPORTANT: this clone is ephemeral scaffolding, not where we do work. Our
# own sources live in $PROJECT_ROOT/src/ and $PROJECT_ROOT/patches/ and are
# stitched into this tree by applyBackend.sh. That means a destructive
# reset here would stomp applied symlinks/patches — so we refuse to touch
# the clone if it looks modified. Use updateLlvmMos.sh to refresh safely.
if [ -d "$LLVM_SRC/.git" ]; then
    currentBranch="$(git -C "$LLVM_SRC" rev-parse --abbrev-ref HEAD)"
    if [ "$currentBranch" != "main" ]; then
        warn "llvm-mos clone is on branch '$currentBranch' (not main); leaving untouched"
    elif ! git -C "$LLVM_SRC" diff --quiet || ! git -C "$LLVM_SRC" diff --cached --quiet; then
        warn "llvm-mos clone has local modifications; leaving untouched. Use scripts/updateLlvmMos.sh to refresh."
    else
        log "llvm-mos source already cloned; fetching and fast-forwarding main"
        git -C "$LLVM_SRC" fetch --depth=1 origin main
        git -C "$LLVM_SRC" merge --ff-only FETCH_HEAD || warn "fast-forward failed; leaving clone as-is"
    fi
else
    log "cloning llvm-mos (shallow)"
    git clone --depth=1 https://github.com/llvm-mos/llvm-mos.git "$LLVM_SRC"
fi

# 2. Prebuilt SDK for testing/reference (smoke tests against the
# vanilla 6502 MOS target; mostly unused once you have a W65816
# build).
if [ -x "$LLVM_SDK/bin/mos-common-clang" ] || [ -x "$LLVM_SDK/bin/clang" ]; then
    log "llvm-mos-sdk already extracted"
else
    archive="$(fetchCached "$SDK_URL" "llvm-mos-linux.tar.xz")"
    log "extracting llvm-mos-sdk"
    install -d "$LLVM_SDK"
    tar -xJf "$archive" -C "$LLVM_SDK" --strip-components=1
fi

# 3. Apply our W65816 backend INTO the clone (symlinks + patches).
# Must run BEFORE cmake configure so the W65816 target dir + cmake
# patch are present.
log "applying W65816 backend (symlinks + patches)"
bash "$(dirname "$0")/applyBackend.sh"

# 4. Configure + build LLVM with W65816 enabled.  We always build —
# without a built clang the rest of the toolchain (runtime, link816)
# can't produce any usable output.  --build is kept as a no-op flag
# for backward compat.
needCmd cmake
needCmd ninja
# Existence check covers the full LTO toolchain.  llvm-link / llvm-as /
# llvm-dis / opt are required by scripts/ltoLink.sh (Phase 5.2 of
# GAP_CLOSURE_PLAN.md); clang and llc are the always-required core.
if [ -x "$LLVM_BUILD/bin/clang" ] && \
   [ -x "$LLVM_BUILD/bin/llc" ] && \
   [ -x "$LLVM_BUILD/bin/llvm-link" ] && \
   [ -x "$LLVM_BUILD/bin/llvm-as" ] && \
   [ -x "$LLVM_BUILD/bin/llvm-dis" ] && \
   [ -x "$LLVM_BUILD/bin/opt" ] && \
   "$LLVM_BUILD/bin/llc" --version 2>/dev/null | grep -q "^[[:space:]]*w65816[[:space:]]"; then
    log "llvm-mos-build/bin/clang already exists and supports w65816 (LTO tools present)"
else
    log "configuring llvm-mos build (LLVM + clang + lld; ~5 min after the first cmake)"
    install -d "$LLVM_BUILD"
    cmake -S "$LLVM_SRC/llvm" -B "$LLVM_BUILD" -G Ninja \
        -DCMAKE_BUILD_TYPE=Release \
        -DLLVM_TARGETS_TO_BUILD="" \
        -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="MOS;W65816" \
        -DLLVM_ENABLE_PROJECTS="clang;lld" \
        -DLLVM_PARALLEL_LINK_JOBS=1 \
        -DLLVM_USE_LINKER=lld \
        -DLLVM_INCLUDE_TESTS=OFF \
        -DLLVM_INCLUDE_EXAMPLES=OFF \
        -DLLVM_INCLUDE_BENCHMARKS=OFF
    log "building clang, llc, llvm-mc, llvm-objdump (the tools we actually use)"
    # LTO chain: llvm-link merges bitcode, opt runs IR-level optimizations
    # (including the Layer 2 gate from Phase 1.12), llvm-as / llvm-dis
    # are the .bc <-> .ll round-trip for debugging.  Phase 5.2.
    ninja -C "$LLVM_BUILD" clang llc llvm-mc llvm-objdump llvm-readobj \
                          llvm-link llvm-as llvm-dis opt
    log "llvm build done: $LLVM_BUILD/bin/clang"
fi
# Sanity check: llc must list w65816 as a registered target.
if ! "$LLVM_BUILD/bin/llc" --version 2>/dev/null | grep -q "^[[:space:]]*w65816[[:space:]]"; then
    "$LLVM_BUILD/bin/llc" --version 2>/dev/null | head -20
    warn "llc built but does NOT list w65816 as a target.  Backend symlinks/patches may have failed.  Re-run scripts/applyBackend.sh and ninja -C tools/llvm-mos-build."
fi

# 5. Build link816 + omfEmit.
log "building link816 + omfEmit"
make -C "$PROJECT_ROOT/src/link816" all

# 6. Build the runtime (libc.o, crt0.o, libgcc.o, etc.).
log "building runtime"
bash "$PROJECT_ROOT/runtime/build.sh"

log "llvm-mos install done"
log "  source:    $LLVM_SRC"
log "  sdk:       $LLVM_SDK"
log "  build:     $LLVM_BUILD"
log "  clang:     $LLVM_BUILD/bin/clang"
log "  link816:   $PROJECT_ROOT/tools/link816"
log "  omfEmit:   $PROJECT_ROOT/tools/omfEmit"
