#! /bin/bash

set -e

# shell opt/var {{{

# Executable command for markdown
declare -r MARKDOWN_BIN="markdown -f fencedcode,autolink,alphalist,autolink,footnote,definitionlist"

# Directory for input/output
declare -r INPUT_DIR=${INPUT_DIR:?ENV \"INPUT_DIR\" is not set}
declare -r OUTPUT_DIR=${OUTPUT_DIR:?ENV \"OUTPUT_DIR\" is not set}
declare -r ASSET_DIR=${ASSET_DIR:-`pwd`/assets}
declare -r TEMPLATE_DIR=${TEMPLATE_DIR:-`pwd`/templates}
declare -r RSSFILE=feed.rss
declare -r HOST=${HOST:?}

# }}}
# functions {{{

# add indent for each line except <pre> {{{

indent() {
  indent="$(printf "%${1}s")"
  sed "/^$/ !s/^/${indent}/; /<pre>/!b; :pre; N; /<\/pre>/!b pre"
}

# }}}
# print comment block {{{
comment_block() {
  [ "$comment" = true ] || return 0
  <<-COMMENT cat
	<div style="border-radius: 6px; background: lightyellow">
	  <a style="display: inline-block; margin: 0.5em 0.5em 0 0; float: right" href="mailto:comment@topo.tw?subject=Comment on page: ${path}">[Comment on this page]</a>
	  <object type="text/html" data="/${path/%html/comment.html}" onload="observeResize(this)" style="width: 100%;"></object>
	  <script>
	    function observeResize(commentBlock) {
	      const doc = commentBlock.contentDocument.documentElement
	      new ResizeObserver(() => {
	        commentBlock.style.height = doc.clientHeight + 'px';
	      }).observe(doc);
	    }
	  </script>
	</div>
	COMMENT
}

# }}}
# generate html by heredoc from .md file and templates {{{
html() {
  <<-END_OF_HTML sed '1d;$d'

	<!DOCTYPE html>
	<html lang="en">
	$(echo "$HEAD" | { [ -z "${title}" ] && cat || sed "/^ *<title>/c \ \ <title>${title}</title>"; } )
	<body>
	${HEADER}
	  <hr><br>
	  <main>
	$(sed -E 's/\[([^]]+)\]\(\)/[\1](\1)/g' | ${MARKDOWN_BIN} | indent 4)
	  </main>
	  $(comment_block | indent 2)
	  <br><hr>
	${FOOTER}
	</body>
	</html>

	END_OF_HTML
}

# }}}
# list of latest posts in markdown format {{{

latest_posts() {
  (IFS=$'\n'; echo "${index_list[*]}") | sort -r | head -20 | while read date path title; do
  echo "- <time title=\"$(date --rfc-email -d "$date")\" datetime=\"$date\">$date</time> [$title](/$path)"
  done
}

# }}}
# process frontmatter {{{

get_frontmatter() {
  sed -n '1 {/<!--/ !q; n}; /-->/q; s/"//g; s/://p'
}

check_frontmatter() {
  unset title public index type date draft

  # define local variables for frontmatter
  while read key value; do
    [ -n "$key" ] || continue
    export ${key,,}="$value"
  done <<<"$(get_frontmatter)"

  # don't process draft after function call
  test "$draft" != ""		&& return 1

  # skip making index in some cases
  test "$title"  = ""		&& return 0
  test "$public" = false	&& return 0
  test "$index"  = false	&& return 0
  test "$type"   = demo		&& return 0
  iso8601=$(date --iso --date "${date:-NULL}" 2>/dev/null)
  test "$iso8601"   = ""	&& return 0

  # put frontmatter info into variable "index" if title and date are valid
  index_list+=("$iso8601 $path $title")
}

# }}}
# remove SGML comments but keep the top one as frontmatter {{{

ignore_comment() {
  sed '1! { /^<!--$/,/^-->$/ d }'
}

# }}}
# RSS Feed {{{

get_channel_item() {
  path=${1#${OUTPUT_DIR}}
  <<-ITEM cat
	<item>
	  $(grep -Po '<title>.+</title>' ${1} | head -1)
	  <link>https://${HOST}${path}</link>
	  <guid isPermaLink="false">$(md5sum ${1} | cut -d' ' -f1)</guid>
	  <pubDate>$(date --rfc-email -r ${1})</pubDate>
	</item>
	ITEM
}

make_rss() {
  # Use modified time to get latest html files
  items="$(
    ls -1t $OUTPUT_DIR/**/**html \
    | head -10 \
    | while read html_file; do
      get_channel_item $html_file
    done \
    | indent 2 \
  )"

  <<-RSS cat
	<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" >
	<channel>
	  <title>Dummy Website</title>
	  <link>https://${HOST}</link>
	  <atom:link href="http://${HOST}/${RSSFILE}" rel="self" type="application/rss+xml" />
	  <description>$global_description</description>
	  <language>zh-tw</language>
	  <pubDate>$(date --rfc-email)</pubDate>
	  <docs>https://www.rssboard.org/rss-specification</docs>
	${items}
	</channel>
	</rss>
	RSS
}

# }}}

# }}}
# prepare directory for outputs {{{

mkdir -p $OUTPUT_DIR/
rm -rf $OUTPUT_DIR/**{md,html,css}
rsync -avrh --delete $ASSET_DIR/ $OUTPUT_DIR/assets/
rsync -avrh --delete $INPUT_DIR/scripts/ $OUTPUT_DIR/scripts/


if [ -f $ASSET_DIR/top.list ]; then
  while read file; do
    ln -sf assets/$file $OUTPUT_DIR/$file
  done <$ASSET_DIR/top.list
fi

# }}}
# content of templates {{{

declare -r HEAD="$(cat $TEMPLATE_DIR/head.html)"
declare -r HEADER="$(<$TEMPLATE_DIR/header.html indent 2)"
declare -r FOOTER="$(<$TEMPLATE_DIR/footer.html indent 2)"
declare -r INDEX_TEMPLATE="$(cat $TEMPLATE_DIR/index.md)"
index_list=()

# }}}
# For each markdown file {{{

files="$(find "$INPUT_DIR" -type f -name '*md' -not -path './templates/*')"
total=$(wc -l <<<"$files")
declare -i counter
for file in $files; do
  # set variables
  path=$(<<<"$file" sed "s#^${INPUT_DIR}/##; s/\.md$//").html; mkdir -p $(dirname $OUTPUT_DIR/$path)
  content="$(<${file} ignore_comment)"

  # use frontmatter to decide making html file or not
  <<<"$content" check_frontmatter || continue

  # log
  echo -e "\033[1K\r$((counter+=1))/$total\t\tprocessing $path"

  # add original content to $OUTPUT_DIR
  # and generate html file with <h1>
  echo "$content" \
  | tee $OUTPUT_DIR/${file#${INPUT_DIR}/} \
  | { [ -n "$title" ] && echo "# $title"; cat; } \
  | html >$OUTPUT_DIR/$path

  # Set modified time for index/RSS
  mtime='@0'
  [ ! "$public" = false ] && [ -n "$iso8601" ] && mtime="$iso8601"
  touch -m -d "$mtime" $OUTPUT_DIR/$path

  unset h1 comment
done
echo

# }}}
# make index.html {{{

echo -en "Making\t\tindex.html"
export title=""
{
  echo "${INDEX_TEMPLATE}"
  echo -e '<br><br>\n\n'
  latest_posts
} \
| tee $OUTPUT_DIR/index.md \
| html >$OUTPUT_DIR/index.html

touch -m -d @0 $OUTPUT_DIR/index.html
echo -e "\tgenerated"

# }}}
# make RSS file {{{

echo -en "Making\t\tRSS"
make_rss >$OUTPUT_DIR/$RSSFILE
echo -e "\t\tgenerated"

# }}}

# vim: fdm=marker fdl=0
