#!/usr/bin/env bash
#
# Refresh the vendored Auto-07p Fortran code in auto/src, auto/
# include and auto/depends from the upstream auto-07p repository
# (https://github.com/auto-07p/auto-07p).
#
# For a full walkthrough of how this works and how to use it, see
# auto/README.md. Short version:
#
#   ./update
#
# copies every file listed in auto/vendor-manifest.txt from the
# commit recorded in auto/UPSTREAM_COMMIT, then reapplies every
# patch in auto/patches/. Run it from anywhere; it finds its own
# location and works out the rest.
#
# To update from a local clone of auto-07p instead of fetching from
# GitHub (useful when offline, or to test against an unreleased
# branch), set AUTO07P_SOURCE to the path of that clone:
#
#   AUTO07P_SOURCE=/path/to/auto-07p ./update
#
# This script only ever *reads* from AUTO07P_SOURCE (via `git show`,
# which reads a file's content at a specific commit without touching
# that repository's working tree or HEAD) - it is never fetched from,
# checked out, or otherwise modified. This is deliberate: it makes it
# safe to point AUTO07P_SOURCE at a clone you use for other purposes
# and don't want this script to change in any way.

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
AUTO_DIR="$SCRIPT_DIR"

UPSTREAM_URL="https://github.com/auto-07p/auto-07p.git"
PINNED_COMMIT="$(tr -d '[:space:]' < "$AUTO_DIR/UPSTREAM_COMMIT")"
MANIFEST="$AUTO_DIR/vendor-manifest.txt"
CACHE_DIR="$AUTO_DIR/.upstream-cache"
PATCH_DIR="$AUTO_DIR/patches"

log() { printf '%s\n' "$1"; }
die() { printf 'update: error: %s\n' "$1" >&2; exit 1; }

# ---------------------------------------------------------------------------
# Step 1: locate a local git object database that contains PINNED_COMMIT.
# ---------------------------------------------------------------------------

if [ -n "${AUTO07P_SOURCE:-}" ]; then
    SOURCE_REPO="$AUTO07P_SOURCE"
    log "Using local auto-07p clone at $SOURCE_REPO (AUTO07P_SOURCE is set)."
    git -C "$SOURCE_REPO" rev-parse --git-dir >/dev/null 2>&1 \
        || die "AUTO07P_SOURCE ($SOURCE_REPO) is not a git repository."
else
    SOURCE_REPO="$CACHE_DIR"
    if [ ! -d "$SOURCE_REPO" ]; then
        log "No cached clone found; cloning $UPSTREAM_URL into $SOURCE_REPO ..."
        # --bare: we only ever read file contents at a specific commit
        # (via `git show`), never check anything out, so a bare
        # (working-tree-less) clone is both sufficient and a clear
        # signal that this directory is a cache, not something to edit.
        git clone --bare --quiet "$UPSTREAM_URL" "$SOURCE_REPO"
    else
        log "Updating cached clone at $SOURCE_REPO ..."
        git -C "$SOURCE_REPO" fetch --quiet origin
    fi
fi

git -C "$SOURCE_REPO" cat-file -e "$PINNED_COMMIT" 2>/dev/null || die "commit $PINNED_COMMIT (from UPSTREAM_COMMIT) was not found in $SOURCE_REPO.
If you are using AUTO07P_SOURCE, fetch the latest history into that clone yourself (bifpy's update script never fetches into a clone you supply) and try again."

log "Vendoring from commit $PINNED_COMMIT."

# ---------------------------------------------------------------------------
# Step 2: copy every file listed in the manifest.
# ---------------------------------------------------------------------------

file_count=0
while IFS= read -r raw_line; do
    line="${raw_line%%#*}"                  # strip comments
    line="$(echo "$line" | xargs)"          # trim whitespace; "" if blank
    [ -z "$line" ] && continue

    read -r upstream_path local_path <<< "$line"

    dest="$REPO_ROOT/$local_path"
    mkdir -p "$(dirname "$dest")"
    git -C "$SOURCE_REPO" show "$PINNED_COMMIT:$upstream_path" > "$dest" \
        || die "could not read $upstream_path from $SOURCE_REPO at $PINNED_COMMIT."
    file_count=$((file_count + 1))
done < "$MANIFEST"

log "Copied $file_count files from the manifest."

# ---------------------------------------------------------------------------
# Step 3: reapply bifpy's own local patches on top of the fresh vendor copy.
# ---------------------------------------------------------------------------

shopt -s nullglob
patches=("$PATCH_DIR"/*.patch)
shopt -u nullglob

cd "$REPO_ROOT"
for patch in "${patches[@]}"; do
    log "Applying $(basename "$patch") ..."
    if ! git apply --check "$patch" 2>/dev/null; then
        die "$(basename "$patch") no longer applies cleanly - upstream likely changed the code it patches.
Regenerate it: revert the file(s) it touches, make the same edit again by hand against the new upstream code, then run:
  git diff -- <file> > $patch
See auto/README.md for the full procedure."
    fi
    git apply "$patch"
done

log "Done. Run 'git status' / 'git diff' to review changes, then rebuild ('make -C auto shared') and run the test suite."
