!******************************************************************************!
! Purpose: Test the FOREACH command
! Author: <Unknown> / J. Czechowski IV / Neon Ngo
!******************************************************************************!
startmacro
  switch INTERACTIVE interAct GET DEF=TRUE  SDEF=TRUE  ! for normal interactive test(s)
  switch BASELINE    baseline GET DEF=FALSE SDEF=TRUE  ! for defects or more obscure test case(s)

  call test_indf "INDF"
  call test_indf "INFILE"  ! INFILE is an alias for INDF
  call test_inlist
  call test_insize
  call test_intable
  call test_intf
  call test_keyvector
  call test_in
  call test_nested_in
  %test_endfor_initialization

  invoke GcontrolExists reg.shell.M.dictionary.isDictionaryEntry(GCONTROL)
  if interAct AND GcontrolExists then
    %test_foreach_intable/interactive=^interAct
  endif
endmacro

!******************************************************************************!
! We should not get any exceptions
procedure processException m:emsg
  assert/text="^reg.main.name: Exception: ^emsg.data" FALSE
  ! invoke ,, emsg.data.printStackTrace()
return

!******************************************************************************!
procedure test_indf S:funcname

set testfile "test_foreach_indf"
menu/f=testfile opt=SYS
status ^testfile size=size

set count 0
foreach cmd "^funcname" ^testfile
  calc count count 1 +
endfor
erase ^testfile

assert/text="^reg.main.name: ^funcname" count GT 0 AND count EQ size
return

!******************************************************************************!
procedure test_inlist
  set L:count 0
  set ok 1
  foreach item INLIST "item1,item2,item3"
    calc L:count count 1 +
    if item NEQS "item^count" then
      set ok -1
    endif
  endfor
  assert/text="^reg.main.name: INLIST count = ^count" count EQ 3 AND ok EQ 1

  set L:count 0
  foreach item INLIST "item1,item2 spoiler,item3,item4"
    calc L:count count 1 +
  endfor
  assert/text="^reg.main.name: INLIST(2) count = ^count" count EQ 4
return

!******************************************************************************!
procedure test_insize
  result testfile "test_foreach_insize"
  result L:nels 10
  file open/d/n tag testfile
    forall #=0:^nels-1 res tag.setdata(#) #+
  file close tag

  set ok 1
  set count 0
  foreach offset INSIZE FILE(^{testfile}).size
    calc count count 1 +
    if ^{FILE(^{testfile}).data(offset)} NEQ count then
      warning "^reg.main.name: Data at ^offset is &
               ^{FILE(^{testfile}).data(offset)}, count = ^count"
      set ok -1
    endif
  endfor
  assert/text="^reg.main.name: INSIZE count = ^count" count EQ nels AND ok EQ 1
  if testfile FEXISTS erase testfile
return

!******************************************************************************!
procedure test_intable
  set T:tab {x=1,y=2,z=3}
  set L:count 0
  set ok 1
  foreach key INTABLE tab
    calc L:count count 1 +
    if tab.^key NEQ count then
      set ok -1
    endif
  endfor
  assert/text="^reg.main.name: INTABLE count = ^count" count EQ 3 AND ok EQ 1

  set L:count 0
  set ok 1
  foreach val INTABLE/V tab
    calc L:count count 1 +
    if val NEQ count then
      set ok -1
    endif
  endfor
  assert/text="^reg.main.name: INTABLE/V count = ^count" count EQ 3 AND ok EQ 1

  !
  !  Test ability to iterate over a KeyVector object
  !
  if baseline isTrue then
    warning "^reg.main.name: test for INTABLE on KeyVector is TBD"
  endif
return

!******************************************************************************!
procedure test_intf
  result testfile "temp_test_foreach_intf.txt{FLAGS=FORCEABORT}"
  result L:numLines 3
  file OPEN/N/T tag testfile
    do i 1 numLines
      file WRITE tag "line ^i"
    enddo
  file CLOSE tag

  set ok 1
  set L:count 0
  foreach line INTF testfile
    calc L:count count 1 +
    if line NEQS "line ^count" then
      set ok -1
    endif
  endfor
  assert/text="^reg.main.name: INTF count = ^count" count EQ numLines AND ok EQ 1

  erase testfile
