#!/usr/bin/env bash

#############################################################################
##
##   Documentation automation script
##   Last updated on March 23, 2025
##
##   This file is part of Logtalk <https://logtalk.org/>
##   SPDX-FileCopyrightText: 1998-2025 Paulo Moura <pmoura@logtalk.org>
##   SPDX-License-Identifier: Apache-2.0
##
##   Licensed under the Apache License, Version 2.0 (the "License");
##   you may not use this file except in compliance with the License.
##   You may obtain a copy of the License at
##
##       http://www.apache.org/licenses/LICENSE-2.0
##
##   Unless required by applicable law or agreed to in writing, software
##   distributed under the License is distributed on an "AS IS" BASIS,
##   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
##   See the License for the specific language governing permissions and
##   limitations under the License.
##
#############################################################################

export LC_ALL=C

print_version() {
	echo "$(basename "$0") 2.7"
	exit 0
}

operating_system=$(uname -s)

if [ "${operating_system:0:10}" == "MINGW32_NT" ] ; then
	# assume that we're running on Windows using the Git for Windows bash shell
	extension='.sh'
elif [ "$LOGTALKHOME" != "" ] && [ "$LOGTALKUSER" != "" ] && [ "$LOGTALKHOME" == "$LOGTALKUSER" ] ; then
	# assume that we're running Logtalk without using the installer scripts
	extension='.sh'
else
	extension=''
fi

# first, make sure that we don't peak Windows own timeout command, which is not usable for this purpose
if [[ "$(command -v timeout)" == *"System32"* ]] || [[ "$(command -v timeout)" == *"system32"* ]] ; then
	timeout_command=""
# second, look for GNU coreutils package timeout command
elif [ -x "$(command -v timeout)" ] && [[ "$(timeout --version)" == *"GNU coreutils"* ]] ; then
	timeout_command="timeout -k 1"
elif [ -x "$(command -v gtimeout)" ] && [[ "$(gtimeout --version)" == *"GNU coreutils"* ]] ; then
	timeout_command="gtimeout -k 1"
else
	timeout_command=""
fi

# default argument values

base="$PWD"
results="$base/logtalk_doclet_logs"
backend=swipl
dot=""
prolog='SWI-Prolog'
logtalk=swilgt$extension
logtalk_call="$logtalk -g"
# disable timeouts to maintain backward compatibility
timeout=0
prefix="$HOME/"

run_doclets() {
	directory=$(dirname "$1")
	directory_short=${directory#$prefix}
	cd "$directory" || exit 1
	echo '*******************************************************************************'
	echo "***** Documenting $directory_short"
	name=${directory////__}
	run_doclet "$name" "$documenting_goal"
	doclet_exit=$?
	if [ $doclet_exit -eq 0 ] ; then
		return 0
	elif [ $doclet_exit -eq 124 ] ; then
		echo "*****         timeout"
		echo "LOGTALK_TIMEOUT" >> "$results/$name.errors"
	else
		echo "*****         crash"
		echo "LOGTALK_CRASH" >> "$results/$name.errors"
	fi
	return 0
}

run_doclet() {
	name="$1"
	goal="$2"
	if [ "$timeout_command" != "" ] && [ $timeout -ne 0 ] ; then
		$timeout_command $timeout $logtalk_call "$goal" -- "$@" < /dev/null > "$results/$name.results" 2> "$results/$name.errors"
	else
		$logtalk_call "$goal" -- "$@" < /dev/null > "$results/$name.results" 2> "$results/$name.errors"
	fi
	return $?
}

usage_help() {
	echo
	echo "This script automates running doclets found on the current directory and recursively"
	echo "in its sub-directories by scanning for doclet.lgt and doclet.logtalk source files. In"
	echo  "case of failed doclets or doclet errors, this script returns a non-zero exit code."
	echo
	echo "Usage:"
	echo "  $(basename "$0") -p prolog [-d results] [-t timeout] [-s prefix] [-- arguments]"
	echo "  $(basename "$0") -v"
	echo "  $(basename "$0") -h"
	echo
	echo "Required arguments:"
	echo "  -p backend Prolog compiler"
	echo "     (possible values are b, ciao, cx, eclipse, gnu, gnunc, ji, xvm, sicstus, swi, swipack, tau, trealla, xsb, and yap)"
	echo
	echo "Optional arguments:"
	echo "  -d directory to store the doclet logs (default is ./logtalk_doclet_logs)"
	echo "  -t timeout in seconds for running each doclet (default is $timeout; i.e. disabled)"
	echo "  -s suppress path prefix (default is $prefix)"
	echo "  -- arguments to be passed to the integration script used to run the doclets (no default)"
	echo "  -v print version"
	echo "  -h help"
	echo
}

while getopts "vp:m:f:d:t:s:h" option; do
	case $option in
		v) print_version;;
		p) p_arg="$OPTARG";;
		d) d_arg="$OPTARG";;
		t) t_arg="$OPTARG";;
		s) s_arg="$OPTARG";;
		h) usage_help; exit 0;;
		*) usage_help; exit 1;;
	esac
