#!/bin/sh -e
#
# https://rgz.ee/bin/ssg6
# Copyright 2018-2019 Roman Zolotarev <hi@romanzolotarev.com>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#

# get all .md files in the src directory and look for the "#tags", then make a 
# new file called "tags.md" as well as create an ".md" file for each tag named 
# after the tag. The tags.md file will have a section for each tag with a link 
# to its corresponding .md file, and the .md files named after the tags will 
# have a section for each tag with a link to the tags.md file.
make_tag_files() {
    # get all .md files in the src directory
    files=$(find src -name "*.md")
    tags=""
    # look for the "#tags" in the files
    for file in $files; do
        found=$(tr "\n" " " < "$file" | grep -oE "[[:space:]]#[^[:space:]&^[:punct:]]+" || :;)
        tags="$tags $found"
    done
    # remove any duplicate tags
    tags=$(echo "$tags" | tr " " "\n" | sort -u | tr "\n" " ")
    echo "$tags"
    # for each tag in tags add a section to the tags.md file with a link t
    for tag in $tags; do
        # remove the "#" from the tag
        tagName=$(echo "$tag" | sed 's/^#//')
        echo "# $tag" > src/tag-"$tagName".md
        echo "" >> src/tag-"$tagName".md
        echo "<ul>" >> src/tag-"$tagName".md
    done

    # for each tagName file add a []() link to the files that have that tag
    for tag in $tags; do
        tagName=$(echo "$tag" | sed 's/^#//')
        for file in $files; do
            search=$(grep -oE "$tag" "$file" || :;)
            # get just the file name
            fileName=$(basename "$file")
            # remove the ".md" from the file name
            fileName=$(echo "$fileName" | sed 's/\.md//')
            if [ "$search" = "$tag" ] ; then
                # get first line of file
                title=$(head -n 1 -q "$file")
                # remove the "# " from the title
                title=${title#"# "}
                # remove any "\" from the title
                title=$(echo "$title" | sed 's/\\//g')
                echo "\"$title\" gets added to \"$tag\""
                echo "<li><a href=\"$fileName.html\">$title</a></li>" >> src/tag-"$tagName".md
            fi
        done
        echo "</ul>" >> src/tag-"$tagName".md
    done
}

convert_tags_to_links() {
    # for each file in files, replace the "#tagname"s with [#tagname](tag-tagname.html)
    for file in $filesMd; do
        # if file name contains "index|contact|tag" then skip it
        if [ "$(echo "$file" | grep -oE "index|contact|tag")" != "" ] ; then
            continue
        fi
        # get just the file name
        fileName=$(basename "$file")
        # remove the ".md" from the file name
        fileName=$(echo "$fileName" | sed 's/\.md//')
        # get all the tags in the file
        tags=$(tr "\n" " " < "$file" | grep -oE "[[:space:]]#[^[:space:]&^[:punct:]]+" || :;)
        # for each tag in tags, replace the "#tagname" with [#tagname](tag-tagname.html)
        for tag in $tags; do
            # remove the "#" from the tag
            tagName=$(echo "$tag" | sed 's/^#//')
            # replace the tag with [#tagname](tag-tagname.html)
                sed -i "s|$tag|<a class=\"tag\" href=\"tag-$tagName.html\">$tag</a>|g" "public/$fileName.html"
        done
    done
}

render_tags_on_index() {
    tags=""
    # look for the "#tags" in the files
    for file in $filesMd; do
        found=$(tr "\n" " " < "$file" | grep -oE "[[:space:]]#[^[:space:]&^[:punct:]]+" || :;)
        tags="$tags $found"
    done
    # remove any duplicate tags
    tags=$(echo "$tags" | tr " " "\n" | sort -u | tr "\n" " ")
    for tag in $tags; do
        # remove the "#" from the tag
        tagName=$(echo "$tag" | sed 's/^#//')
        tagList="$tagList<a class=\"tag\" href=\"tag-$tagName.html\">$tag</a> "
    done
    sed -i "s:</article>:<h4>Browse by tag</h4>\n<p>$tagList</p>\n</article>:" public/index.html
}

fix_emojis() {
    # for each file in files, replace the "↩" with "\&#x21A9;\&#xFE0E;"
    for file in $files; do
        sed -i "s|\&#8617;|\&#x21A9;\&#xFE0E;|g" "$file"
    done
}

fix_md_links() {
    # for each file in files, replace ".md">" with ".html">"
    for file in $files; do
        sed -i "s|\.md\">|\.html\">|g" "$file"
    done
}

# Not done, still need to figure out how to get this to ignore the logo, and how to correctly regex
link_images() {
    # for each file in files, look for html img tags and wrap them in a link to the image
    for file in $filesPhoto; do
        # get all the html img tags in the file
        # one example of an img tag/match is: <img src="files/dotfile-aliases.png" alt="dotfile-aliases" />
        images=$(grep -oE "<img src=\"[^[:space:]]+" "$file" || :;)
        # replace "<img " with "<img_"
        images=$(echo "$images" | sed 's/<img /<img_/g')
        # for each image in images, replace the src with a link to the image
        for image in $images; do
            # remove the '<img_src="' from the image
            imageName=$(echo "$image" | sed 's/<img_src="files\///')
            # remove the '"' from the image
            imageName=$(echo "$imageName" | sed 's/"//g')
            # wrap the "<img src" tags with a link to the full size image
            sed -i "s|<img src=\"files/$imageName|<a href=\"full-size-files/$imageName\"><img  src=\"files/$imageName|g" "$file"
        done
        sed -i "s|\" />|\" /></a>|g" "$file"
    done
}

process_html() {
    # get all .html files in the public directory that are not tag-*.html
    files=$(find public -name "*.html" | grep -v "tag-")
    # get all .md files in the src directory
    filesMd=$(find src -name "*.md")
    # only files with "photo" in the file name and ignore the tag-*.html files
    filesPhoto=$(find public -name "*photo*" | grep -v "tag-")
    convert_tags_to_links
    render_tags_on_index
    fix_md_links
    fix_emojis
    link_images
}


main() {
	test -n "$1" || usage
	test -n "$2" || usage
	test -n "$3" || usage
	test -n "$4" || usage
	test -d "$1" || no_dir "$1"
	test -d "$2" || no_dir "$2"

	src=$(readlink_f "$1")
	dst=$(readlink_f "$2")

	IGNORE=$(
		if ! test -f "$src/.ssgignore"; then
			printf ' ! -path "*/.*"'
			return
		fi
		while read -r x; do
			test -n "$x" || continue
			printf ' ! -path "*/%s*"' "$x"
		done <"$src/.ssgignore"
	)

	# files

	title="$3"

	h_file="$src/_header.html"
	f_file="$src/_footer.html"
	test -f "$f_file" && FOOTER=$(cat "$f_file") && export FOOTER
	test -f "$h_file" && HEADER=$(cat "$h_file") && export HEADER

	list_dirs "$src" |
		(cd "$src" && cpio -pdu "$dst")

	fs=$(
		if test -f "$dst/.files"; then
			list_affected_files "$src" "$dst/.files"
		else
			list_files "$1"
		fi
	)

	if test -n "$fs"; then
		echo "$fs" | tee "$dst/.files"

		if echo "$fs" | grep -q '\.md$'; then
			if test -x "$(command -v lowdown 2>/dev/null)"; then
				echo "$fs" | grep '\.md$' |
					render_md_files_lowdown "$src" "$dst" "$title"
			else
				if test -x "$(command -v Markdown.pl 2>/dev/null)"; then
					echo "$fs" | grep '\.md$' |
						render_md_files_Markdown_pl "$src" "$dst" "$title"
				else
					echo "couldn't find lowdown nor Markdown.pl"
					exit 3
				fi
			fi
		fi

		echo "$fs" | grep '\.html$' |
			render_html_files "$src" "$dst" "$title"

		echo "$fs" | grep -Ev '\.md$|\.html$' |
			(cd "$src" && cpio -pu "$dst")
	fi

	printf '[ssg] ' >&2
	print_status 'file, ' 'files, ' "$fs" >&2

	# sitemap

	base_url="$4"
	date=$(date +%Y-%m-%d)
	urls=$(list_pages "$src")

	test -n "$urls" &&
		render_sitemap "$urls" "$base_url" "$date" >"$dst/sitemap.xml"
        render_article_list "$urls" "$base_url" "$dst" "$src"

	print_status 'url' 'urls' "$urls" >&2
	echo >&2
}

readlink_f() {
	file="$1"
	cd "$(dirname "$file")"
	file=$(basename "$file")
	while test -L "$file"; do
		file=$(readlink "$file")
		cd "$(dirname "$file")"
		file=$(basename "$file")
	done
	dir=$(pwd -P)
	echo "$dir/$file"
}

print_status() {
	test -z "$3" && printf 'no %s' "$2" && return

	echo "$3" | awk -v singular="$1" -v plural="$2" '
	END {
		if (NR==1) printf NR " " singular
		if (NR>1) printf NR " " plural
	}'
}

usage() {
	echo "usage: ${0##*/} src dst title base_url" >&2
	exit 1
}

no_dir() {
	echo "${0##*/}: $1: No such directory" >&2
	exit 2
}

list_dirs() {
	cd "$1" && eval "find . -type d ! -name '.' ! -path '*/_*' $IGNORE"
}

list_files() {
	cd "$1" && eval "find . -type f ! -name '.' ! -path '*/_*' $IGNORE"
}

list_dependant_files() {
	e="\\( -name '*.html' -o -name '*.md' -o -name '*.css' -o -name '*.js' \\)"
	cd "$1" && eval "find . -type f ! -name '.' ! -path '*/_*' $IGNORE $e"
}

list_newer_files() {
	cd "$1" && eval "find . -type f ! -name '.' $IGNORE -newer $2"
}

has_partials() {
	grep -qE '^./_.*\.html$|^./_.*\.js$|^./_.*\.css$'
}

list_affected_files() {
	fs=$(list_newer_files "$1" "$2")

	if echo "$fs" | has_partials; then
		list_dependant_files "$1"
	else
		echo "$fs"
	fi
}

render_html_files() {
	while read -r f; do
		render_html_file "$3" <"$1/$f" >"$2/$f"
	done
}

render_md_files_lowdown() {
	while read -r f; do
		lowdown \
			--html-no-escapehtml \
			--html-no-skiphtml \
			--parse-no-autolink \
            --parse-no-metadata < "$1/$f" |
			render_html_file "$3" > "$2/${f%\.md}.html"
    done
}

render_md_files_Markdown_pl() {
	while read -r f; do
		Markdown.pl <"$1/$f" |
			render_html_file "$3" \
				>"$2/${f%\.md}.html"
	done
}

render_html_file() {
	# h/t Devin Teske
	awk -v title="$1" '
	{ body = body "\n" $0 }
	END {
		body = substr(body, 2)
		if (body ~ /<\/?[Hh][Tt][Mm][Ll]/) {
			print body
			exit
		}
		if (match(body, /<[[:space:]]*[Hh]1(>|[[:space:]][^>]*>)/)) {
			t = substr(body, RSTART + RLENGTH)
			sub("<[[:space:]]*/[[:space:]]*[Hh]1.*", "", t)
			gsub(/^[[:space:]]*|[[:space:]]$/, "", t)
			if (t) title = t " &mdash; " title
		}
		n = split(ENVIRON["HEADER"], header, /\n/)
		for (i = 1; i <= n; i++) {
			if (match(tolower(header[i]), "<title></title>")) {
				head = substr(header[i], 1, RSTART - 1)
				tail = substr(header[i], RSTART + RLENGTH)
				print head "<title>" title "</title>" tail
			} else print header[i]
		}
		print body
		print ENVIRON["FOOTER"]
	}'
}

list_pages() {
	e="\\( -name '*.html' -o -name '*.md' \\)"
	cd "$1" && eval "find . -type f ! -path '*/.*' ! -path '*/_*' $IGNORE $e" |
		sed 's#^./##;s#.md$#.html#;s#/index.html$#/#'
}

render_article_list() {
	urls="$1"

    echo "$urls"

	base_url="$2"
	items=""
    for i in $1; do
        if ! echo "$i" | grep -Eq "index|contact|tag"; then
            page_date=$(awk 'NR==3' "$4/${i%\.html}.md")
            page_date_clean=$(echo "$page_date" | sed 's/<[^>]*>//g')
            # get the year from the date
            year=$(echo "$page_date_clean" | cut -d ',' -f 2)
            # get the month from page_date_clean, make sure to remove everything after
            # the space
            month=$(echo "$page_date_clean" | cut -d ' ' -f 1)
            # convert month string "january" to number
            month_num=$(date -d "$month 01, $year" +%m)
            # get the day from date
            # if there is a sequence like " 4th," remove everything that is not a
            # number
            day=$(echo "$page_date_clean" | cut -d ' ' -f 2 | sed 's/[^0-9]//g')
            day_num=$(date -d "$month $day, $year" +%d)
            # combine year, month, and day in string format
            sortable_title="$year-$month_num-$day_num:$i"
            echo "$sortable_title"
            title_list="$title_list""$sortable_title" #perhaps could be an issue
        fi

    done

    # sort the list of titles
    title_list=$(echo "$title_list" | tr " " "\n" | sort -r | tr "\n" " ")
    echo "$title_list"
    
	for i in $title_list; do
		if ! echo "$i" | grep -Eq "index|contact|tag"; then
            url=$(echo "$i" | cut -d ':' -f 2)
            echo "$url"
	    page_title=$(awk 'NR==1 {print substr($0, 3)}' "$4/${url%\.html}.md")
            page_date=$(awk 'NR==3' "$4/${url%\.html}.md")
            echo "$page_title"
            item="<li><a href="\"${url}\"">${page_title} \&#x21A9;\&#xFE0E;</a>${page_date}</li>"
            items="$items""$item"
		fi
	done

	sed -i "s|</article>|<ul class=\"articles\">${items}</ul></article>|g" "$3/index.html"
}


render_sitemap() {
	urls="$1"
	base_url="$2"
	date="$3"

	echo '<?xml version="1.0" encoding="UTF-8"?>'
	echo '<urlset'
	echo 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'
	echo 'xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9'
	echo 'http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"'
	echo 'xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">'
	echo "$urls" |
		sed -E 's#^(.*)$#<url><loc>'"$base_url"'/\1</loc><lastmod>'"$date"'</lastmod><priority>1.0</priority></url>#'
	echo '</urlset>'
}

make_tag_files

main "$@"

process_html
