#!/bin/bash
#
# Regenerate include/akgl/SDL_GameControllerDB.h from the community controller
# database.
#
# That header is tracked on purpose: it is the offline fallback that keeps the
# library buildable when upstream is renamed, rate-limited, taken down, or
# simply unreachable. This script therefore has one rule above all others --
# **it must never replace a good header with a worse one**. Until 0.5.0 it did
# exactly that: no `set -e`, no check on curl's exit status, so a failed fetch
# wrote an empty mappings file, overwrote the good tracked header with
# `AKGL_SDL_GAMECONTROLLER_DB_LEN 0` and an empty initializer, and exited 0.
# Verified by pointing it at an unresolvable host.
#
# Everything is assembled in a temporary directory and moved into place only
# once it has been checked. A failure leaves the existing header untouched and
# exits non-zero.
#
# This is no longer run by an ordinary build. `cmake --build build --target
# controllerdb` is the deliberate way to update the mappings, so a build needs
# no network access and does not leave the working tree dirty.
#
# Usage: mkcontrollermappings.sh <repository-root>

set -euo pipefail

AKGL_CONTROLLERDB_URL="${AKGL_CONTROLLERDB_URL:-https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/refs/heads/master/gamecontrollerdb.txt}"
# A floor, not a target. The database has carried well over two thousand entries
# for years; a few hundred means something truncated the response, which is a
# fetch that "succeeded" and still must not be committed.
AKGL_CONTROLLERDB_MIN_LINES="${AKGL_CONTROLLERDB_MIN_LINES:-1000}"

function die()
{
    echo "mkcontrollermappings: $*" >&2
    exit 1
}

if [[ $# -lt 1 ]]; then
    die "usage: $0 <repository-root>"
fi

rootdir="$1"
target="${rootdir}/include/akgl/SDL_GameControllerDB.h"

if [[ ! -d "${rootdir}/include/akgl" ]]; then
    die "no such directory: ${rootdir}/include/akgl"
fi
command -v curl >/dev/null 2>&1 || die "curl is not available"

workdir="$(mktemp -d)"

function cleanup()
{
    rm -rf "${workdir}"
}
trap cleanup EXIT

raw="${workdir}/raw.txt"
mappings="${workdir}/mappings.txt"
staged="${workdir}/SDL_GameControllerDB.h"

# --fail so an HTTP error status is an exit status rather than an error page in
# the body. Not a pipeline: the exit status being checked is curl's, and a
# pipeline reports the last command's.
if ! curl --fail --silent --show-error --location "${AKGL_CONTROLLERDB_URL}" --output "${raw}"; then
    die "fetch failed from ${AKGL_CONTROLLERDB_URL}; ${target} left as it was"
fi

grep -v '^#' "${raw}" | grep -v '^$' | sed s/',$'//g > "${mappings}" || true

filelen=$(wc -l < "${mappings}" | tr -d ' ')

if [[ "${filelen}" -lt "${AKGL_CONTROLLERDB_MIN_LINES}" ]]; then
    die "fetched only ${filelen} mappings, expected at least ${AKGL_CONTROLLERDB_MIN_LINES}; ${target} left as it was"
fi

# Every mapping becomes a C string literal, so a quote or a backslash would
# produce a header that does not compile -- or, worse, one that does.
if grep -q '["\\]' "${mappings}"; then
    die "fetched mappings contain a quote or a backslash; ${target} left as it was"
fi

{
    echo "#ifndef _AKGL_SDL_GAMECONTROLLERDB_H_"
    echo "#define _AKGL_SDL_GAMECONTROLLERDB_H_"
    echo ""
    echo "// Taken from ${AKGL_CONTROLLERDB_URL} on $(date)"
    echo ""
    echo "#define AKGL_SDL_GAMECONTROLLER_DB_LEN         ${filelen}"
    echo ""
    echo "const char *SDL_GAMECONTROLLER_DB[] = {"
    # One pass: wrap each mapping as a quoted, indented, comma-terminated entry,
    # then drop the trailing comma from the last. An empty initializer is a
    # constraint violation in ISO C that compiles only as a GCC extension, and
    # the length check above is what guarantees there is at least one entry.
    sed -e 's/.*/    "&",/' -e '$ s/,$//' "${mappings}"
    echo "};"
    echo "#endif // _AKGL_SDL_GAMECONTROLLERDB_H_"
} > "${staged}"

# Checked before it is allowed anywhere near the tracked copy.
if ! grep -q "^#define AKGL_SDL_GAMECONTROLLER_DB_LEN  *${filelen}\$" "${staged}"; then
    die "staged header did not come out as expected; ${target} left as it was"
fi

mv "${staged}" "${target}"
echo "mkcontrollermappings: wrote ${filelen} mappings to ${target}"
