#!/bin/bash -u
# shellcheck disable=SC2034,SC2086,SC2145,SC2155,SC2294

# CLOS like modifiers for bash functions.


_FUNCMOD_proxy () {

    local funcname=$1
    local list
    local var
    shift

    local args=( "$@" )

    var="_FUNCMOD_before_${funcname}"

    if [ "${!var:+set}" = set ]
    then

	eval 'list=( ${'"$var"'[@]} )'
	for func in "${list[@]}"; do
	    eval $func "$@"
	done
    fi

    eval "_FUNCMOD_around_${funcname} $@"

    var="_FUNCMOD_after_${funcname}"

    if [ "${!var:+set}" = set ]
    then

	eval 'list=( ${'"$var"'[@]} )'
	for func in "${list[@]}" ; do
	    eval $func "$@"
	done
    fi

}

_FUNCMOD_copy_func () {

    local src=$1
    local dst=$2

    local definition=$(declare -f $src)
    definition=${definition/#$src/$dst}

    eval "$definition"
}

_FUNCMOD_func_name() {

    local funcname=$1
    local cntr=_FUNCMOD_cnt_${funcname}

    local n=${!cntr}

    echo _FUNCMOD_${n}_${funcname}
}


_FUNCMOD_install_proxy() {

    local funcname=$1
    local cntr=_FUNCMOD_cnt_${funcname}

    [ "${!cntr:-}" != '' ] && return

    eval "$cntr=0"
    _FUNCMOD_copy_func $funcname _FUNCMOD_around_${funcname}
    eval $funcname'() { _FUNCMOD_proxy '"$funcname"' "$@" ; }'
}

before() {

    local funcname=$1
    _FUNCMOD_install_proxy $funcname

    local modifier=$2

    local list=_FUNCMOD_before_${funcname}

    eval "$list=( $modifier \${${list}[@]} )"
}

after() {

    local funcname=$1
    _FUNCMOD_install_proxy $funcname
    local modifier=$2

    local list=_FUNCMOD_after_${funcname}
    eval "$list+=( $modifier )"
}



around() {

    local funcname=$1

    _FUNCMOD_install_proxy $funcname

    local modifier=$2
    local cntr=_FUNCMOD_cnt_${funcname}

    eval "let ++$cntr"

    local orig=$(_FUNCMOD_func_name $funcname)
    _FUNCMOD_copy_func  _FUNCMOD_around_${funcname} "$orig"

    eval "_FUNCMOD_around_${funcname}"'() { '"$modifier"' '"$orig"' "$@" ; }'

}
