########################################################################
#                                                                      #
#               This software is part of the ast package               #
#          Copyright (c) 1982-2013 AT&T Intellectual Property          #
#                      and is licensed under the                       #
#                 Eclipse Public License, Version 1.0                  #
#                    by AT&T Intellectual Property                     #
#                                                                      #
#                A copy of the License is available at                 #
#          http://www.eclipse.org/org/documents/epl-v10.html           #
#         (with md5 checksum b35adb5213ca9657e911e9befb180842)         #
#                                                                      #
#              Information and Software Systems Research               #
#                            AT&T Research                             #
#                           Florham Park NJ                            #
#                                                                      #
#                    David Korn <dgkorn@gmail.com>                     #
#                                                                      #
########################################################################

function checkref
{
    nameref foo=$1 bar=$2
    if [[ $foo !=  $bar ]]
    then
        log_error "foo=$foo != bar=$bar"
    fi

    foo=hello
    if [[ $foo !=  $bar ]]
    then
        log_error "foo=$foo != bar=$bar"
    fi

    foo.child=child
    if [[ ${foo.child} !=  ${bar.child} ]]
    then
        log_error "foo.child=${foo.child} != bar=${bar.child}"
    fi
}

name=first
checkref name name
name.child=second
checkref name name
.foo=top
.foo.bar=next
checkref .foo.bar .foo.bar
if [[ ${.foo.bar} !=  hello ]]
then
    log_error ".foo.bar=${.foo.bar} != hello"
fi

if [[ ${.foo.bar.child} !=  child ]]
then
    log_error ".foo.bar.child=${.foo.bar.child} != child"
fi

function func1
{
        nameref color=$1
        func2 color
}

function func2
{
        nameref color=$1
        set -s -- ${!color[@]}
    print -r -- "$@"
}

typeset -A color
color[apple]=red
color[grape]=purple
color[banana]=yellow
if [[ $(func1 color) != 'apple banana grape' ]]
then
    log_error "nameref or nameref not working"
fi

nameref x=.foo.bar
if [[ ${!x} != .foo.bar ]]
then
    log_error "${!x} not working"
fi

typeset +n x $(typeset +n)
unset x
nameref x=.foo.bar
function x.set
{
    [[ ${.sh.value} ]] && print hello
}
if [[ $(.foo.bar.set) != $(x.set) ]]
then
    log_error "function references  not working"
fi

if [[ $(typeset +n) != x ]]
then
    log_error "typeset +n doesn't list names of reference variables"
fi

if [[ $(typeset -n) != x=.foo.bar ]]
then
    log_error "typeset +n doesn't list values of reference variables"
fi

file=$TEST_DIR/test
typeset +n foo bar 2> /dev/null
unset foo bar
export bar=foo
nameref foo=bar
if [[ $foo != foo ]]
then
    log_error "value of nameref foo !=  $foo"
fi

cat > $file <<\!
print -r -- $foo
!
chmod +x "$file"
y=$( $file)
if [[ $y != '' ]]
then
    log_error "reference variable not cleared"
fi

{
    command nameref xx=yy
    command nameref yy=xx
} 2> /dev/null && log_error "self reference not detected"
typeset +n foo bar
unset foo bar
set foo
nameref bar=$1
foo=hello
if [[ $bar !=  hello ]]
then
    log_error 'nameref of positional paramters outside of function not working'
fi

unset foo bar
bar=123
function foobar
{
    typeset -n foo=bar
    typeset -n foo=bar
}
foobar 2> /dev/null || log_error 'nameref not unsetting previous reference'
(
    nameref short=verylong
    short=( A=a B=b )
    if [[ ${verylong.A} != a ]]
    then
        log_error 'nameref short to longname compound assignment error'
    fi
) 2> /dev/null|| log_error 'nameref short to longname compound assignment error'
unset x
if [[    $(var1=1 var2=2
        for i in var1 var2
        do
            nameref x=$i
            print $x
        done) != $'1\n2' ]]
then
    log_error 'for loop nameref optimization error'
fi