done

shift $((OPTIND - 1))

if [ "$p_arg" == "" ] ; then
	echo "Error! Backend Prolog compiler not specified!" >&2
	usage_help
	exit 1
elif [ "$p_arg" == "b" ] ; then
	prolog='B-Prolog'
	logtalk=bplgt$extension
	logtalk_call="$logtalk -g"
elif [ "$p_arg" == "ciao" ] ; then
	prolog='Ciao Prolog'
	logtalk=ciaolgt$extension
	logtalk_call="$logtalk -e"
elif [ "$p_arg" == "cx" ] ; then
	prolog='CxProlog'
	logtalk=cxlgt$extension
	logtalk_call="$logtalk --goal"
elif [ "$p_arg" == "eclipse" ] ; then
	prolog='ECLiPSe'
	logtalk=eclipselgt$extension
	logtalk_call="$logtalk -e"
elif [ "$p_arg" == "gnu" ] ; then
	prolog='GNU Prolog'
	logtalk=gplgt$extension
	logtalk_call="$logtalk --query-goal"
elif [ "$p_arg" == "gnunc" ] ; then
	prolog='GNU Prolog (native code)'
	logtalk=gplgtnc
	logtalk_call="$logtalk --query-goal"
elif [ "$p_arg" == "ji" ] ; then
	prolog='JIProlog'
	logtalk=jiplgt$extension
	logtalk_call="$logtalk -n -g"
elif [ "$p_arg" == "xvm" ] ; then
	prolog='XVM'
	logtalk=xvmlgt$extension
	logtalk_call="$logtalk -g"
	dot="."
elif [ "$p_arg" == "sicstus" ] ; then
	prolog='SICStus Prolog'
	logtalk=sicstuslgt$extension
	logtalk_call="$logtalk --goal"
	dot="."
elif [ "$p_arg" == "swi" ] ; then
	prolog='SWI-Prolog'
	logtalk=swilgt$extension
	logtalk_call="$logtalk -g"
elif [ "$p_arg" == "swipack" ] ; then
	prolog='SWI-Prolog'
	logtalk=swipl
	logtalk_call="$logtalk -g"
elif [ "$p_arg" == "tau" ] ; then
	prolog='Tau Prolog'
	logtalk=taulgt$extension
	logtalk_call="$logtalk -g"
elif [ "$p_arg" == "trealla" ] ; then
	prolog='Trealla Prolog'
	logtalk=tplgt$extension
	logtalk_call="$logtalk -g"
elif [ "$p_arg" == "xsb" ] ; then
	prolog='XSB'
	logtalk=xsblgt$extension
	logtalk_call="$logtalk -e"
	dot="."
elif [ "$p_arg" == "yap" ] ; then
	prolog='YAP'
	logtalk=yaplgt$extension
	logtalk_call="$logtalk -g"
elif [ "$p_arg" != "" ] ; then
	echo "Error! Unsupported backend Prolog compiler: $p_arg" >&2
	usage_help
	exit 1
elif [ ! "$(command -v $backend)" ] ; then
	echo "Error! Default backend Prolog compiler not found: $prolog" >&2
	usage_help
	exit 1
