#!/bin/bash

### 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: ""
  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

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

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

: ${CSG_TIMESTAMP_FILE:="$(realpath ./timestamps.txt)"}

: ${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'
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

if [ -n "$AWKSCRIPTS" ]
then
	overwriteLine "[Stage 1] Combining awk scripts"

	scr_path=()
	scr_recip=()
	while read -r reg; read -r scripts
	do
		cat $scripts > "$CSG_TMP_DIR/csg-${#scr_path[@]}.awk"
		scr_path+=("$CSG_TMP_DIR/csg-${#scr_path[@]}.awk")
		scr_recip+=("$(find $DESTINATION -type f -regex "$reg")")
	done < <($CSG_AWK '
	{
		# Remove first field
		$1=""; sub(FS, "")
		# Associate current file with regex
		if (awks[$0] == "") awks[$0] = FILENAME
		else                awks[$0] = awks[$0] " " FILENAME

		nextfile
	}
	END {
		for (reg in awks) {
			print reg
			print awks[reg]
		}
	}
	' $(find $AWKSCRIPTS -type f -name "[A-Z]*.awk"))

	overwriteLine "[Stage 1] Finding awk libraries"

	scr_libs=""
	while read -r file
	do
		scr_libs="$scr_libs -f $file "
	done << EOF
$(find $AWKSCRIPTS -type f -name "#*.awk")
EOF
fi

endTimer "[Stage 1] Done"

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

transform-file() {
	local file="$1"
	local outf="${file%md}html"
	local scripts="$2"

	# Gets special information from the file
	local title descr words
	{ read -r title; read -r descr; read -r words; } << EOF
$($CSG_AWK -v titlef="$TITLEF" '
	title == "" && /^# / {
		# Remove first field
		$1 = ""; sub(FS, "")
		# Output title
		sub(/.*/, titlef)
		title = $0
		next
	}
	stopdesc == 0 && /^[[:alpha:]]/ {
		startedGathering = 1
		# Take only anchor text
		gsub(/\]\([^)]*\)/, "")
		gsub(/\[/, "")
		# Append line
		if (desc == "") desc = $0
		else desc = desc " " $0
		# Trim line to 200 characters
		if (length(desc) > 200) {
			desc = substr(desc, 1, 197) "..."
			stopdesc = 1
		}
	}
	startedGathering && /^$/ { exit }
	{ words += NF }
	END { print title ORS desc ORS words }' "$file")
EOF

	local created edited
	{ 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

	# Generate
	$CSG_MD_TO_HTML "$file" > "$outf"

	# Run custom awk scripts
	$CSG_AWK \
		-v title="$title" \
		-v description="$descr" \
		-v words="$words" \
		-v created="$created" \
		-v edited="$edited" \
		$scr_libs $scripts -f "$CSG_TMP_DIR/csg-print.awk" \
		"$outf" > "$outf.tmp"
	mv "$outf.tmp" "$outf"

	# Create final file
	$CSG_AWK \
		-v title="$title" \
		-v description="$descr" '
	BEGIN { gsub(/&/, "\\\\&", title) }
	/\[DESCRIPTION\]/ {
		gsub(/\[DESCRIPTION\]/, description)
	}
	/\[TITLE\]/ {
		gsub(/\[TITLE\]/, title)
	}
	{print}' "$SOURCE/_header.html" > "$file"
	cat "$outf" "$SOURCE/_footer.html" >> "$file"
	mv "$file" "$outf"
}

echo '$0 != 0 {print}' > "$CSG_TMP_DIR/csg-print.awk"

while read -r file
do
	scripts=""
	for i in ${!scr_recip[@]}
	do
		[ "${scr_recip[$i]}" != "${scr_recip[$i]/${file%md}html/}" ] && scripts+=" -f ${scr_path[$i]} "
	done

	transform-file "$file" "$scripts" &
done < <(find "$DESTINATION" -type f -name "*.md")
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"