if [[    $(typeset -A var1 var2
        var1[sub1]=1 var2[sub2]=1
        for i in var1 var2
        do
            typeset -n array=$i
            print ${!array[*]}
        done) != $'sub1\nsub2' ]]
then
     log_error 'for loop nameref optimization test2 error'
fi

unset -n x foo bar
if [[ $(nameref x=foo;for x in foo bar;do print ${!x};done) != $'foo\nbar' ]]
then
    log_error 'for loop optimization with namerefs not working'
fi

if [[ $(
    p=(x=(r=3) y=(r=4))
    for i in x y
    do
        nameref x=p.$i
        print ${x.r}
    done
) != $'3\n4' ]]
then
    log_error 'nameref optimization error'
fi

[[ $(
unset x y var
var=(foo=bar)
for i in y var
do
    typeset -n x=$i
    if [[ ${!x.@} ]]
    then
        print ok
    fi

    typeset +n x
done) != ok ]] && log_error 'invalid for loop optimization of name references'
function setval # name value
{
    nameref arg=$1
    nameref var=arg.bar
    var=$2
}
foo=( integer bar=0)
setval foo 5
(( foo.bar == 5)) || log_error 'nested nameref not working'
function selfref
{
    typeset -n ps=$1
    print -r -- "${ps}"
}
ps=(a=1 b=2)
[[ $(selfref ps) == *a=1* ]] ||  log_error 'local nameref cannot reference global variable of the same name'
function subref
{
    typeset -n foo=$1
    print -r -- ${foo.a}
}
[[ $(subref ps) == 1 ]] ||  log_error 'local nameref cannot reference global variable child'

