#!/bin/sh

### Code:

usage() {
	cat <<\EOF
ht: Build a website
Usage:	ht -h
	ht [OPTIONS...] FILE...

FLAGS:
 -h	Show this help and exit.
 -B	Treat all targets as "new" (like in `make').

OPTIONS:
 -u BASEURL	The base URL of the built site.  Default: https://example.com
 -o OUTD	Output built files to OUTD.  Default: ./out/
 -w WORKD	Use WORKD as the working directory.  Default: /tmp/ht/
 -p PROC	Use PROC to process each input file.  Default: ./ht.awk
 -P TEMPLATE	Use TEMPLATE as the page template.  Default: ./page.tmpl.htm
 -i ITEMFMT	Format individual items with ITEMFMT in the site index.
 -I TEMPLATE	Use TEMPLATE as the index template.  Default: ./index.tmpl.htm
 -f ITEMFMT	Format individual items with ITEMFMT in the site feed.
 -F TEMPLATE	Use TEMPLATE as the feed template.  Default: ./feed.tmpl.xml
 -s DIRECTORY	The DIRECTORY holding static files (images, css, etc.).
			Default: ./static/
 -S DIRECTORY	The DIRECTORY to copy static files to.  Default: ./out

PARAMETERS:
 FILE...	The list of files to include in the website.
EOF
	exit "${1:-0}"
}

main() {
	configure "$@"
	shift $((OPTIND - 1))
	test $# -eq 0 && usage 1

	prepare
	static_copy

	for input; do
		case "$input" in
		_* | */_*) continue ;;
		*) page_write "$PAGE_TEMPLATE" "$input" ;;
		esac
	done

	if okp && [ -n "$INDEXEACH" ]; then
		index_write "$INDEX_TEMPLATE" "$INDEXEACH" "$OUTD/$INDEXNAME"
	fi
	if okp && [ -n "$FEEDEACH" ]; then
		index_write "$FEED_TEMPLATE" "$FEEDEACH" "$OUTD/$FEEDNAME"
	fi

	printf 'Done.  ' >&2
	if okp; then
		eprint "No errors reported."
	else
		eprint "There were errors."
	fi
}

htawk_resolve() {
	if ! command -v ht.awk 2>/dev/null; then
		print ./ht.awk
	fi
}

configure() {
	OUTD="${HT_OUT_DIR:-./out}"
	WORKD="${HT_WORKD:-/tmp/ht}"
	PROC="${HT_PROC:-$(htawk_resolve)}"
	BASEURL="${HT_BASEURL:-https://example.com}"
	PAGE_TEMPLATE="${HT_PAGE_TEMPLATE:-./page.tmpl.htm}"
	INDEX_TEMPLATE="${HT_INDEX_TEMPLATE:-./index.tmpl.htm}"
	FEED_TEMPLATE="${HT_FEED_TEMPLATE:-./feed.tmpl.xml}"
	ALLNEW=false
	STATICD="${HT_STATIC_DIR:-./static}"
	STATICOUT="${HT_STATIC_OUTPUT_DIR:-${OUTD}}"

	INDEXNAME="${HT_INDEX_NAME:-index.html}"
	if [ -n "$HT_INDEX_ITEM_FORMAT" ]; then
		INDEXEACH="$HT_INDEX_ITEM_FORMAT"
	else
		# shellcheck disable=2016
		INDEXEACH='<li><a href=\"/${file}/\">${title}</a></li>'
	fi

	FEEDNAME="${HT_FEED_NAME:-feed.xml}"
	if [ -n "$HT_FEED_ITEM_FORMAT" ]; then
		FEEDEACH="$HT_FEED_ITEM_FORMAT"
	else
		# shellcheck disable=2016
		FEEDEACH='<entry>
		<id>$BASEURL/$file</id>
		<link rel=\"alternate\" href=\"$BASEURL/$file/\" />
		<title>${title}</title>
		<updated>$(date -u -d "@${time}" -R)</updated>
		<author>$AUTHOR</author>
		<content type=\"text/html\">
		<![CDATA[$(cat "$WORKD/${file}.body")]]>
		</content>
		</entry>'
	fi

	# shellcheck disable=2034 # BASEURL is used in templates
	while getopts ho:w:p:P:i:I:f:F:u:Bs:S: opt; do
		case "$opt" in
		h) usage ;;
		o) OUTD="$OPTARG" ;;
		w) WORKD="$OPTARG" ;;
		p) PROC="$OPTARG" ;;
		P) PAGE_TEMPLATE="$OPTARG" ;;
		i) INDEXEACH="$OPTARG" ;;
		I) INDEX_TEMPLATE="$OPTARG" ;;
		f) FEEDEACH="$OPTARG" ;;
		F) FEED_TEMPLATE="$OPTARG" ;;
		u) BASEURL="$OPTARG" ;;
		B) ALLNEW=true ;;
		s) STATICD="$OPTARG" ;;
		S) STATICOUT="$OPTARG" ;;
		*) usage 1 ;;
		esac
	done

	STATICOUT="${HT_STATIC_OUTPUT_DIR:-${OUTD}}"
	INDEX="$WORKD/index.txt"
}

prepare() {
	test -n "$WORKD" && rm -rf "$WORKD"
	mkdir -p "$OUTD" "$WORKD"
	if ! test -x "$PROC"; then
		eprint "Can't find processor: $PROC"
		exit 2
	fi
	touch "$WORKD/ok"
}

### Utilities

print() {
	printf '%s\n' "$*"
}

eprint() {
	print "$@" >&2
}

okp() {
	test -f "$WORKD/ok"
}

notok() {
	rm "$WORKD/ok" 2>/dev/null
}

olderp() { # olderp REF OTHER
	# Is REF older than OTHER ?  Necessary b/c test -ot is not POSIX.
	a="$1"
	b="$2"

	if a_age="$(stat -c%Y "$a" 2>/dev/null)"; then
		a_exist=true
	else
		a_exist=false
	fi
	if b_age="$(stat -c%Y "$b" 2>/dev/null)"; then
		b_exist=true
	else
		b_exist=false
	fi

	if $a_exist && ! $b_exist; then
		# B doesn't exist -- so B is newer
		# eprint "$a is older than $b (doesn't exist)"
		return 0
	elif ! $a_exist && $b_exist; then
		# A doesn't exist -- so A is newer
		# eprint "$a (doesn't exist) is newer than $b"
		return 1
	else
		# A's age > B's age ?
		# eprint "$a ($a_age) <= $b ($b_age)?"
		test "$a_age" -le "$b_age"
	fi
}

uptodate() { # uptodate FILE DEPENDENCIES... # && return
	# Check if FILE is up-to-date with DEPENDENCIES.
	$ALLNEW && return 1
	uptodate=1
	file="$1"
	shift
	for dep in "$@"; do

		olderp "$dep" "$file" && uptodate=0
	done
	return $uptodate
}

### File conversion

template_expand() { # template_expand TEMPLATES...
	if test "$noproc"; then
		cat -
	else
		end="tmpl_$(date +%s)_${count:=0}"
		eval "$(
			print "cat <<$end"
			cat "$@"
			print
			print "$end"
		)" && count=$((count + 1))
	fi
}

meta_save() { # meta_save META_FILE < INPUT
	sed -n "s/^;;@//p" 2>/dev/null | tee "$1"
}

meta_clear() { # meta_clear META_FILE
	while read -r line; do
		case "$line" in
		*'()'*) unset -f "${line%()*}" ;;
		*=*) unset -v "${line%=*}" ;;
		*) ;;
		esac
	done <"$1" 2>/dev/null
}

page_write() { # html_write TEMPLATE INPUT
	template="$1"
	file="$2"
	noproc= # File can set `noproc' in their metadata to not be processed.

	if ! test -f "$file"; then
		eprint "ERROR: File doesn't exist: $file"
		notok
		return 1
	fi

	fn="${file##*/}"
	fn="${fn%%.*}"
	out="${OUTD}/${fn}/index.html"
	mkdir -p "${out%/*}"
	meta="$WORKD/${fn}.meta"
	body="$WORKD/${fn}.body"

	eval "$(meta_save "$meta" <"$file")"
	stat -c "%Y	${fn}	$meta" "$file" >>"$INDEX"

	# uptodate "$out" "$template" "$file" && return

	eprint "Page:  $file -> $out"
	test "$noproc" || "$PROC" "$file" >"$body"
	if ! [ -f "$body" ]; then
		eprint "ERROR: Conversion: $file"
		notok
		return 2
	fi
	test "$noproc" || body="$(template_expand "${body}")"
	if ! template_expand "$template" >"$out"; then
		eprint "ERROR: Expansion: $file"
		notok
		return 3
	fi
	meta_clear "$meta"
}

index_write() { # index_write TEMPLATE EACH OUTFILE
	template="$1"
	each="$2"
	out="$3"

	if ! test -f "$INDEX"; then
		: >"$INDEX"
	fi
	# uptodate "$out" "$template" "$INDEX" && return

	eprint "Index: $out"
	# shellcheck disable=2034 # file and time can be used in `each'
	sort -k1,1 -nr "$INDEX" |
		while IFS='	' read -r time file meta; do
			# shellcheck disable=1090
			. "$meta"
			# shellcheck disable=2154 # noindex is a file var
			[ "$noindex" ] && continue
			if ! item="$(eval print "\"$each\"")"; then
				eprint "ERROR: couldn't add '$file' to $out"
				notok
			fi
			print "$item"
			meta_clear "$meta"
		done |
		template_expand "$template" >"$out"
}

### Static files

static_copy() { # static_copy
	if ! test -d "$STATICD"; then
		return 1
	fi
	mkdir -p "$STATICOUT"
	for f in "$STATICD"/*; do
		if [ -d "$f" ]; then
			cp -r "$f" "$STATICOUT/"
		else
			cp "$f" "$STATICOUT/"
		fi
	done
}

### Do the thing

test "$DEBUG" && set -x
test "$SOURCE" || main "$@"