#!/usr/bin/env sh
#
# td: "Telodendria Developer"
#
# A helper script that operates sort of like Make, except it is
# a lot easier to read and write than a Makefile, and it works on
# any POSIX system.
#
# I chose to use a custom build script instead of using Make because
# it is much more portable and flexible. This script doesn't only
# handle building the code, it also handles formatting it, as well
# as generating patch files.

#
# Set variables
#
# This syntax may look odd, but as far as I can tell, it is POSIX,
# and it allows the values to be overridden by the environment,
# such that these serve more as sane defaults than hard requirements.
#

: "${TELODENDRIA_VERSION:=0.0.0}"
: "${CVS_TAG:=Telodendria-$(echo $TELODENDRIA_VERSION | sed 's/\./_/g')}"

: "${DEFINES:=-D_DEFAULT_SOURCE -DTELODENDRIA_VERSION=\"$TELODENDRIA_VERSION\"}"
: "${INCLUDES:=-Isrc/include}"

: "${CC:=cc}"
: "${CFLAGS:=-Wall -Wextra -pedantic -std=c89 -O3}"
: "${LDFLAGS:=-lm -pthread -static -flto -fdata-sections -ffunction-sections -s -Wl,-static -Wl,-gc-sections}"
: "${PROG:=telodendria}"

CFLAGS="${CFLAGS} ${DEFINES} ${INCLUDES}"

. "$(pwd)/tools/lib/common.sh"

if [ "$DEBUG" = "1" ]; then
	CFLAGS="$CFLAGS -O0 -g"
	LDFLAGS="-lm -pthread -v"
	PROG="$PROG-debug"
fi

# Check the modificiation time of a file. This is used to do
# incremental builds; we only want to rebuild files that have
# have changed.
mod_time() {
    if [ -n "$1" ] && [ -f "$1" ]; then
        case "$(uname)" in
            Linux|CYGWIN_NT*)
                stat -c %Y "$1" 
                ;;
            *BSD|DragonFly)
                stat -f %m "$1" 
                ;;
            *)
				# Platform unknown, force rebuilding the whole
				# project every time.
                echo "0" 
                ;;
        esac 
    else
        echo "0" 
    fi 
}

# Build the source code, and generate  the 'build/telodendria'
# binary.
recipe_build() {
	mkdir -p build

	do_rebuild=0
	objs=""
	for src in $(find src -name '*.c'); do
		obj=$(echo "$src" | sed -e 's/^src/build/' -e 's/\.c$/\.o/')
		objs="$objs $obj"
    
		if [ $(mod_time "$src") -ge $(mod_time "$obj") ]; then
			echo "CC $obj"
			obj_dir=$(dirname "$obj")
			mkdir -p "$obj_dir"
			if ! $CC $CFLAGS -c -o "$obj" "$src"; then
				exit 1
			fi
			do_rebuild=1
		fi
	done

	if [ $do_rebuild -eq 1 ] || [ ! -f "build/$PROG" ]; then
		echo "LD build/$PROG"
		$CC $LDFLAGS -o "build/$PROG" $objs
	else
		echo "Up to date."
	fi
}

recipe_run() {
	if [ -f "build/$PROG" ]; then
		"build/$PROG" -f contrib/development.conf
	else
		echo "build/$PROG does not exist; build it first."
	fi
}

# Remove all build files, which can be regenerated by re-running the
# build recipe.
recipe_clean() {
	rm -rv build
}

# Format the source code by updating the copyright headers and
# then running indent(1) on each source code file.
recipe_format() {
	find src -name '*.c' -or -name '*.h' | while IFS= read -r src; do
		# Update the headers
		srcHeader=$(grep -n -m 1 '^ \*/' "$src" | cut -d ':' -f 1)
		head "-n$srcHeader" "$src" |
			diff -u -p - LICENSE.txt |
			patch "$src" | grep -v "^Hmm"

		# Format the source code
		if indent "$src"; then
			rm $(basename "$src").BAK
		fi
	done
}

