#!/bin/bash
#shellcheck disable=SC1090

. "$(sfpath)" || exit 3

shellfu import pretty


usage() {
    mkusage "$@" \
        "[options] SRC DST" \
        -- \
            "Copy tree from SRC to DST, converting Markdown to HTML pages." \
            "" \
        -o \
            "-d, --debug    Turn on debbugging output"                      \
            "--log LOG      Store publish plan in LOG file.  (Keep this"    \
            "               file PRIVATE!)"                                 \
            "--if-ready     Publish only if .mdpublish/ready file exists;"  \
            "               delete it afterwards (useful for cron jobs)"    \
            "-f, --force    Remove all content from DST beforehand"         \
            "-v, --verbose  Be more verbose"                                \
            "--version      Show version"                                   \
        -- \
            "By default, mdpublish does not keep logfile.  If you provide"  \
            "one, make sure it's outside the published content, otherwise"  \
            "someone might get list of all files on your site."
}

#
# Markdown to HTML command
#
# Has to accept Markdown on standard input and pring HTML on standard
# output.
#
MDPUBLISH__MDBIN=${MDPUBLISH__MDBIN:-markdown}

#
# Logfile for plan details
#
MDPUBLISH__LOG=${MDPUBLISH__LOG:-/dev/null}

#
# Style to load into each generated HTML page
#
MDPUBLISH__STYLE="_mdpublish/style.css"

#
# Nav to load for each generated HTML page
#
MDPUBLISH__NAV=".mdpublish/nav"