elif [ ! "$(command -v $logtalk)" ] ; then
	echo "Error! $logtalk integration script for $prolog not found." >&2
	echo "       Check that its directory is in your execution path." >&2
	exit 1
fi

if [ "$p_arg" == "swipack" ] ; then
	versions_goal="use_module(library(logtalk)),$versions_goal"
fi

if [ "$d_arg" != "" ] ; then
	results="$d_arg"
fi

if [ "$t_arg" != "" ] ; then
	timeout="$t_arg"
fi

if [ "$s_arg" != "" ] ; then
	prefix="$s_arg"
fi

if [ "$timeout_command" == "" ] ; then
	echo "Warning! Timeout support not available. The timeout option will be ignored." >&2
fi

# documenting goals

versions_goal="logtalk_load(library(tester_versions)),halt$dot"
documenting_goal="logtalk_load([doclet(loader),doclet]),halt$dot"

mkdir -p "$results"
rm -f "$results"/*.results
rm -f "$results"/*.errors
rm -f "$results"/errors.all
rm -f "$results"/tester_versions.txt

touch "$results"/*.results
touch "$results"/*.errors

start_date=$(eval date \"+%Y-%m-%d %H:%M:%S\")

echo '*******************************************************************************'
echo "***** Batch documentation processing started @ $start_date"
$logtalk_call $versions_goal > "$results"/tester_versions.txt 2> /dev/null
grep -a "Logtalk version:" "$results"/tester_versions.txt
grep -a "Prolog version:" "$results"/tester_versions.txt | sed "s/Prolog/$prolog/"

doclets=0
output="$(find "$base" -name "doclet.lgt" -or -name "doclet.logtalk")"
while read -r file && [ "$file" != "" ]; do
	((doclets++))
	run_doclets "$file"
done <<< "$output"

cd "$results" || exit 1
timeouts=$(cat -- *.errors | grep -c 'LOGTALK_TIMEOUT')
crashes=$(cat -- *.errors | grep -c 'LOGTALK_CRASH')
failures=$(cat -- *.results | grep -c 'failed')

echo "*******************************************************************************"
echo "***** Compilation errors/warnings and failed doclets"
echo "*******************************************************************************"
grep -s -a -A2 'syntax_error' -- *.results | sed 's/.results//' | tee errors.all
grep -s -a -A2 'syntax_error' -- *.errors | sed 's/.errors//' | tee -a errors.all
grep -s -a -h '!     ' -- *.errors | sed 's/.errors//' | tee -a errors.all
grep -s -a -h '!     ' -- *.results | sed 's/.results//' | tee -a errors.all
grep -s -a -h -F '*     ' -- *.errors | sed 's/.errors//' | tee -a errors.all
grep -s -a -h -F '*     ' -- *.results | sed 's/.results//' | tee -a errors.all
echo "*******************************************************************************"
echo "***** Timeouts"
echo "*******************************************************************************"
grep -s -a 'LOGTALK_TIMEOUT' -- *.errors | sed 's/LOGTALK_TIMEOUT//' | sed 's/.errors://' | sed 's|__|/|g' | sed "s|^$prefix||"
echo "*******************************************************************************"
echo "***** Crashes"
echo "*******************************************************************************"
grep -s -a 'LOGTALK_CRASH' -- *.errors | sed 's/LOGTALK_CRASH//' | sed 's/.errors://' | sed 's|__|/|g' | sed "s|^$prefix||"
echo "*******************************************************************************"
echo "***** Failures"
echo "*******************************************************************************"
grep -s -a 'failed' -- *.results
echo "*******************************************************************************"
echo "***** $doclets doclets: $timeouts timeouts, $crashes crashes, $failures failures"
echo "*******************************************************************************"

end_date=$(eval date \"+%Y-%m-%d %H:%M:%S\")

echo "***** Batch documentation processing ended @ $end_date"
echo '*******************************************************************************'

if [ "$crashes" -gt 0 ] ; then
	exit 7
elif [ "$timeouts" -gt 0 ] ; then
	exit 3
elif [ "$failures" -gt 0 ] ; then
	exit 1
else
	exit 0
fi