# Deploy the Telodendria website by copying the required files to
# a web root defined by TELODENDRIA_PUB.
recipe_site() {
	if [ -z "$TELODENDRIA_PUB" ]; then
		echo "No public root directory specified."
		echo "Set TELODENDRIA_PUB."
		exit 1
	fi

	cp -v site/* "$TELODENDRIA_PUB/"

	find man/ -name '*.[1-9]' | while IFS= read -r man; do
		dir=$(dirname "$man")
		html=$(basename "$man")

		mkdir -p "$TELODENDRIA_PUB/$dir/"
		mandoc -Thtml -O style=/style.css,man=/man/man%S/%N.%S.html "$man" > "$TELODENDRIA_PUB/$dir/$html.html"
		echo "$man -> $TELODENDRIA_PUB/$dir/$html.html"
	done
}

# Generate a release tarball, checksum and sign it, and push it to
# the web root.
recipe_release() {
	if [ -z "$TELODENDRIA_PUB" ]; then
		echo "No public root directory specified."
		echo "Set TELODENDRIA_PUB."
		exit 1
	fi

	mkdir -p "$TELODENDRIA_PUB/pub/v$TELODENDRIA_VERSION"
	cd "$TELODENDRIA_PUB/pub/v$TELODENDRIA_VERSION"

	# Generate release tarball
	cvs export "-r$CVS_TAG" "Telodendria"
	mv "Telodendria" "Telodendria-v$TELODENDRIA_VERSION"
	tar -czf "Telodendria-v$TELODENDRIA_VERSION.tar.gz" \
		"Telodendria-v$TELODENDRIA_VERSION"
	rm -r "Telodendria-v$TELODENDRIA_VERSION"

	# Checksum release tarball
	sha256 "Telodendria-v$TELODENDRIA_VERSION.tar.gz" \
		> "Telodendria-v$TELODENDRIA_VERSION.tar.gz.sha256"

	# Sign release tarball
	if [ ! -z "$TELODENDRIA_SIGNIFY_SECRET" ]; then
		signify -S -s "$TELODENDRIA_SIGNIFY_SECRET" \
			-m "Telodendria-v$TELODENDRIA_VERSION.tar.gz" \
			-x "Telodendria-v$TELODENDRIA_VERSION.tar.gz.sig"
	else
		echo "Warning: TELODENDRIA_SIGNIFY_SECRET not net."
		echo "The built tarball will not be signed."
	fi
}

# Generate a formatted patch file. The Telodendria project isn't
# really picky about how patches look, but this is how we like them
# best. Makes them easy to read.
recipe_patch() {

	# If the user has not set their MXID, try to deduce one from
	# their system.
	if [ -z "$MXID" ]; then
		MXID="@${USER}:$(hostname)"
	fi

	# If the user has not set their DISPLAY_NAME, try to deduce it
	# from their system.
	if [ -z "$DISPLAY_NAME" ]; then
		DISPLAY_NAME=$(getent passwd "$USER" | cut -d ':' -f 5 | cut -d ',' -f 1)
	fi

	# If the user has not set their EDITOR, use a safe default.
	# (vi should be available on any POSIX system)
	if [ -z "$EDITOR" ]; then
		EDITOR=vi
	fi

	NORMALIZED_MXID=$(echo "$MXID" | sed -e 's/@//g' -e 's/:/-/g')
	PATCH_FILE="${NORMALIZED_MXID}_$(date +%s).patch"

	# Generate the actual patch file
	# Here we write some nice headers, and then do a cvs diff.
	(
		printf 'From: "%s" <%s>\n' "$DISPLAY_NAME" "$MXID"
		echo "Date: $(date)"
		echo "Subject: "
		echo
		echo "[ ] I have read the Telodendria Project developer certificate"
		echo "    of origin, and certify that I have permission to submit"
		echo "    this patch under the conditions specified in it."
		echo
		cvs -q diff -uNp $PATCHSET | grep -v '^\? '
	) > "$PATCH_FILE"

	"$EDITOR" "$PATCH_FILE"
	echo "$PATCH_FILE"
}

recipe_diff() {
	tmp_patch="/tmp/telodendria-$(date +%s).patch"
	cvs -q diff -uNp $PATCHSET > "$tmp_patch"
	if [ -z "$PAGER" ]; then
		PAGER="less -F"
	fi

	$PAGER "$tmp_patch"
	rm "$tmp_patch"
}

# Execute the user-specified recipes.
for recipe in $@; do
	recipe_$recipe
done

# If no recipe was provided, run a build.
if [ -z "$1" ]; then
	recipe_build
fi