# -*- shell-script -*-

################################################################################

§class "handler" --with-interface \
    --properties "name" "match=(.+)" "test" "command" \
                 "action" "complete" "fallback:b=0"

bashrun_from_cmdnfh=0
bashrun_handlers_order=""

§function.clone "§handlers.dump" "§handlers._dump"
§function.clone "§handler.code" "§handler._code"

################################################################################

function §handler.fallback? { (( $(§handler.get_fallback) )); }

function §handlers.init {
    bashrun_handlers_order="$(§handlers.select fallback 0) $(§handlers.select fallback 1)"
    §handlers.install_completion
}

function §handlers.apply {
    
    # applies matching handlers to bashrun_command

    §debug "trying to match handlers..."

    local name
    local handled=0

    for name in $bashrun_handlers_order; do
	§handlers.seek "$name"

	local append=1
	local match="$(§handler.get_match)"
	local command="$(§handler.get_command)"
	local action="$(§handler.get_action)"

	local result=""

	# interpolate variables if necessary (e.g. $PAGER, $BROWSER)
	if [[ "$command" =~ \$[a-zA-z0-9_] ]]; then
	    result="$(§interpolate '$command')"
	    §debug -v "$command" "->" -v "$result"
	    command="$result"
	fi

	if [[ "$bashrun_command" =~ $match ]]; then	    

	    §debug -c "$name" ":"  -v "$bashrun_command" "matches" \
		-v "$match" "->" -v "$command"

	    local tests="$(§handler.get_test)"
	    if [[ -n "$tests" ]]; then 
		if [[ "$tests" =~ [abcdefghkprstuwxOGLSN!] ]]; then

        	    # run tests...
		    local test=""
		    local success=0
		    local k=0
		    for(( k=0; k<${#tests}; k++ )); do
			local CHAR=${tests:$k:1}
			if [[ "$CHAR" == "!" ]]; then
			    test="!"
			    continue
			else
			    test="$test -$CHAR"
			    if eval "test $test $bashrun_command"; then
				§debug -c "$name" ":"  \
				    -v "test $test $bashrun_command" "->" -g "succeeds"
				success=1
			    else
				§debug fail -c "$name" ":" \
				    -v "test $test $bashrun_command" "->" -r "fails" 
				success=0
				break
			    fi			
			    test=""
			fi
		    done
		else
		    tests="${tests//!/}"
		    tests="${tests//[abcdefghkprstuwxOGLSN]/}"
		    §debug -c "$name" ":"  \
			-v "$tests" "-> no such file test(s), skipping handler"
		    success=0
		fi
		
		if ! ((success)); then
		    §debug -c "$name" ":"  "not handled"
		    §handlers.next
		    continue
		fi
	    fi

	    # subpattern substitution

	    if [[ "$command" =~ %[0-9] ]]; then

	        # rewrite handler if subpatterns match
		[[ "$bashrun_command" =~ $match ]]

		local i=0
		for (( i=1; i<${#BASH_REMATCH[*]}; i++ )); do
		    
		    local m=${BASH_REMATCH[$i]}
		    §debug -c "$name" ":"  "subpattern match:" -v "$m" "->" -v "%$i"
		    command="${command//%$i/$m}"
		    export append=0
		done
	        # fail if unmatched $n's remain in handler command
		if [[ "$command" =~ [^\\](%[0-9]) ]]; then
		    local missing=${BASH_REMATCH[1]}
		    §debug -c "$name" ":"  -v "$command"
		    §debug fail "^ command incomplete: no content for" -v "$missing"
		    return 1
		fi
	    fi

	    if (( append )); then
		if [[ -n "$command" ]]; then
		    bashrun_command="$command $bashrun_command"
		fi
	    else
		bashrun_command="$command"
	    fi

	    §debug -c "$name" ":"  "result:" -v "$bashrun_command"
	    handled=1

	    if [[ -n "$action" ]]; then
		local saved="$(§action.get_name)"
		if §actions.seek "$action"; then
		    if §action.available?; then
			§debug -c "$name" ":"  "->" -y "$action"
			§action.run
			return 0
		    fi
		fi
	    fi
	    return 0
	fi
    done

    # run bookmark function unless already handled, or pass through
    # command to let bash complain as usual.
    if ! (( handled )); then
	if §command.bookmark?; then
	    $bashrun_command
	    return 0
	else
	    local cmdnfh="$(§function.code command_not_found_handle)"
	    unset -f command_not_found_handle
	    $bashrun_command
	    eval "$cmdnfh"
	fi
    fi
    return 1
}

function §handlers.install_completion {

    local name=""
    local prefix=""
    local compfunc=""

    §handlers.seek_start
    while §handlers.next?; do
	name="$(§handler.get_name)"
	prefix=($(§handler.get_complete))
	if [[ -n prefix ]]; then
	    if §function.defined? +handler-$name-complete; then
	    
		compfunc="function _bashrun_${name} {

	        COMPREPLY=()
	        local cur=\"\${COMP_WORDS[COMP_CWORD]}\"		
		        
                local IFS=\$'\n'
                COMPREPLY=(\$(compgen -W \"\$(+handler-$name-complete)\" \${cur}))
		return 0
	        }"
		eval "$compfunc"
	    
		eval "function $prefix { 
                        §engine.action run 0 \"$prefix \$@\" 
                      }"
		
		complete -o filenames -F "_bashrun_$name" "$prefix"

		# add this to the remote interface, too
		§remote.interface.add "# completion for +handler $name"
		§remote.interface.add "$(§function.code +handler-$name-complete)"
		§remote.interface.add "$compfunc"
		§remote.interface.add \
		    "function $prefix { bashrun2 do run \"$prefix \$@\"; }"
		§remote.interface.add \
		    "complete -o filenames -F '_bashrun_$name' '$prefix'"
	    fi	
	fi
	§handlers.next
    done
}

function §handler.code {

    local name="$(§handler.get_name)"

    §handler._code

    if §function.defined? "+handler-$name-complete"; then
	printf '\n'
	§function.code "+handler-$name-complete"
    fi
}

function §handlers.dump {

    local file="$(§objects.data_home)/${1:-handlers.dump}"
    local name=''
    §handlers._dump "$1"

    local saved="$(§handler.get_name)"

    §handlers.seek_start
    while §handlers.next?; do
	name="$(§handler.get_name)"
	if §function.defined? +handler-$name-complete; then
	    §function.code "+handler-$name-complete" >> "$file"
	fi
	§handlers.next
    done
    §handlers.seek "$saved"    
}