nonempty() {
    #
    # True if directory $1 has files
    #
    local dir=$1
    local es
    test -n "$dir" \
     || warn "usage: nonempty DIR"
    test -d "$dir" \
     || warn "usage: nonempty DIR"
    test -e "$dir"/* 2>/dev/null; es=$?
    case $es in
        0)  return 0 ;;     # one file
        2)  return 0 ;;     # more files (`test` usage error)
        *)  return 1 ;;     # anything else
    esac
}

mktitle() {
    #
    # Read title from Markdown file $MdSrc
    #
    local lines=()
    local head
    head="$(head -3 "$MdSrc")"
    readarray lines <<<"$head"
    debug -v head lines
    test -n "${lines[0]}" || return 1
    grep -qx '==*' <<<"${lines[1]}" || return 1
    test -n "${lines[2]}" && return 1
    echo "${lines[0]}"
    return 0
}

mkhead() {
    #
    # Print page head
    #
    echo '<!DOCTYPE html>'
    echo '<html>'
    echo '    <head>'
    echo "        <title>$Title</title>"
    test -n "$Style" && echo "        <link rel='stylesheet' href='$BaseUrl/$Style'>"
    echo '        <meta charset="utf-8" />'
    echo '    </head>'
    echo '    <body>'
    echo '        <div id="box">'
    mknav
    echo '            <div id="main">'
}

mknav() {
    local self
    local text
    local href
    local self_id
    test -n "$Nav" || return 0
    echo '            <nav>'
    echo '                <ul>'
    while read -r href text self; do
        debug -v self Relpath
        self_id=
        test "$Relpath" == "$self" && self_id=" class='selected'"
        echo "                    <li><a$self_id href='$BaseUrl$href'>$text</a></li>"
    done <"$Nav"
        echo '                </ul>'
        echo '            </nav>'
}

mktail() {
    echo '            </div>'
    echo '            <div id="tail">'
    echo "published by <i><a href='https://netvor.info/scripts/mdpublish'>mdpublish</a></i>, <span title='$(date -Iseconds)'>$(date -I)</span>"
    echo '            </div>'
    echo '        </div>'
    echo '    </body>'
    echo '</html>'
}

conv1() {
    #
    # Convert Markdown file $1 to HTML on stdout
    #
    local MdSrc
    local Title
    MdSrc=$(mktemp -t mdpublish.XXXXXXX)
    cat > "$MdSrc"
    Title=$(mktitle) || Title="(untitled) $Relpath"
    debug -v Title
    mkhead
    <"$MdSrc" conv_links | "$MDPUBLISH__MDBIN"
    mktail
    rm "$MdSrc"
}

show_version() {
    echo "v0.0.0"
    echo "(mdpublish is not versioned yet)"
}

conv_links() {
    #
    # Convert Markdown links to HTML links
    #
    perl -pe '
        m|^  \[[^]]+]: | or next;
        m|: https?:| and next;
        s|\.md$|.html|;
    '
}

main() {
    local SrcTree
    local DstTree
    local force=false
    local Style
    local BaseUrl
    local Nav
    local ck_ready=false
    local PlanLog=$MDPUBLISH__LOG
    #shellcheck disable=SC2034
    while true; do case "$1" in
        -b|--baseurl)   BaseUrl=$2; shift 2 || usage ;;
        -f|--force)     force=true;          shift ;;
        -d|--debug)     PRETTY_DEBUG=true;   shift ;;
        --if-ready)     ck_ready=true;       shift ;;
        --log)          PlanLog=$2; shift 2 || usage ;;
        -v|--verbose)   PRETTY_VERBOSE=true; shift ;;
        --version)      show_version; exit 0 ;;
        -*)             usage ;;
        *)              break ;;
    esac done
    SrcTree=$1; DstTree=$2
    test -n "$SrcTree" || usage -w "no SRC?"
    test -n "$DstTree" || usage -w "no DST?"
    test -d "$SrcTree" || die "no such directory: $SrcTree"
    test -d "$DstTree" || die "no such directory: $DstTree"
    if $ck_ready; then
        if test -f "$SrcTree/.mdpublish/ready"; then
            rm "$SrcTree/.mdpublish/ready" \
             && think "removed ready flag"
        else
            return 1
        fi
    fi
    if nonempty "$DstTree"; then
        if $force; then
            think "cleaning up: $DstTree"
            #shellcheck disable=SC2115
            rm -r "$DstTree/"*
        else
            die "destination has files (use -f to clean): $DstTree"
        fi
    fi
    test -f "$SrcTree/$MDPUBLISH__STYLE" && {
        think "style found: $SrcTree/$MDPUBLISH__STYLE"
        Style="$MDPUBLISH__STYLE"
    }
    test -f "$SrcTree/$MDPUBLISH__NAV" && {
        think "nav found: $SrcTree/$MDPUBLISH__NAV"
        Nav="$SrcTree/$MDPUBLISH__NAV"
    }
    debug -v SrcTree DstTree MDPUBLISH__STYLE MDPUBLISH__MDBIN
    mkplan \
      | tee "$PlanLog" \
      | exec_plan
}

mkplan() {
    #
    # Create publish plan from $SrcTree
    #
    # Output one line for each file to publish, in format
    #
    #     static RELPATH
    #     url RELPATH URL
    #
    # In case of 'static', source will be taken directly from $SrcTree.
    # In case if 'url', source file will be downloaded from URL.
    # In both cases, RELPATH determines relative path of final file
    # under destination tree.  RELPATH is also what determines
    # whether Markdown conversion happens.
    #
    local urls=$SrcTree/.mdpublish/urls
    local relpath
    local url
    find -L "$SrcTree" -type f -printf 'static %P\n' \
      | grep -v \
            -e '^static [.]git$' \
            -e '^static [.]git/.*'
    test -f "$urls" || return 0
    while read -r relpath url; do
        echo "url $relpath $url"
    done <"$urls"
}

exec_plan() {
    #
    # Execute publish plan on stdin
    #
    local ptype
    local Relpath
    local Origin
    local genfn
    local dstpath
    while read -r ptype Relpath Origin; do
        case $ptype in
            static)     genfn=gen_static ;;
            url)        genfn=gen_url;   ;;
            *)  die "invalid publishing keyword: $ptype" ;;
        esac
        debug -v Relpath dstpath
        case $Relpath in
            _mdpublish/*)
                $genfn | publish_pipe "$Relpath"
                ;;
            *.md)
                think "converting: $Relpath"
                $genfn | conv1 | publish_pipe "${Relpath%.md}.html"
                ;;
            .*)
                think "skipping: $Relpath"
                ;;
            *)
                $genfn | publish_pipe "$Relpath"
                ;;
        esac
    done
}

gen_static() {
    #
    # Read static file $Relpath from $SrcTree
    #
    cat "$SrcTree/$Relpath"
}

gen_url() {
    #
    # Read file from URL $Origin
    #
    think "fetching: $Relpath from $Origin"
    curl -fsSL "$Origin" || die "failed to fetch URL: $Origin"
}

publish_pipe() {
    #
    # Publish content of stdin as $1
    #
    local relpath=$1
    local dstpath="$DstTree/$relpath"
    mkdir -p "$(dirname "$dstpath")"
    cat >"$dstpath"
    think "publishing: $relpath"
}

main "$@"
