#!/usr/bin/bash

. "$(sfpath)" || exit 3

shellfu import pretty

SFD_EMBED_BEGIN="#----- SFDOC EMBEDDED POD BEGIN -----#"
SFD_EMBED_END="#----- SFDOC EMBEDDED POD END -----#"
SFD_EMBED_PLACE="#----- SFDOC EMBEDDED POD PLACEHOLDER -----#"

usage() {
    mkusage "$@" \
            "[options] [-u] FILE.SH"                                        \
            "[options] -r FILE.SH"                                          \
        -c                                                                  \
            "-u     (default) add or update POD under name NAME from FILE"  \
            "-r     remove embedded POD from FILE"                          \
        -o                                                                  \
            "-B BEGIN_MARK   use BEGIN_MARK to demark beginning of the"     \
            "       embed section in FILE"                                  \
            "-E END_MARK  use END_MARK to demark ending of the embed"       \
            "       section in FILE."                                       \
            "-P PLACEHOLDER  use PLACEHOLDER to demark empty embed section" \
            "       embedded section"                                       \
            "-n NAME   refer to module as NAME in in POD meta-data and"     \
            "       outline"                                                \
            "-d     show debugging information"                             \
            "-i     in-place update; ie. replace original FILE"             \
        --                                                                  \
        "FILE must be formatted according to Docstrings section of Shellfu" \
        "Coding Style[1]."                                                  \
        ""                                                                  \
        "Additionally, it must contain embed section, which will be"        \
        "replaced with documentation extracted from the FILE.  A valid"     \
        "embed section is either a single line marking empty section:"      \
        ""                                                                  \
        "    $SFD_EMBED_PLACE"                                              \
        ""                                                                  \
        "or (when is POD already embedded) following lines in this order:"  \
        ""                                                                  \
        "    $SFD_EMBED_BEGIN"                                              \
        "    [...any number of lines ...]"                                  \
        "    [...containing POD already embedded ...]"                      \
        "    $SFD_EMBED_END"                                                \
        ""                                                                  \
        "Marker strings can be changed using envvars SFD_EMBED_PLACE,"      \
        "SFD_EMBED_BEGIN, SFD_EMBED_END or options -P, -B and -E, but they" \
        "must always be single line."                                       \
        ""                                                                  \
        "  [1]: https://gitlab.com/vornet/shellfu/shellfu/-/blob/master/notes/style.md"
}

locate_line() {
    #
    # Find line $1 in file $2 and print line number
    #
    local line=$1
    local file=$2
    local match
    match=$(grep -nFxm1 "$line" "$file")
    test -n "$match" || { echo "N"; return 1; }
    echo "${match%%:*}"
}

validate() {
    #
    # Validate $1 for mark errors
    #
    local src=$1    # file to validate
    local bgn_addr  # begin mark's line number or 'N' if mark is missing
    local plh_addr  # placeholder  ^^^^
    local end_addr  # end mark's   ^^^^
    local err       # error message, if any was identified
    bgn_addr=$(locate_line "$BeginMark" "$src")
    plh_addr=$(locate_line "$Placeholder" "$src")
    end_addr=$(locate_line "$EndMark" "$src")
    err=""
    case $bgn_addr:$end_addr:$plh_addr in
        N:N:N)  err="missing embed section in $src" ;;
        N:N:*)  true ;;
        N:*:*)  err="invalid embed section: end mark without begin mark at $end_addr in $src" ;;
        *:N:*)  err="invalid embed section: begin mark without end mark at $bgn_addr in $src" ;;
        *:*:N)  test "$bgn_addr" -lt "$end_addr" || err="invalid embed section: swapped begin and end marks at $end_addr and $bgn_addr in $src" ;;
        *:*:*)  err="invalid embed section: too many marks at $bgn_addr, $end_addr and $plh_addr in $src" ;;
    esac
    test -n "$err" || return 0
    warn "$err"
    return 1
}

remove() {
    #
    # Strip embedded documentation from $1
    #
    local src=$1
    <"$src" awk "
        BEGIN { p=1 }
        /^$BeginMark$/ {
            p=0
            next
        }
        /^$EndMark$/ {
            p=1
            print \"$Placeholder\"
            next
        }
        p {
            print
        }
    "
}

