# -*- shell-script -*-

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

§class "binding" --properties "keyseq" "keymap" "action" \
                              "is_bound:b" "readline"  

§class "internal" --properties "action" "keymap" "char" "readline"

§class "keyname" --properties "keyseq" "name"                             

bind -m emacs '"\C-x00": accept-line'
bind -m vi-insert '"\C-x00": accept-line'
bind -m vi-command '"\C-x00": accept-line'

bashrun_bindings_next_char=20 
bashrun_bindings_blacklist=" 34 39 127 "                         

bashrun_keymap='emacs' # will be initialized in engine.init

§function.clone "§bindings.init" "§bindings._init"
§function.clone "§bindings.dump" "§bindings._dump"
§function.clone "§internals.dump" "§internals._dump"
§function.clone "§keynames.dump" "§keynames._dump"

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

function §bindings.init {
    local file="$bashrun_cache_home/keybindings"
    [[ -f "$file" ]] && /bin/rm "$file"

    §bindings._init
}

function §binding.bound? { (( $(§binding.get_is_bound) )); }

function §binding.get_keyname {
    local keyseq="$(§binding.get_keyseq)"
    if §keynames.seek "$keyseq"; then
	printf '%s' "$(§keyname.get_name)"
    else
	printf '%s' "$(§ks2kn "$keyseq")"
    fi
}

function §bindings.update {

    # create a binding for a specific keyseq in a specific keymap
    # or update the existing binding

    local keyseq="$1"
    local keymap="$2"
    local action="$3"
    local bind="$4"

    if §bindings.seek "$keyseq" "$keymap"; then
	§binding.set_action "$action"
	§binding.set_keymap "$keymap"   
	§binding.set_is_bound "1" 
	§binding.set_readline "$bind"
    else
	§binding.new "$keyseq" "$keymap" "$action" "1" "$bind"
    fi    
}

function §emacs-mode.dump () {
    §bindings.dump "$@"
}

function §vi-mode.dump () {
    §bindings.dump "$@"
}

function set {
    # overload the set builtin to switch bindings if the user switches
    # the editing mode via set -o vi or set -o emacs

    builtin set "$@"
    if [[ "$1" == "-o" && "$2" =~ (emacs|vi) ]]; then
	local mode="${BASH_REMATCH[1]}-mode"
	local feedback=$bashrun_feedback
	bashrun_feedback=0

	§bindings.clear
	§internals.clear
	§keynames.clear
	§configs.clear

	§configs.restore_core "$mode" &>/dev/null
	§configs.collect_user_configs "$mode"

	§configs.seek "$mode" && §config.restore 

	+mode-${bashrun_mode}-init

	§readline.get_settings

	bashrun_feedback=$feedback
    fi
}

function §bindings.dump {

    local file="$(§objects.data_home)/${1:-bindings.dump}"
    local remote_bindings="$bashrun_cache_home/remote-bindings.bash"

    [[ -f "$remote_bindings" ]] && /bin/rm "$remote_bindings"

    §bindings._dump "${1:-bindings.dump}"

    printf '%s\n' "bashrun_bindings_next_char=$bashrun_bindings_next_char" >> $file

    §internals.dump "${1:-bindings.dump}" append
    §keynames.dump "${1:-bindings.dump}" append

    local saved=$(§binding.get_keyseq)

    §bindings.seek_start
    while §bindings.next?; do
	if §binding.bound?; then
	    # dump the bashrun binding
	    printf '%s\n' "bind $(§binding.get_readline)" >> $file

	    # create the remote interface binding
	    local keyseq="$(§binding.get_keyseq)"
	    local keymap="$(§binding.get_keymap)"
	    local action="$(§binding.get_action)"
	    
	    §internals.seek "$action" "$keymap"
	    local char="$(§internal.get_char)"
	    
	    §remote.interface.bind "internal" \
		"$action" "$keymap" "$char" "$keyseq"
	    §remote.interface.bind "requested" \
		"$action" "$keymap" "$char" "$keyseq"	
	fi
	§bindings.next
    done
    §bindings.seek $saved
}

function §bindings.print {

    local w="\033[1;37m" # white
    local r="\033[1;31m" # red
    local g="\033[1;32m" # green 
    local b="\033[1;34m" # blue
    local c="\033[1;36m" # cyan
    local m="\033[1;35m" # magenta
    local y="\033[1;33m" # yellow
    local n="\033[0m"    # none

    local mode="$(§readline.get_setting 'editing-mode')"
    local keymaps="emacs"
    local keymap id ids covered action keyname
    
    if [[ "$mode" == "vi" ]]; then
	keymaps="vi-insert vi-command"
    fi
    mode+="-mode"
    
    for keymap in $keymaps; do
	ids="$(§bindings.select keymap "$keymap")"
	[[ -z "$ids" ]] && continue

	printf '%b' "${w}Bashrun2 Keybindings$n ($c$mode$n, keymap: $c$keymap$n):\n\n"

	covered=""

	for id in $ids; do
	    §bindings.seek "$id"
	    §binding.bound? || continue

	    action="$(§binding.get_action)"
	    if §actions.seek "$action"; then
		keyname="$(§binding.get_keyname)"
		[[ " $covered " =~ " $keyname " ]] && continue
			
		printf '%b' "\t$b$keyname$n\t"
		printf '%b' "$(§interpolate "$(§action.get_description)" check)"
		if [[ -n "$(§action.get_depends)" ]]; then
    		    printf '%b' " ($(§action.print_depends))"
		fi
		printf '\n'
		covered+="$keyname "
	    fi
	done
	printf '\n'
    done	
}

function §internals.dump {

    local file="$(§objects.data_home)/${1:-internals.dump}"

    §internals._dump "${1:-internals.dump}" append

    local saved=$(§internal.get_action)

    §internals.seek_start
    while §internals.next?; do
	printf '%s\n' "bind $(§internal.get_readline)" >> $file
	§internals.next
    done    
    §internals.seek $saved
}

function §keynames.dump {

    §keynames._dump "${1:-§keynames.dump}" append

}

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