function local
{
    typeset ps=(typeset -i a=3 b=4)
    [[ $(subref ps) == 3 ]] ||  log_error 'local nameref cannot reference caller compound variable'
}
local
unset -f local
function local
{
    qs=(integer  a=3; integer b=4)
}
local 2> /dev/null || log_error 'function local has non-zero exit status'
[[ ${qs.a} == 3 ]] || log_error 'function cannot set compound global variable'
unset fun i
foo=(x=hi)
function fun
{
        nameref i=$1
        print -r -- "${i.x}"
}
i=foo
[[ $(fun $i) == hi ]] || log_error 'nameref for compound variable with in function name of caller fails'
unset -n foo bar
typeset -A foo
foo[x.y]=(x=3 y=4)
nameref bar=foo[x.y]
[[ ${bar.x} == 3 ]] || log_error 'nameref to subscript containing . fails'
[[ ${!bar} == 'foo[x.y]' ]] || log_error '${!var} not correct for nameref to an array instance'
typeset +n bar
nameref bar=foo
[[ ${!bar} == foo ]] || log_error '${!var} not correct for nameref to array variable'
$SHELL -c 'function bar { nameref x=foo[++];};typeset -A foo;bar' 2> /dev/null ||log_error 'nameref of associative array tries to evaluate subscript'
i=$($SHELL -c 'nameref foo=bar; bar[2]=(x=3 y=4); nameref x=foo[2].y;print -r -- $x' 2> /dev/null)
[[ $i == 4 ]] || log_error 'creating reference from subscripted variable whose name is a reference failed'
[[ $($SHELL 2> /dev/null <<- '+++EOF'
	function bar
	{
		nameref x=$1
		print -r -- "$x"
	}
	function foo
	{
		typeset var=( foo=hello)
		bar var
	}
	foo
+++EOF
) ==  *foo=hello* ]] || log_error 'unable to display compound variable from name reference of local variable'
for c in '=' '[' ']' '\' "'" '"' '<' '=' '('
do
    [[ $($SHELL 2> /dev/null <<- ++EOF++
	i=\\$c;typeset -A a; a[\$i]=foo;typeset -n x=a[\$i]; print "\$x"
	++EOF++
) != foo ]] && log_error 'nameref x=a[$c] '"not working for c=$c"
done

for c in '=' '[' ']' '\' "'" '"' '<' '=' '('
do
    [[ $($SHELL 2> /dev/null <<- ++EOF++
	i=\\$c;typeset -A a; a[\$i]=foo;b=a[\$i];typeset -n x=\$b; print "\$x"
	++EOF++
) != foo ]] && log_error 'nameref x=$b with b=a[$c] '"not working for c=$c"
done

unset -n foo x
unset foo x
typeset -A foo
nameref x=foo[xyz]
foo[xyz]=ok
[[ $x == ok ]] || log_error 'nameref to unset subscript not working'
function function2
{
    nameref v=$1
    v.x=19 v.y=20
}
function function1
{
    typeset compound_var=()
    function2 compound_var
    printf "x=%d, y=%d\n" compound_var.x compound_var.y
}
x="$(function1)"
[[ "$x" != 'x=19, y=20' ]] && log_error "expected 'x=19, y=20', got '${x}'"
typeset +n bar
unset foo bar
[[ $(function a
{
    for i in  foo bar
    do
        typeset -n v=$i
        print $v
    done | cat
}
foo=1 bar=2;a) == $'1\n2' ]] 2> /dev/null || log_error 'nameref in pipeline broken'
function a
{
    typeset -n v=vars.data._1
    print "${v.a} ${v.b}"
}
vars=(data=())
vars.data._1.a=a.1
vars.data._1.b=b.1
[[ $(a) == 'a.1 b.1' ]] || log_error 'nameref choosing wrong scope -- '
typeset +n bam zip foo
unset bam zip foo
typeset -A foo
foo[2]=bar
typeset -n bam=foo[2]
typeset -n zip=bam
[[ $zip == bar ]] || log_error 'nameref to another nameref to array element fails'
[[ -R zip ]] || log_error '[[ -R zip ]] should detect that zip is a reference'
[[ -R bam ]] || log_error '[[ -R bam ]] should detect that bam is a reference'
[[ -R zip ]] || log_error '[[ -v zip ]] should detect that zip is set'
[[ -v bam ]] || log_error '[[ -v bam ]] should detect that bam is set'
[[ -R 123 ]] && log_error '[[ -R 123 ]] should detect that 123 is not a reference'
[[ -v 123 ]] && log_error '[[ -v 123 ]] should detect that 123 is not set'

unset ref x
typeset -n ref
x=3
function foobar
{
    typeset xxx=3
    ref=xxx
    return 0
}
foobar 2> /dev/null && log_error 'invalid reference should cause foobar to fail'
[[ -v ref ]] && log_error '$ref should be unset'
ref=x
[[ $ref == 3 ]] || log_error "\$ref is $ref, it should be 3"
function foobar
{
        typeset fvar=()
        typeset -n ref=fvar.foo
        ref=ok
        print -r $ref
}
[[ $(foobar) ==  ok ]] 2> /dev/null  || log_error 'nameref in function not creating variable in proper scope'
function foobar
{
        nameref doc=docs
        nameref bar=doc.num
    [[ $bar == 2 ]] || log_error 'nameref scoping error'
}

docs=(num=2)
foobar

typeset +n x y
unset x y
typeset -A x
x[a]=(b=c)
typeset -n y=x[a]
[[ ${!y.@} == 'x[a].b' ]] || log_error 'reference to array element not expanded with ${!y.@}'

typeset +n v
v=()
k=a.b.c/d
command typeset -n n=v.${k//['./']/_} 2> /dev/null || log_error 'patterns with quotes not handled correctly with name reference assignment'

typeset _n sp
nameref sp=addrsp
sp[14]=( size=1 )
[[ -v sp[19] ]]  && log_error '[[ -v sp[19] ]] where sp is a nameref should not be set'

function fun2
{
    nameref var=$1
    var.foo=bar
}

function fun1
{
    compound -S container
    fun2 container
    [[ $container == *foo=bar* ]] || log_error 'name references to static compound variables in parent scope not working'
}
fun1

function fun2
{
    nameref var=$1
    var.foo=bar
}

typeset -T container_t=(
    typeset foo
)

function fun1
{
    container_t -S container
    fun2 container
    [[ $container == *foo=bar* ]] || log_error 'name references to static type variables in parent scope not working'
}
fun1

function fun2
{
    nameref var=$1
    nameref node=var.foo
    node=bar
}
function fun3
{
       fun2 container #2> /dev/null
}
compound container
fun3
[[ $container == *foo=bar* ]] || log_error 'name reference to a name reference variable in a function not working'

typeset -A x=( [a]=1 )
nameref c=x[h]
[[ -v x[h] ]] && log_error 'creating reference to non-existant associative array element causes element to get added'

unset a
function x
{
    nameref a=a
    (( $# > 0 )) && typeset -A a
    a[a b]=${1-99}  # this was cauing a syntax on the second call
}
x 7
x 2> /dev/null
[[ ${a[a b]} == 99 ]] || log_error 'nameref not handling subscript correctly'

nameref sizes=baz
typeset -A -i sizes
sizes[bar]=1
[[ ${sizes[*]} == 1 ]] || log_error 'adding -Ai attribute to name referenced variable not working'

$SHELL 2> /dev/null -c 'nameref foo=bar; typeset -A foo; (( (x=foo[a])==0 ))' || log_error 'references inside arithmetic expressions not working'
:

unset ar z
integer -a ar
nameref z=ar[0]
(( z[2]=3))
[[ ${ar[0][2]} == 3 ]] || log_error "\${ar[0][2]} is '${ar[0][2]}' but should be 3"
(( ar[0][2] == 3 )) || log_error "ar[0][2] is '${ar[0][2]}' but should be 3"

unset c x
typeset +n c x
compound c=( typeset -a x )
nameref x=c.x
x[4]=1
[[ ${ typeset -p c.x ;} == *-C* ]] && log_error 'c.x should not have -C attributes'

{ $SHELL 2> /dev/null  <<- \EOF
	typeset -T xxx_t=(
		float x=1 y=2
		typeset name=abc
	)
	xxx_t x
	nameref r=x.y
	[[ $r == 2 ]] || exit 1
	unset x
	[[ ${!r} == .deleted ]] || exit 2
EOF
} 2> /dev/null #|| print -u2 bad
exitval=$?
if [[ $(kill -l $exitval) == SEGV ]]
then
    print -u2 'name reference to unset type instance causes segmentation violation'
else
    if((exitval))
    then
        print -u2 'name reference to unset type instance not redirected to .deleted'
    fi
fi

# TODO: Remove the `unset x` when https://github.com/att/ast/issues/1267 is fixed.
# The problem is that the `unset c` below invalidates the Namval_t that `x` points to.
unset x

typeset +n nr
unset c nr
compound c
compound -A c.a
nameref nr=c.a[hello]
[[ ${!nr} == "c.a[hello]" ]] || log_error 'name reference nr to unset associative array instance does not expand ${!nr} correctly.'

typeset +n nr
compound -a c.b
nameref nr=c.b[2]
[[ ${!nr} == "c.b[2]" ]] || log_error 'name reference nr to unset indexed array instance does not expand ${!nr} correctly.'

typeset +n a b
unset a b
typeset -n a=ls[0] b=ls[1]
read line << \!
3 4
!
set -A ls -- $line
[[ $a == 3 ]] || log_error 'name reference to ls[0] when ls is not an array fails'

$SHELL  2> /dev/null <<-\EOF || log_error 'nameref to array element fails'
	set -o errexit
	function bf {
		nameref treename=$1
		nodepath="treename" ;
		nameref x="$nodepath"
		compound -A x.nodes
		nameref node=treename.nodes[4]
		node=()
		typeset +p node.elements
	}
	compound c
	bf c
EOF

function add_compound
{
    nameref arr=$1
    arr[34]+=( float val=1.1 )
}
compound -a rootcpv
nameref mycpv=rootcpv[4][8][16][32][64]
compound -a mycpv.myindexedcompoundarray
add_compound mycpv.myindexedcompoundarray
(( mycpv.myindexedcompoundarray[34].val == 1.1 )) ||  log_error 'nameref scoping error'

function add_file_to_tree
{
    nameref node=$1
    compound -A node.elements
    node.elements[/]=(filepath=foobar)
}
function main
{
    compound filetree
    add_file_to_tree filetree
}
main 2> /dev/null
[[ $? == 0 ]] || log_error 'nameref binding to calling function compound variable failed'

unset l
typeset -a -C l
printf "( typeset -a ar=( 1\n2\n3\n) b=1 )\n" | read -C l[4][6]
exp=$(print -v l)
unset l
typeset -a -C l
nameref l4=l[4]
printf "( typeset -a ar=( 1\n2\n3\n) b=1 )\n" | read -C l4[6]
[[ $(print -v l) == "$exp" ]] || log_error  'nameref l4=l[4] not working'
unset l
typeset -a -C l
nameref l46=l[4][6]
printf "( typeset -a ar=( 1\n2\n3\n) b=1 )\n" | read -C l46
[[ $(print -v l) == "$exp" ]] || log_error  'nameref l46=l[4][6] not working'

exp=$'(\n\t[4]=(\n\t\ttypeset -a ar=(\n\t\t\t1\n\t\t\t2\n\t\t)\n\t\tb=1\n\t)\n)'
unset l
typeset +n l4
typeset -a -C l
nameref l4=l[4]
printf "( typeset -a ar=( 1\n2\n) b=1 )\n" | read -C l4
[[ $(print -v l) == "$exp" ]] || log_error  'nameref l4=l[4] not working with indexed array read'

unset l
typeset +n l4
typeset -A -C l
nameref l4=l[4]
printf "( typeset -a ar=( 1\n2\n) b=1 )\n" | read -C l4
[[ $(print -v l) == "$exp" ]] || log_error  'nameref l4=l[4] not working with associative array read'

exp=$'(\n\t[9]=(\n\t\ttypeset -i fish=4\n\t)\n)'
function add_eval
{
    nameref pos=$1
    source /dev/stdin <<<"$2"
    typeset -m pos=addvar
}
function do_local_plain
{
    compound -A local_tree
    add_eval local_tree[9].fish "typeset -i addvar=4"
    [[ $(print -v local_tree) == "$exp" ]] || log_error 'do_local_plain failed'
}
function do_global_throughnameref
{
    nameref tr=global_tree
    add_eval tr[9].fish "typeset -i addvar=4"
    [[ $(print -v tr) == "$exp" ]] || log_error 'do_global_throughnameref failed'
}
function do_local_throughnameref
{
    compound -A local_tree
    nameref tr=local_tree
    add_eval tr[9].fish "typeset -i addvar=4"
    [[ $(print -v tr) == "$exp" ]] || log_error 'do_local_throughnameref failed'
}
compound -A global_tree
let 1
do_global_throughnameref
do_local_throughnameref
do_local_plain

unset ar
compound -a ar
function read_c
{
    nameref v=$1
    read -C v
}
print "( typeset -i x=36 ) " | read_c ar[5][9][2]
exp=$'(\n\t[5]=(\n\t\t[9]=(\n\t\t\t[2]=(\n\t\t\t\ttypeset -i x=36\n\t\t\t)\n\t\t)\n\t)\n)'
exp=$'(\n\ttypeset -a [5]=(\n\t\ttypeset -a [9]=(\n\t\t\t[2]=(\n\t\t\t\ttypeset -i x=36\n\t\t\t)\n\t\t)\n\t)\n)'
[[ $(print -v ar) == "$exp" ]] || log_error 'read into nameref of global array instance from within a function fails'

function read_c
{
    nameref v=$1
    read -C v
}
function main
{
    compound -a ar
    nameref nar=ar
    print "( typeset -i x=36 ) " | read_c nar[5][9][2]
    exp=$'(\n\ttypeset -a [5]=(\n\t\ttypeset -a [9]=(\n\t\t\t[2]=(\n\t\t\t\ttypeset -i x=36\n\t\t\t)\n\t\t)\n\t)\n)'
    [[ $(print -v nar) == "$exp" ]] || log_error 'read from a nameref variable from calling scope fails'
}
main

function rf2
{
    nameref val=$1
    read -C val
}
function rf
{
    nameref val=$1
    rf2 val
}
function main
{
    compound c
    typeset -A -C c.l
    nameref l4=c.l[4]
    printf "( typeset -a ar=( 1\n2\n3\n) b=1 )\n" | rf l4
    exp=$'(\n\ttypeset -C -A l=(\n\t\t[4]=(\n\t\t\ttypeset -a ar=(\n\t\t\t\t1\n\t\t\t\t2\n\t\t\t\t3\n\t\t\t)\n\t\t\tb=1\n\t\t)\n\t)\n)'
    [[ $(print -v c) == "$exp" ]] || log_error 'read -C with nameref to array element fails'
}
main

# bug reported by ek
cfg=( alarms=(type=3))
function a
{
    typeset -n y=$1
    print -- ${y.type}
}
function b
{
    a $1
}
[[  $(a cfg.alarms) == 3 ]] || log_error  "nameref scoping error in function"
[[  $(b cfg.alarms) == 3 ]] || log_error  "nameref scoping error in nested function"

function yy
{
    nameref n=$1
    n=( z=4 )
}
yy foo
unset foo
[[ $foo ]] && log_error 'unset after creating via nameref in function not working'

unset arr
typeset -a arr=( ( 1 2 3 ) ( 4 5 6 ) ( 7 8 9 ))
typeset -n ref=arr[1]
[[ $ref == 4 ]] || log_error '$ref should be 4'
[[ ${ref[@]} == '4 5 6' ]] || log_error '${ref[@]} should be "4 5 6"'
[[ $ref == "${arr[1]}" ]] || log_error '$ref shuld be ${arr[1]}'
[[ ${ref[@]} == "${arr[1][@]}" ]] || log_error '${ref[@]} should be ${arr[1][@]}'

function fun2
{
    nameref var=$1.foo
    var=$2
}
function fun1
{
    xxx=$1
    fun2 $xxx bam
}
args=(bar=yes)
fun1 args
[[ $args == *foo=bam* ]] || log_error 'nameref does not bind to correct scope'

typeset +n ref
unset ref ar
typeset -a arr=( 1 2 3 )
typeset -n ref='arr[2]'
[[ $(typeset -p ref) == *'arr[2]'* ]] || log_error 'typeset -p ref when ref is a reference to an index array element is wrong'

$SHELL  2> /dev/null -c 'function x { nameref lv=gg ; compound -A lv.c=( [4]=( x=1 )) ; } ; compound gg ; x' || log_error 'compound array assignment with nameref in a function failed'

unset one bar baz arr val vv
one=1 bar=2 baz=3
arr=(one bar baz)
nameref vv
val=$(
    for vv in "${arr[@]}"
    do
        print -n -- "$vv"
    done
)
[[ $val == 123 ]] || log_error 'optimization bug with for loops with references'

function setref
{
    nameref obj=$1
    obj=foo
}
function foo
{
    typeset -S static
    compound v
    setref v.s 2> /dev/null
    [[ ${v.s} == foo ]] || log_error  'nameref to v.s cannot failed when called from function with static variables ans v.s does not exist'
}
foo

val=$($SHELL 2> /dev/null <<- \EOF
	(sleep 2;kill -9 $$) &
	typeset -a ar=( 2 3 4 )
	function fn
	{
		nameref n=$1
		print -- "$n"
	}
	function main
	{
		integer i
		for ((i=0 ; i < ${#ar[@]} ;))
		do
			fn ar[i++]
		done
	}
	main
EOF
) || log_error  'nameref n=ar[i++] hangs'
[[ $val == $'2\n3\n4' ]] || log_error 'nameref n=ar[i++] gives wrong value'

#name references in name spaces
namespace sp1
{
    compound -a c=( [4][16]=( bool -a b=( [4][3]=true [4][5]=false ) ) )
}
nameref n=.sp1.c[4][16]
[[ ${n.b[4][@]} == "${.sp1.c[4][16].b[4][@]}" ]] || log_error 'name references to variables in name spaces not working'
