#!/usr/bin/env sh

### Preprocessing 1
### Help message

csg_help() {
	echo 'Usage: csg [-h | -? | --help]
       csg <source_dir> <destination_dir> <title_format> <base_url> [<scripts_dir>]

Options:
  -h, -?, --help    Print this help message (also available when given no
                    arguments)

Arguments:
  <source_dir>         Folder which contains source files
  <destination_dir>    Folder in which the generated site will be put
  <title_format>       Format string for page title
                       Accepts any text or HTML, & is replaced with contents
                       title (first h1 header).
  <base_url>           Base URL of your website (scheme + authority), without
                       trailing "/", used for sitemap generation.
                       Example: https://google.com
  <scripts_dir>        Optional, folder which contains awk script for editing of
                       generated webpages.

Variables (you can predefine):
  CSG_TIMESTAMP_FILE    File path for git-based timestamps (when git exists)
                        Default: "./timestamps.txt"
                        Disable: "none"
  CSG_TMP_DIR           Directory to put temporary files
                        Default: "/tmp"
  CSG_AWK               awk executable and flags
                        Default: searches for gawk
  CSG_MD_TO_HTML        Program to convert Markdown to HTML and it'\''s flags
                        Default: searches for lowdown

Dependencies (required):
  bash, printf, find, cpio


csg - complicated (static) site generator
Created by Syndamia, https://gitlab.com/Syndamia/csg'
  exit $1
}

[ -z "$1" -o "$1" = '-h' -o "$1" = '-?' -o "$1" = '--help' ] && csg_help 0

### Preprocessing 2
### Script parameters

SOURCE="$1"
[ ! -d "$SOURCE" ] && echo "[Error] $SOURCE (<source_dir>) isn't a directory or doesn't exist!" \
	&& csg_help 1
[ ! -r "$SOURCE" ] && echo "[Error] No read access to $SOURCE (<source_dir>)!" \
	&& csg_help 2

DESTINATION="$(realpath "$2")"
[ ! -d "${DESTINATION%/*}" ] && echo "[Error] Parent directory of $2 (<destination_dir>) isn't a directory or doesn't exist!" \
	&& csg_help 1
[ ! -w "${DESTINATION%/*}" ] && echo "[Error] No write access to parent directory of $2 (<destination_dir>)!" \
	&& csg_help 2

TITLEF="$3"
URL="$4"
[ -z "$URL" ] && echo "[Error] <base_url> cannot be an empty string!" \
	&& csg_help 3

LIB_SCRIPT="$5"
[ -n "$LIB_SCRIPT" -a ! -d "$LIB_SCRIPT" ] && echo "[Error] $LIB_SCRIPT (<scripts_dir>) isn't a directory or doesn't exist!" \
	&& csg_help 1

PREV_ALL="$6"
[ -n "$PREV_ALL" -a ! -d "$PREV_ALL" ] && echo "[Error] $PREV_ALL (<scripts_dir>) isn't a directory or doesn't exist!" \
	&& csg_help 1

PREV_MD="$7"
[ -n "$PREV_MD" -a ! -d "$PREV_MD" ] && echo "[Error] $PREV_MD (<scripts_dir>) isn't a directory or doesn't exist!" \
	&& csg_help 1

ON_MD="$8"
[ -n "$ON_MD" -a ! -d "$ON_MD" ] && echo "[Error] $ON_MD (<scripts_dir>) isn't a directory or doesn't exist!" \
	&& csg_help 1

ON_HTML="$9"
[ -n "$ON_HTML" -a ! -d "$ON_HTML" ] && echo "[Error] $ON_HTML (<scripts_dir>) isn't a directory or doesn't exist!" \
	&& csg_help 1

ON_OTHER="${10}"
[ -n "$ON_OTHER" -a ! -d "$ON_OTHER" ] && echo "[Error] $ON_OTHER (<scripts_dir>) isn't a directory or doesn't exist!" \
	&& csg_help 1

### Preprocessing 3
### "Global" variables

: ${CSG_TIMESTAMP_FILE:="$(realpath ./timestamps.txt)"}
[ "$CSG_TIMESTAMP_FILE" = 'none' ] && CSG_TIMESTAMP_FILE=""

