#!/bin/sh
# gen_port_stubs.sh — generate the unported-surface stub tree for vimlrs.
#
# For every vendored interpreter C file under vendor/, extract each function
# DEFINITION (column-0 return type + name + "(", not ending in ";") and emit a
# Rust stub carrying the real C name and a `vendor/<file>:<line>` citation. Stubs
# whose names are already hand-ported (anywhere under src/ported/ outside the
# generated stubs/ tree) are skipped, so the stub tree is exactly the surface
# that still needs porting. f_* builtins get the real uniform eval-func
# signature; other functions get a nullary placeholder with the verbatim C
# prototype recorded in the doc comment.
#
# This is a STUB GENERATOR, never an audit/report tool: it does not compute or
# emit any "how done" number. Run under sh (zsh shadows grep/sed).
set -eu

ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$ROOT"

VENDOR="vendor"
OUT="src/ported/stubs"
FILES="vendor/eval.c vendor/ex_eval.c vendor/eval/buffer.c vendor/eval/decode.c \
vendor/eval/deprecated.c vendor/eval/encode.c vendor/eval/executor.c vendor/eval/fs.c \
vendor/eval/funcs.c vendor/eval/list.c vendor/eval/typval.c vendor/eval/userfunc.c \
vendor/eval/vars.c vendor/eval/window.c"

# ── 1. names already hand-ported (exclude stubs/ tree) ──
PORTED="$(mktemp)"
grep -rhoE '\b(pub )?fn [A-Za-z_][A-Za-z0-9_]*' src/ported \
  --exclude-dir=stubs 2>/dev/null \
  | sed -E 's/.*fn //' | sort -u > "$PORTED"

rm -rf "$OUT"
mkdir -p "$OUT"

TV="crate::ported::eval::typval_defs_h::typval_T"
MODS="$OUT/mod.rs"
{
  echo "//! Generated by scripts/gen_port_stubs.sh — DO NOT EDIT BY HAND."
  echo "//!"
  echo "//! The not-yet-ported surface of the vimlrs interpreter: one stub per"
  echo "//! Neovim C function definition (real name + \`vendor/<file>:<line>\`"
  echo "//! citation, \`unimplemented!()\` body). As a function is faithfully"
  echo "//! ported into the hand-written modules it drops out of this tree on the"
  echo "//! next regeneration. Counts here are the remaining work, nothing else."
} > "$MODS"

emit_file() {
  cfile="$1"
  base="$(basename "$cfile" .c)"
  rsf="$OUT/$base.rs"
  echo "pub mod $base;" >> "$MODS"

  {
    echo "//! STUB surface for \`$cfile\` — generated, do not edit."
    echo "#![allow(dead_code, unused_variables, non_snake_case, clippy::all)]"
    echo
  } > "$rsf"

  # Extract definitions: col-0, has '(', not ending ';', name before '(' is a
  # lowercase C identifier, not a control keyword.
  awk -v cfile="$cfile" -v tv="$TV" -v portedfile="$PORTED" '
    BEGIN {
      while ((getline n < portedfile) > 0) ported[n] = 1
      kw["if"]=1; kw["for"]=1; kw["while"]=1; kw["switch"]=1; kw["return"]=1
      kw["sizeof"]=1; kw["else"]=1; kw["do"]=1; kw["typedef"]=1; kw["struct"]=1
      kw["enum"]=1; kw["union"]=1; kw["case"]=1; kw["EXTERN"]=1
    }
    {
      line = $0
      # only column-0 starts (a letter or "static")
      if (line !~ /^[A-Za-z_]/) next
      if (line ~ /^#/) next
      p = index(line, "(")
      if (p == 0) next
      pre = substr(line, 1, p-1)
      # a definition signature line does not end in ; and has no = before (
      if (line ~ /;[ \t]*$/) next
      if (pre ~ /=/) next
      sub(/[ \t]+$/, "", pre)
      # name = trailing identifier of pre
      if (match(pre, /[A-Za-z_][A-Za-z0-9_]*$/) == 0) next
      name = substr(pre, RSTART, RLENGTH)
      if (name in kw) next
      # C function names are lowercase_snake (incl f_*); skip Type-like names
      if (name !~ /^[a-z_]/) next
      if (name in seen) next
      seen[name] = 1
      if (name in ported) next
      # capture the prototype up to the matching ) on this line (best effort)
      proto = line
      sub(/[ \t]*\{[ \t]*$/, "", proto)
      gsub(/"/, "\\\"", proto)
      cite = cfile ":" NR
      if (name ~ /^f_/) {
        printf "/// Port of `%s()` — `%s`. STUB: not yet ported.\n", name, cite
        printf "pub fn %s(_argvars: &[%s], _rettv: &mut %s) { unimplemented!(\"STUB %s — %s\") }\n\n", name, tv, tv, name, cite
      } else {
        printf "/// Port of `%s()` — `%s`. STUB: not yet ported.\n", name, cite
        printf "/// C: `%s`\n", proto
        printf "pub fn %s() { unimplemented!(\"STUB %s — %s\") }\n\n", name, name, cite
      }
    }
  ' "$cfile" >> "$rsf"
}

for f in $FILES; do
  [ -f "$f" ] && emit_file "$f"
done

rm -f "$PORTED"

# ── summary (informational only, to stdout — not written into any tracked artifact) ──
total_c=0
for f in $FILES; do
  n=$(grep -cE '^[A-Za-z_]' "$f" 2>/dev/null || echo 0)
  total_c=$((total_c + 0))
done
stub_count=$(grep -rhE '^pub fn ' "$OUT" | wc -l | tr -d ' ')
echo "generated $stub_count stubs across $(ls "$OUT"/*.rs | grep -v mod.rs | wc -l | tr -d ' ') modules under $OUT"
