#!/usr/bin/env bash
#
# SPDX-License-Identifier: GPL-2.0
#
# Copyright (c) 2013-2026 Igor Pecovnik, igor@armbian.com
#
# This file is a part of the Armbian Build Framework
# https://github.com/armbian/build/

function cli_show_extensions_pre_run() {
	# Informational only: no host deps, no Docker relaunch, no board config needed; it just reads ${SRC}.
	# Skip the log tmpfs mount so a root invocation in an unprivileged container works too (no mount syscall).
	declare -g USE_TMPFS="${USE_TMPFS:-no}"
	return 0
}

function cli_show_extensions_run() {
	# Output format: "list" (default) prints the hook names, one per line, sorted.
	# "docs" prints a Markdown document with each hook's inline documentation.
	declare format="${SHOW_EXTENSIONS:-list}"

	display_alert "Scanning sources for extension hook points" "lib, extensions, config" "info"

	# Find every framework file that actually calls a hook, regardless of suffix or directory, so the
	# list stays exhaustive even for hooks declared outside lib/ and extensions/ (e.g. config/sources/*.conf).
	declare -a hook_files=()
	mapfile -t -d '' hook_files < <(grep -r -I -l --null "call_extension_method" "${SRC}/lib" "${SRC}/extensions" "${SRC}/config" 2> /dev/null)
	if [[ ${#hook_files[@]} -eq 0 ]]; then
		display_alert "No hook call sites found" "nothing to list" "warn"
		return 0
	fi

	# Statically parse each call site and its inline heredoc (the hook's documentation), the same data the
	# build's metadata collector records, but without needing a build to run first. Uses only POSIX awk
	# features (no gawk-specific asorti/PROCINFO) so it runs before host deps are installed; sorting and
	# formatting happen in the shell pipeline below.
	printf '%s\0' "${hook_files[@]}" |
		xargs -0 awk '
		BEGIN { dq = "\042"; FSEP = "\002"; NLEN = "\001" } # double-quote, field-sep and newline-placeholder bytes

		# Collect every double-quoted token from a string into arr[1..n]; returns n.
		function extract_quotes(s, arr,    i, c, inq, cur, n) {
			n = 0; inq = 0; cur = ""
			for (i = 1; i <= length(s); i++) {
				c = substr(s, i, 1)
				if (c == dq) {
					if (inq) { arr[++n] = cur; cur = ""; inq = 0 } else { inq = 1 }
				} else if (inq) { cur = cur c }
			}
			return n
		}

		FNR == 1 { in_heredoc = 0 } # a never-closed heredoc must not bleed across files

		{
			if (!in_heredoc) {
				# A real hook call starts the line (modulo indent): <tab>call_extension_method "name" [compat...] <<- DELIM
				# Anchoring on line start skips comments and code that merely mention the function name.
				if ($0 ~ /^[ \t]*call_extension_method[ \t]+\042/ && index($0, "<<") > 0) {
					call_part = substr($0, 1, index($0, "<<") - 1)
					n = extract_quotes(call_part, toks)
					if (n >= 1) {
						cur_hook = toks[1]
						gsub(/\$\{[^}]*\}/, "<branch>", cur_hook) # templated names like ${BRANCH,,} are not literal hooks
						compat = ""
						for (i = 2; i <= n; i++) compat = compat (compat == "" ? "" : " ") toks[i]

						# The heredoc delimiter is the first bare word after "<<", minus "-" and quotes.
						rest = substr($0, index($0, "<<") + 2)
						sub(/^-/, "", rest); sub(/^[ \t]+/, "", rest)
						gsub(/\042/, "", rest); gsub(/\047/, "", rest)
						split(rest, da, /[ \t]+/); delim = da[1]

						in_heredoc = 1; body = ""
					}
				}
				next
			}

			# Inside the heredoc body; a line equal to the delimiter (ignoring indent) closes it.
			line = $0; trimmed = line; sub(/^[ \t]+/, "", trimmed)
			if (trimmed == delim) {
				in_heredoc = 0
				print cur_hook FSEP compat FSEP body # one record per hook; sorted/formatted downstream
				next
			}
			sub(/^\t+/, "", line) # mimic "<<-" tab stripping
			body = (body == "" ? line : body NLEN line)
		}
	' | LC_ALL=C sort -u | format="${format}" awk -F "\002" '
		BEGIN {
			if (ENVIRON["format"] == "docs") {
				print "# Armbian build system extension hook points"
				print "- Generated by '\''./compile.sh show-extensions SHOW_EXTENSIONS=docs'\'' from the build sources."
				print "- Hooks are listed alphabetically; the build invokes them in code order, not this order."
				print ""
			}
		}
		!seen[$1]++ { # dedup hook points called from more than one site
			if (ENVIRON["format"] != "docs") { print $1; next }
			nlines = split($3, bl, "\001")
			summary = bl[1]
			sub(/^[ \t]*#+[ \t]*/, "", summary) # strip leading "#" so the summary cannot become a heading inside the blockquote
			print "### `" $1 "`"
			print "> " summary
			if (nlines >= 2) {
				print ""
				for (j = 2; j <= nlines; j++) print bl[j]
			}
			if ($2 != "") {
				print ""
				print "Also known as (for backwards compatibility only):"
				nc = split($2, ca, " ")
				for (j = 1; j <= nc; j++) print "- `" ca[j] "`"
			}
			print ""
		}
	'
}