#!/bin/sh -efu

alterator_api_version="${alterator_api_version:-0}"

. shell-error
. shell-quote

exec 3>&1
[ "$alterator_api_version" -le 0 ] || exec 1>&2

### internal functions
_write_begin()
{
    printf '(\n' >&3
}

_write_end()
{
    printf ')\n' >&3
}

_validate_symbol()
{
    [ -n "${1##*[!0-9A-Za-z_]*}" ] || fatal "wrong attribute name:$1"
}

### quote
string_quote()
{
    sed 's,[\"],\\&,g' -- "$@"
}

newline_unquote()
{
    if [ "$1" != "_objects" ]; then
      local separator=$(printf '\007')
      sed  -e "s,\\\\\(.\),$separator\1,g" \
	   -e "s,${separator}n,\n,g" \
	   -e "s,$separator,,g"
    else
      cat
    fi
}

### main message loop

_userhandler() {  # trick to protect loop values
    local l= readmsg= params=
    local IFS="$IFS" PATH="$PATH" # Protected variables
    [ "$alterator_api_version" -le 0 ] || _write_begin
    "${1:-on_message}" ||: # ignore exit code of user actions
    [ "$alterator_api_version" -le 0 ] || _write_end
}


message_loop() {
    local l= readmsg= params= name= value=
    while read -r l; do
	if [ "$l" != "${l#_message:begin}" ]; then 
	    [ -n "$params" ] && unset $params params ||:
	    readmsg=1
	    continue
	fi
	if [ -n "$readmsg" -a "$l" != "${l#_message:end}" ]; then
	    _userhandler "$1"
	    readmsg=
	fi
	[ -n "$readmsg" ] || continue
	name="$(printf %s\\n "${l%%:*}" | sed -e 's,[^[:alnum:]_],,g')" && 
	value="$(printf %s\\n "${l#*:}" | newline_unquote "$name")" ||
    		continue
	eval "in_$name=\"$(quote_shell "$value")\"" && params="$params in_$name" && name= value= ||:
    done
}

### input/output functions

write_string()
{
    local out="$*"
    if [ -z "${out##*[\"\\\\]*}" ]; then
	out="$(printf %s "$out" |string_quote)" ||
	return 1
    fi
    printf %s "$out"
}

write_bool()
{
    case "$1" in
	[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|[Yy]|1)
	    echo '#t'
	    ;;
	*)
	    echo '#f'
	    ;;
    esac
}


test_bool()
{
    [ "$1" = "#t" ]
}


### i18n support

write_language()
{
    local charset="${po_charset:-UTF-8}"
    echo "$1" |
	sed -r \
	    -e "s,([^;]+)(;|$),\1.$charset\2,g" \
	    -e 's,;,:,g'
}

#Note: gettext uses encoding from lc_ctype and translation from language
po_domain="${po_domain:-alterator-${0##*/}}"

_()
{
    local domain="${2:-$po_domain}"
    local langlist="$(write_language "$in_language")"
    local firstlang="${langlist%%:*}"

    LC_ALL="$firstlang" LANGUAGE="$langlist" gettext "$domain" "$1"
}

### high level output

write_string_param()
{
    _validate_symbol "$1"
    printf '%s "%s"\n' "$1" "$(write_string "$2")" >&3
}

write_bool_param()
{
    _validate_symbol "$1"
    printf '%s %s\n' "$1" "$(write_bool "$2")" >&3
}

write_enum_item()
{
    if [ $# -eq 0 ];then
	echo "WARNING: write_enum_item for stream is deprecated, use write_enum instead" >&2
	string_quote|
	    while read -r name label;do
		[ -n "$name" ] || continue
		printf '(name "%s" label "%s")\n' \
		    "$name" \
		    "${label:-$name}"
	    done
    else
	printf '(name "%s" label "%s")\n' \
	    "$(write_string "$1")" \
	    "$(write_string "${2:-$1}")"
    fi >&3
}

write_enum()
{
    string_quote|
        while read -r name label;do
	    [ -n "$name" ] || continue
	    printf '(name "%s" label "%s")\n' \
		    "$name" \
		    "${label:-$name}"
	done >&3
}

write_table_item()
{
    [ $# -gt 0 -a "$(($# % 2))" = "0" ] || fatal "wrong number of arguments"

    printf '(' >&3
    while [ $# -gt 0 ]; do
	local name="$1";shift
	local value="$1";shift
	_validate_symbol "$name"
	printf ' %s "%s"' "$name" "$(write_string "$value")" >&3
    done
    printf ')' >&3
}

write_error()
{
    [ "$alterator_api_version" -gt 0 ] || _write_begin
    write_string_param 'error' "$1"
    [ "$alterator_api_version" -gt 0 ] || _write_end
}

write_nop()
{
    [ "$alterator_api_version" -gt 0 ] || _write_begin
    [ "$alterator_api_version" -gt 0 ] || _write_end
}

write_debug()
{
    [ -z "$DEBUG" ] || printf "$@" >&2
}

### backward compatibility: api_version < 0
alias simple_quote=string_quote