mkembed_comment() {
    #
    # Print explanatory comment
    #
    echo '#'
    echo '# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'
    echo '# !! DO NOT EDIT section between this comment      !!'
    echo '# !! and the end mark or YOUR CHANGES WILL BE LOST !!'
    echo '# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'
    echo '#'
    echo '# The end mark should look like this:'
    echo '#'
    echo "#     $EndMark"
    echo '#'
    echo '# To update module documentation properly:'
    echo '#'
    echo '#  1. Edit in-code docstrings according to Docstrings section'
    echo '#     of the Shellfu coding style guide:'
    echo '#'
    echo '#     <https://gitlab.com/vornet/shellfu/shellfu/-/blob/master/notes/style.md>'
    echo '#'
    echo '#  2. Run following command to re-build this section:'
    echo '#'
    echo '#         sfembed_pod -n "library/name" -i path/to/lib.sh'
    echo '#'
    echo '# Call sfembed_pod --help for more details.'
    echo '#'
    echo '# For Fedora-based distributions, sfembed_pod can be found in'
    echo '# shellfu-devel package in this COPR:'
    echo '#'
    echo '# <https://copr.fedorainfracloud.org/coprs/netvor/shellfu/>'
    echo '#'
    echo
}

mkpod() {
    #
    # Extract POD from $1
    #
    local src=$1
    echo '#shellcheck disable=SC2217'
    sfdoc --name "$Name" -e pod "$(readlink -f "$src")"
}

embed() {
    #
    # Add self-generated POD inside $1
    #
    local src=$1
    local line
    #shellcheck disable=SC2002
    cat "$src" \
     |  while IFS= read -r line;
        do
            case $line in
                "$Placeholder")
                    echo "$BeginMark"
                    mkembed_comment
                    mkpod "$src"
                    echo "$EndMark"
                    ;;
                *)
                    printf "%s\n" "$line"
                    ;;
            esac
        done
}

guess_name() {
    #
    # Knock the module name out of $ModuleFile
    #
    sed '
        s|.*/||
        s|[.][[:alpha:]]*sh$||
    ' <<<"$ModuleFile"
}

main() {
    local ModuleFile
    local naked
    local cache
    local Name
    local action=update
    local inplace=false
    local BeginMark=$SFD_EMBED_BEGIN
    local EndMark=$SFD_EMBED_END
    local Placeholder=$SFD_EMBED_PLACE
    naked=$(mktemp /tmp/sfembed_pod.naked.XXXXXXXX.sh)
    cache=$(mktemp /tmp/sfembed_pod.cache.XXXXXXXX.sh)
    while true; do case $1 in
        -B) BeginMark="$2";         shift 2 || usage -M BEGIN_MARK ;;
        -E) EndMark="$2";           shift 2 || usage -M END_MARK ;;
        -P) Placeholder="$2";       shift 2 || usage -M PLACEHOLDER ;;
        -d) PRETTY_DEBUG=true;      shift ;;
        -i) inplace=true;           shift ;;
        -n) Name="$2";              shift 2 || usage -M NAME ;;
        -r) action=remove;          shift ;;
        -u) action=update;          shift ;;
        -*) usage -u "$1" ;;
        *)  break ;;
    esac done
    ModuleFile=$1
    test -n "$ModuleFile" || usage -w "no FILE?"
    test -f "$ModuleFile" || die "no such file: $ModuleFile"
    test -n "$Name" || Name=$(guess_name)
    validate "$ModuleFile" || die
    debug -v action Name ModuleFile inplace naked cache
    case $action in
        embed)      embed "$ModuleFile"             || die ;;
        remove)     remove "$ModuleFile"            || die ;;
        update)     remove "$ModuleFile" >"$naked"  || die
                    embed "$naked"                  || die ;;
    esac > "$cache"
    if $inplace;
    then
        cat "$cache" > "$ModuleFile"
    else
        cat "$cache"
    fi
    rm "$naked" "$cache"
}

main "$@"