: ${CSG_TMP_DIR:='/tmp'}
[ ! -d "$CSG_TMP_DIR" ] && echo "[Error] $CSG_TMP_DIR (\$CSG_TMP_DIR) doesn't exist!" \
	&& csg_help 1
[ ! -r "$CSG_TMP_DIR" -o ! -w "$CSG_TMP_DIR" ] && echo "[Error] No read or write permissions for $CSG_TMP_DIR (\$CSG_TMP_DIR)!" \
	&& csg_help 2

isCommand() {
	command -v "$1" >/dev/null 2>&1
}

if [ -n "$CSG_AWK" ]
then :
elif isCommand 'gawk'
then
	CSG_AWK='gawk
		--posix'
else
	echo '[Error] Couldn'\''t find an awk implementation!'
	csg_help 4
fi

if [ -n "$CSG_MD_TO_HTML" ]
then :
elif isCommand 'lowdown'
then
	CSG_MD_TO_HTML="lowdown
		--html-no-escapehtml
		--html-no-skiphtml
		--parse-no-metadata
		--parse-no-autolink"
else
	echo '[Error] Couldn'\''t find a Markdown to HTML convertion program!'
	csg_help 4
fi

### Preprocessing 4
### Timer/status function definitions

overwriteLine() {
	printf "\033[1A\033[K$1\n"
}

_dur=0
startTimer() {
	_dur="$(date +%s%N)"
	printf "$1\n"
}
endTimer() {
	_dur="$(echo "scale=4 ; ($(date +%s%N) - $_dur) / 1000000000" | bc -l)"
	printf "\033[1A\033[K$1    %.3fs\n" $_dur
}

### Stage 0
### Create timestamps.txt file, if necessary

if [ -n "$CSG_TIMESTAMP_FILE" ] && isCommand 'git' && [ -d '.git' ]
then
	timestamps_writeDates() {
		while read -r file
		do
			git log --follow --pretty=format:%ad --date="format:%d %B %Y" -- "$file" | $CSG_AWK -v dest="$DESTINATION" -v file="$file" '
				BEGIN { file = dest substr(file, 2, length(file) - 3) "html" }
				NR == 1 { later = $0 }
				END { print file "\t" $0 "\t" later }' >> "$CSG_TIMESTAMP_FILE"
		done << EOF
$(find . -type f -name '*.md')
EOF
	}

	headc="$(git rev-parse HEAD)"
	if [ ! -f "$CSG_TIMESTAMP_FILE" ] || [ "$headc" != "$(head -1 "$CSG_TIMESTAMP_FILE")" ]
	then
	startTimer "[Stage 0] Creating timestamps file"
		echo "$headc" > "$CSG_TIMESTAMP_FILE"
		(cd "$SOURCE" && timestamps_writeDates)
	endTimer "[Stage 0] Done"
	fi
fi

### Stage 1
### Copy everything from SOURCE to DESTINATION
startTimer "[Stage 1] Moving files to destination"

(cd "$SOURCE" && \
	find . -type f -not -name "\.*" -not -name '_header.html' -not -name '_footer.html' | \
	cpio -pdu "$DESTINATION" >/dev/null 2>&1)

while read -r file
do
	mv "$file" "${file%md}html"
done << EOF
$(find "$DESTINATION" -type f -name "*index.md" -links +1)
EOF

overwriteLine "[Stage 1] Combining awk scripts"

combine_scripts() {
	rm "$CSG_TMP_DIR/$2" >/dev/null 2>&1

	[ -z "$1" ] && echo "" && return

	scripts=$(find "$1" -type f -name "*.awk" | sort)
	[ -z "$scripts" ] && echo "" && return

	cat $scripts > "$CSG_TMP_DIR/$2"
	echo "-f	$CSG_TMP_DIR/$2"
}

lib_script="$(combine_scripts "$LIB_SCRIPT" "csg-lib_script.awk")"
on_md="$(combine_scripts "$ON_MD" "csg-on_md.awk")"
on_html="$(combine_scripts "$ON_HTML" "csg-on_html.awk")"
on_other="$(combine_scripts "$ON_OTHER" "csg-on_other.awk")"
prev_all="$(combine_scripts "$PREV_ALL" "csg-prev_all.awk")"
prev_md="$(combine_scripts "$PREV_MD" "csg-prev_md.awk")"

