#!/usr/bin/env bash

# Quick script to handle updating our dotfiles

# A better class of script...
set -o errexit          # Exit on most errors (see the manual)
set -o errtrace         # Make sure any error trap is inherited
set -o nounset          # Disallow expansion of unset variables
set -o pipefail         # Use last non-zero exit code in a pipeline
#set -o xtrace          # Trace the execution of the script (debug)

# Path to the dot-manage script
DOT_MANAGE='dot-manage'

# DESC: Usage help
# ARGS: None
# OUTS: None
function script_usage() {
    cat << EOF
Usage:
     -h|--help                  Displays this help
     -v|--verbose               Displays verbose output
    -nc|--no-colour             Disables colour output

    Update Options:
     -a|--all                   Update all the things
     -b|--bin                   Update binaries
     -e|--vim                   Update Vim plug-ins
     -g|--git                   Update Git sources (default)
     -s|--stow                  Run dot-manage (default)
EOF
}

# DESC: Parameter parser
# ARGS: $@ (optional): Arguments provided to the script
# OUTS: Variables indicating command-line parameters and options
function parse_params() {
    local param

    while [[ $# -gt 0 ]]; do
        param="$1"
        shift
        case $param in
            -h | --help)
                script_usage
                exit 0
                ;;
            -v | --verbose)
                verbose=true
                ;;
            -nc | --no-colour)
                no_colour=true
                ;;
            -a | --all)
                bin_update=true
                git_update=true
                stow_update=true
                vim_update=true
                ;;
            -b | --bin)
                bin_update=true
                ;;
            -e | --vim)
                vim_update=true
                ;;
            -g | --git)
                git_update=true
                ;;
            -s | --stow)
                stow_update=true
                ;;
            *)
                echo "Invalid parameter was provided: $param"
                exit 1
                ;;
        esac
    done

    if [[ -z ${bin_update-} &&
          -z ${git_update-} &&
          -z ${stow_update-} &&
          -z ${vim_update-} ]]; then
        default_update=true
    fi
}

# DESC: Find some important paths we need before script_init() is called
# ARGS: None
# OUTS: $script_real_path: The full canonicalized path to the script
#       $script_real_dir: The canonicalized directory path of the script
#       $script_real_name: The canonicalized file name of the script
#       $dotfiles_path: The dotfiles directory path
#       $dotfiles_name: The dotfiles directory name
# shellcheck disable=SC2034
function early_init() {
    # Determine the physical directory this script resides in. This approach is
    # necessarily clumsy as simple methods like "readlink -f" and "realpath"
    # are not platform independent. We have to work on Linux, BSD, OS X, etc...
    script_real_path="$(perl -MCwd -le 'print Cwd::abs_path(shift)' "${BASH_SOURCE[0]}")"
    script_real_dir="$(dirname "$script_real_path")"
    script_real_name="$(basename "$script_real_path")"
    readonly script_real_path script_real_dir script_real_name

    # Dotfiles directory path & name
    dotfiles_path="$(cd -P "$script_real_dir/../../" && pwd)"
    dotfiles_name="$(basename "$dotfiles_path")"
    readonly dotfiles_path dotfiles_name
}

# DESC: Main control flow
# ARGS: $@ (optional): Arguments provided to the script
# OUTS: None
function main() {
    early_init

    # shellcheck source=scripts/bin/bash-template.sh
    source "$script_real_dir/bash-template.sh"

    trap script_trap_err ERR
    trap script_trap_exit EXIT

    script_init "$@"
    parse_params "$@"
    colour_init

    cd "$dotfiles_path"

    if [[ -n ${default_update-} || -n ${git_update-} ]]; then
        pretty_print '*** Updating Git sources ...'
        check_binary 'git' 'fatal'

        # Default to the upstream branch for an unqualified merge
        git config merge.defaultToUpstream true

        git fetch
        git merge --ff-only
        git submodule init
        git submodule update
    fi

    if [[ -n ${default_update-} || -n ${stow_update-} ]]; then
        "./$DOT_MANAGE"
    fi

    if [[ -n ${bin_update-} ]]; then
        pretty_print '*** Updating binaries ...'
        scripts/bin/dl-shellcheck || true
        scripts/bin/dl-shfmt || true

        # shellcheck disable=SC2310
        if check_binary docker; then
            scripts/bin/dl-hadolint
        fi
    fi

    if [[ -n ${vim_update-} ]]; then
        pretty_print '*** Updating Vim plug-ins ...'
        check_binary 'vim' 'fatal'
        vim +PlugUpdate +qall
    fi
}

# Update all the things!
main "$@"

# vim: syntax=sh cc=80 tw=79 ts=4 sw=4 sts=4 et sr
