# -*- mode: sh; sh-shell: bash -*-
# vim: set ft=bash:
# shellcheck shell=bash
# shellcheck disable=2016

### Module to install, initialize and update bar itself

function init # --hidden|--update|--update-existing - initialize bar in a workdir.
{
    # shellcheck disable=2155
    local origin="$(command -v bar)"
    local prefix="${origin%/bin/*}"

    [[ -f "$prefix/bin/bar" && -d "$prefix/share/bar/Bar.d" && -f "$prefix/share/bar/Barf" ]] ||
        die "bar not properly installed, run './bar init_install <prefix>' from its git repository first"

    local update=
    local update_existing=
    local hidden=
    if [[ "${1:-}" == --update ]]; then
        update=true
        shift
    elif [[ "${1:-}" == --update-existing ]]; then
        update_existing=true
        shift
    elif [[ "${1:-}" == --hidden ]]; then
        hidden=true
        shift
    fi

    if [[ -z "$update" && ( -f bar || -f .bar )]]; then
        die "bar already initialized"
    fi

    if [[ -n "$update" || -n "$update_existing" ]]; then
        if [[ -f bar ]]; then
            if [[ "$origin" -nt "bar" ]]; then
                debug "updating 'bar'"
                # prevent writing into the running script
                cp -L "$origin" "bar.new"
                mv  "bar.new" "bar"
            fi
            if [[ -n "$update" ]]; then
                debug "updating 'Bar.d'"
                cp -Lru "$prefix/share/bar/Bar.d/"* "Bar.d"
            else
                debug "updating existing 'Bar.d'"
                rsync -rtuL --existing "$prefix/share/bar/Bar.d/" "Bar.d/"
            fi
        elif [[ -f .bar ]]; then
            if [[ "$origin" -nt ".bar" ]]; then
                debug "updating '.bar'"
                cp -L "$origin" ".bar.new"
                mv  ".bar.new" ".bar"
            fi
            if [[ -n "$update" ]]; then
                debug "updating '.Bar.d'"
                cp -Lru "$prefix/share/bar/Bar.d/"* ".Bar.d"
            else
                debug "updating existing '.Bar.d'"
                rsync -rtuL --existing "$prefix/share/bar/Bar.d/" "Bar.d/"
            fi
        else
            die "bar not initialized"
        fi
        return 0
    fi

    if [[ -z "$hidden" ]]; then
        debug "installing bar"
        cp -L "$origin" "bar.new"
        mv  "bar.new" "bar"
        cp -L "$prefix/share/bar/Barf" "Barf"
        cp -Lr "$prefix/share/bar/Bar.d" "Bar.d"
    else
        debug "installing .bar"
        cp -L "$origin" ".bar"
        cp -L "$prefix/share/bar/Barf" ".Barf"
        cp -Lr "$prefix/share/bar/Bar.d" ".Bar.d"
    fi
    return 0
}

rule init_update_gitignore: is_git_toplevel? -- '
     require --opt testdir
     git_add_ignore ".bar.\*"
     [[ -v TESTDIR_PREFIX ]] && git_add_ignore "$TESTDIR_PREFIX-"\*
'

## Initialize bar in a workdir. This copies the main 'bar' script, a default 'Barf' and all
## 'Bar.d/' modules to current directory. When the workdir is a git repository then
## '.gitignore' is updated to exclude the files and directories 'bar' will use for
## temporary/testing storage. A user may edit the 'Barf' file to suit the project/personal
## preference.  Unneeded modules in 'Bar.d' can be deleted. When that is done the new/changed
## files should be committed to version control.
rule init: 'called_as? bar' -
rule init: 'called_as? bar' init_update_gitignore
rule init: '!called_as bar' -- error "not called as installed bar"

## Updates a initialized working tree. That is 'bar' itself will be updated when a newer
## version is available and any module in 'Bar.d/' that exists in the local working tree will
## be updated as well.  Barf will be left untouched.
rule init_update_existing: 'called_as? bar' -- init --update-existing
rule init_update_existing: 'called_as? bar' init_update_gitignore
rule init_update_existing: '!called_as bar' -- error "not called as installed bar"

## Updates a initialized working tree. That is 'bar' itself will be updated when a newer
## version is available and all modules including ones that are not present in 'Bar.d/' will
## be updated as well.  Barf will be left untouched.
rule init_update: 'called_as? bar' -- init --update
rule init_update: 'called_as? bar' init_update_gitignore
rule init_update: '!called_as bar' -- error "not called as installed bar"