echo
endTimer "[Stage 1] Done"

### Stage 2
### Transforming Markdown
startTimer "[Stage 2] Transforming Markdown to HTML"

# prev_all
cat << EOF > "$CSG_TMP_DIR/csg-export.awk"
function export(name, value) {
	if (name == "" || value == "") {
		return
	}
	# if is string
	if (value + 0 != value) {
		gsub(/"/, "\\\\\\"", value)
	}

	printf "	-v	" name "=" ((value + 0 == value) ? value : ("\\"" value "\\"")) "	"
}
EOF
exports=$($CSG_AWK \
          	-f "$CSG_TMP_DIR/csg-export.awk" \
          	$lib_script $prev_all \
          	$(find "$DESTINATION" -type f -name "*.md"))

echo '$0 != 0 {print}' > "$CSG_TMP_DIR/csg-print.awk"
echo 'BEGIN { FILENAME = truefilename } NR == 1 { FILENAME = truefilename }' > "$CSG_TMP_DIR/csg-filename.awk"

transform_file() {
	local file="$1"
	local outf="${file%md}html"

	local created edited
	if [ -f "$CSG_TIMESTAMP_FILE" ]
	then
		{ read -r created; read -r edited; } << EOF
$($CSG_AWK -v file="$outf" '
	BEGIN { FS = "\t" }
	$1 == file {
		print $2 ORS $3
		exit
	}' "$CSG_TIMESTAMP_FILE")
EOF
	fi

	# prev_md
	local_exports=$($CSG_AWK\
					-f	"$CSG_TMP_DIR/csg-export.awk"\
					$lib_script	$prev_md\
					"$file")

	# on_md and on_html
	$CSG_AWK \
		-v created="$created" \
		-v edited="$edited" \
		$exports $local_exports \
		$lib_script $on_md -f "$CSG_TMP_DIR/csg-print.awk" \
		"$file" \
	| $CSG_MD_TO_HTML \
	| cat "$SOURCE/_header.html" - "$SOURCE/_footer.html" \
	| $CSG_AWK \
		-v created="$created" \
		-v edited="$edited" \
		$exports $local_exports \
		-v truefilename="$outf" -f "$CSG_TMP_DIR/csg-filename.awk" \
		$lib_script $on_html -f "$CSG_TMP_DIR/csg-print.awk" \
	> "$outf"

	# For hardlinks
	cat "$outf" > "$file"
	mv "$file" "$outf"
}

tempifs=$IFS
IFS='
	'

while read -r file
do
	transform_file "$file" &
done << EOF
$(find "$DESTINATION" -type f -name "*.md")
EOF

IFS=$tempifs

if [ -n "$on_other" ]
then
	transform_other() {
		local file="$1"

		$CSG_AWK \
			$exports $local_exports \
			$lib_script $on_other -f "$CSG_TMP_DIR/csg-print.awk" \
			"$file" > temp
		mv temp "$file"
	}

	while read -r file
	do
		if [ -f "$file" ]
		then
			transform_other "$file" &
		fi
	done << EOF
$(find "$DESTINATION" -type f -not -name "*.md")
EOF
fi

wait

overwriteLine "[Stage 2] Generating sitemap"
  # This cannot really be done while transforming files, since you could have static .html pages

$CSG_AWK -v url="$URL" -v dest="$DESTINATION" -v date="$(date +%Y-%m-%d)" '
	BEGIN {
		print "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" ORS \
		      "<urlset" ORS \
		      "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" ORS \
		      "xsi:schemaLocation=\"http://www.sitemaps.org/schemas/sitemap/0.9" ORS \
		      "http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd\"" ORS \
		      "xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"
		lendest = length(dest)
	}
	{
		$0 = substr($0, index($0, dest) + lendest)
		print "<url><loc>" url $0 "</loc><lastmod>" date "</lastmod><priority>1.0</priority></url>"
	}
	END { print "</urlset>" }
'<< EOF > "$DESTINATION/sitemap.xml"
$(find "$DESTINATION" -type f -name "*.html")
EOF

endTimer "[Stage 2] Done"