return

!******************************************************************************!
procedure test_keyvector
  new nxm.sys.lib.KeyVector kv
  set kv.x 1
  set kv.y 2
  set kv.z 3
  ! Bug#258 is that the second time through FOREACH does not reset the loop counter
  call read_keyvector
  call read_keyvector
return

!******************************************************************************!
procedure read_keyvector
  set count 0
  foreach key INTABLE kv
    set count count+1
  endfor
  assert/text="FOREACH on KEYVECTOR" count EQ 3
return

!******************************************************************************!
procedure test_in
  !---------- Test with Data object ----------
  set data (1,2,3)
  set i 0
  foreach val IN data
    calc l:i i 1 +
    assert/text="FOREACH key IN data (i=^{i})" val eq i
  endfor

  !---------- Test with Map object ----------
  set t:mapData   {ONE=CAT,TWO=BIRD,THREE=DOG}
  set t:tempData1 {ONE=CAT,TWO=BIRD,THREE=DOG}
  set t:tempData2 {ONE=CAT,TWO=BIRD,THREE=DOG}
  new java.util.HashMap map
  foreach key inTable mapData
    invoke ,, map.put("^{key}","^{mapData.^{key}}")
  endfor

  foreach key IN map
    set    expected tempData1.^{key}
    invoke actual   map.get(^{key})
    assert/text="FOREACH key IN map (key=^{key})" "^expected" eqs "^actual"
    rem tempData1.^{key}
  endfor

  foreach val IN/V map
    set z:match false
    foreach key INTABLE tempData2
      if tempData2.^key eqs val then
        set z:match true
        ! below remove is for testing Bug 1791 (It should not cause ConcurrentModificationException)
        rem tempData2.^key
      endif
    endfor
    assert/text="FOREACH key IN map (val=^{val})" match isTrue
  endfor
  assert/text="FOREACH remove from Table" tempData2.getsize eq 0

  !---------- Test with Vector object ----------
  new java.util.Vector vect
  set values "A,B,C,D"
  foreach val inList values
    invoke ,, vect.add("^val")
  endfor

  set i 0
  foreach val IN vect
    calc l:i i 1 +
    SEDIT values expected "ELEM" i ","
    assert/text="FOREACH key IN vector (i=^i)" expected eqs val
  endfor

  !---------- Test with Java array ----------
  invoke array vect.toArray()

  set i 0
  foreach val IN array
    calc l:i i 1 +
    SEDIT values expected "ELEM" i ","
    assert/text="FOREACH key IN array (i=^i)" expected eqs val
  endfor

  set i 5
  foreach/REV val IN array
    calc l:i i 1 -
    SEDIT values expected "ELEM" i ","
    assert/text="FOREACH/REV key IN array (i=^i)" expected eqs val
  endfor

  !---------- Test with an Enumeration ----------
  invoke enum vect.elements()

  set i 0
  foreach val IN enum
    calc l:i i 1 +
    SEDIT values expected "ELEM" i ","
    assert/text="FOREACH key IN enumeration (i=^i)" expected eqs val
  endfor

return

!******************************************************************************!
procedure test_nested_in

  new java.lang.String[3] strArray1
  res strArray1.0 "aaaa"
  res strArray1.1 "bbbb"
  res strArray1.2 "cccc"

  new java.lang.String[3] strArray2
  res strArray2.0 "AAAA"
  res strArray2.1 "BBBB"
  res strArray2.2 "CCCC"

  res l:outer 0 ! keep track of how many times outer loop called
  res l:inner 0 ! keep track of how many times inner loop called

  foreach val IN/V strArray1
    calc outer outer 1 +
    foreach val2 IN strArray2
      calc inner inner 1 +
      set value ^val2
      !say "Outer count=^{outer} value = ^{val} : Inner count=^{inner} value=^{val2}"
    endfor
  endfor


  assert/text="FOREACH with nested array." inner eq 9 AND val2 eqs "CCCC"

return