## Since the Barf is the users configuration we can't just update it this 'a bit mouthful'
## rule will do a merge of the local './Barf' with the shipped 'Barf.default'.  This will
## leave conflicts which have to be manually resolved. This is only useful when the shipped
## when 'Barf' was not or only slightly edited.
function init_update_merge_barf
{
    # shellcheck disable=2155
    local origin="$(command -v bar)"
    local prefix="${origin%/bin/*}"

    if [[  -f "$prefix/share/bar/Barf" ]]; then
        info "merging Barf file"
    else
        die "bar not properly installed, run './bar init_install <prefix>' from its git repository first"
    fi

    if [[ -f Barf ]]; then
        git merge-file --diff-algorithm=histogram ./Barf /dev/null "$prefix/share/bar/Barf" ||
            note "Please check the merged Barf file conflict markers and fix them"
    elif [[ -f .Barf ]]; then
        git merge-file --diff-algorithm=histogram ./.Barf /dev/null "$prefix/share/bar/Barf" ||
            note "Please check the merged Barf file conflict markers and fix them"
    else
        die "bar not initialized"
    fi
}

function init_install ## [prefix] - install links in [prefix] or '~/.local'.
{
    ## Bar will usually exist somewhere in a users home directory where it got checked
    ## out. Bar does rolling releases, it is continuously improved. To make this checked out
    ## version available for use this 'init_install' will create symlinks at the given
    ## [prefix] directory or in '~/.local' by default.
    local prefix="${1:-$HOME/.local}"
    shift

    # shellcheck disable=2155
    local origin="$(realpath "$BASH_ARGV0")"
    # link the main script
    if [[ "$origin" != *"/bar" ]]; then
        die "not called as original bar"
    elif [[ -d "$prefix" && ! -f "$prefix/bin/bar" ]]; then
        mkdir -p "$prefix/bin"
        ln -s "$origin" "$prefix/bin/bar" || error "linking $origin to $prefix/bin/bar"
        info "linked $origin to $prefix/bin/bar"
        ln -s "$origin" "$prefix/bin/please" || error "linking $origin to $prefix/bin/please"
        info "linked $origin to $prefix/bin/please"
    fi
    # link Bar.d
    if [[ ! -d "$prefix/share/bar" ]]; then
        mkdir -p "$prefix/share/bar"
        ln -s "${origin%/*}/Bar.d" "$prefix/share/bar/Bar.d"
        info "linked ${origin%/*}/Bar.d to $prefix/share/bar/Bar.d"
        ln -s "${origin%/*}/Barf.default" "$prefix/share/bar/Barf"
        info "linked ${origin%/*}/Barf.default to $prefix/share/bar/Barf"
        ln -s "${origin%/*}/Pleasef.default" "$prefix/share/bar/Pleasef"
        info "linked ${origin%/*}/Pleasef.default to $prefix/share/bar/Pleasef"
    else
        warn "can't link to $prefix/share/bar/"
    fi

    # link .config/please
    if [[ -L "$HOME/.config/please/std_lib" || ! -d "$HOME/.config/please" ]]; then
        mkdir -p "$HOME/.config/please"
        ln -s "${origin%/*}/Bar.d/"* "$HOME/.config/please/" || true   # TODO: better update handling
        info "linked ${origin%/*}/Bar.d/* to $HOME/.config/please/"
    else
        warn "can't create to $HOME/.config/please"
    fi

    if [[ ! -f "$HOME/.Pleasef" ]]; then
        cp -L "$prefix/share/bar/Pleasef" "$HOME/.Pleasef"
        info "installed default $HOME/.Pleasef"
    fi

    # link bash completion
    if [[ ! -d "$HOME/.bash_completion" ]]; then
        mkdir -p "$HOME/.bash_completion"
    fi
    if [[ ! -f "$HOME/.bash_completion/bar_complete" ]]; then
        ln -s "${origin%/*}/contrib/bar_complete" "$HOME/.bash_completion/bar_complete" || error "linking completion script"
        info "linked ${origin%/*}/contrib/bar_complete to $HOME/.bash_completion/bar_complete"
        note "To enable bash completion, source $HOME/.bash_completion/bar_complete in your .bashrc or restart your shell"
    fi
}

