!******************************************************************************!
! Tests visibility filter on display of LayerT5 points
!
! @bug      NXM-3862 Need visibility filtering on which LayerT5 points Are displayed
!           NXM-3873 Retrieval of points matching visibility filter
!           NXM-3901 Support for Jeremy-style data
!           NXM-3904 Retrieval of points matching any filter (independent of visibility filter)
!           NXM-3910 Point3D/LayerT5 support for Symbols / Icons
! @since    4.1.1
! @author:  Jeanette McCall
!******************************************************************************!
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)

  if interAct then
    call setDatDir

    ! Process the csv file
    res test_blue_file ellipseflagsmeta5000.tmp
    res csvFile        nxm.sys.test.ellipseflagsmeta5000.csv
    res grpTable       nxm.sys.test.ellipseflagsmeta5000grp.tbl
    call createBlue

    ! Create the starting visibility filters
    res t:defaults {csv={first={key="NAME",pred="STARTS",val="J"},&
                         second={key="POS.LON",pred="LT",val=80}},&
                    snake1={key="NAME",pred="ENDS",val="2"},&
                    snake2={key="NAME",pred="ENDS",val="5"}}
    call createDefaultVisibilityFilters
    call createVisibilityFilters useDefaults=true

    ! Create the independent filter
    call createIndependentFilter

    ! Set attributes for each of the snake plots
    res snake1color "YELLOW"
    res snake2color "MAGENTA"
    res snake1 "^snake1color SNAKE"
    res snake2 "^snake2color SNAKE"
    res snakerate 30
    invoke L:symbolsField nxm.sys.libg.LayerT5.SYMBOLS
    invoke ,, nxm.sys.test.t5pathgenerator.setVaryNote(true)
    res L:numDisplay 10

    ! Set the attributes for each LayerT5
    set ellipseColumnNames "D:ALT,D:LAT,D:LON,D:ETILT,D:EMIN,D:EMAJ,NAME,D:TIME,B:F1,B:F2,B:F3,B:F4,B:F5,B:F6,B:F7,B:F8"
    set ellipseLayerCSV "LT=T5,TimeOrder=TRUE,Group=t5Group,NAME=CSV,AutoselectEnable=CLICK,AutoSelectMode=PIXEL,SELECTCOLOR=PINK,X=LON,Y=LAT,Z=ALT,LABEL=NAME,TIMELF=TIME,FILTERDATAFIELDS=ALL"
    set qualsEllipseCSV {HEADERROWS=L:1,COLUMNNAMES="^ellipseColumnNames",LAYER={^ellipseLayerCSV},TAG=CSVFILE}
    set layerSNAKE1 "LT=T5,TimeOrder=False,Group=t5Group2,NAME=SNAKE1,ColorFade=TRUE,ColorFadeFactor=1,AutoselectEnable=CLICK,AutoSelectMode=PIXEL,SELECTCOLOR=BLUE,SELECTMESSAGES=TRUE,METADATAFIELDS=NAME|NOTE,FIELD=^symbolsField"
    set layerSNAKE2 "LT=T5,TimeOrder=False,Group=t5Group2,NAME=SNAKE2,ColorFade=TRUE,ColorFadeFactor=1,FadeColor=0x200000,AutoselectEnable=CLICK,AutoSelectMode=PIXEL,SELECTCOLOR=BLUE,SELECTMESSAGES=TRUE,METADATAFIELDS=NAME|NOTE,FIELD=^symbolsField"
    set layerSNAKE3 "LT=T5,TimeOrder=False,Group=t5Group2,NAME=SNAKE3,ColorFade=TRUE,ColorFadeFactor=1,AutoselectEnable=CLICK,AutoSelectMode=PIXEL,SELECTCOLOR=BLUE,SELECTMESSAGES=TRUE,FIELD=^symbolsField"

    ! Set the lists of choices
    set keyCSVChoices    "POS.LAT,POS.LON,NAME"
    set keySnakeChoices  "NAME,NOTE"
    set predCSVChoices   "EQ,NE,GT,LT,GE,LE,EQS,SEQS,STARTS,ENDS,SUBS,CONTAINS/S"
    set predSnakeChoices "ANY,EQ,NE,GT,LT,GE,LE,EQS,SEQS,STARTS,ENDS,SUBS,CONTAINS/S"

    ! Set the instructions
    set instructions "Applying a visibility filter will trigger only points passing the filter to be visible.\n\n&
                      1) Click 'Run Automated Test'.\n   &
                         - The top plot should show the Jeremy, Jeff and Jay points.\n   &
                         - The bottom plot should show every 10th point of both the yellow and magenta snakes.\n   &
                         - The bottom plot should show a blue airplane symbol as well as a green arrow icon.\n\n&
                      2) Click 'Apply Visibility Filter'.\n   &
                         - The top plot should show all the points with names that start with J.\n   &
                         - The bottom plot should continue to show every 10th point of both the yellow and magenta snakes.\n   &
                         - The bottom plot should continue to show a blue airplane symbol as well as a green arrow icons.\n\n&
                      3) Adjust the Filters and try applying them.\n\n&
                      NOTE: The green snake in the bottom plot is for testing the new LayerT5 optional switches &
                      /NOELLIPSE and /TIME to support Jeremy-style data."

    pipe on
      panel/msgid=MAIN/setup/controls=gc
      gcontrol label macro "Test MACRO"
      gcontrol menu  main "Exit with Status =" "PASS,FAIL" TABLE={ItemColors={PASS="GREEN",FAIL="RED"}} /tleft
      gcontrol text  idtext "Instructions" instructions 24 16 /title

      nxm.sys.test.t5pathgenerator/TRAJECTORY/NAME="SNAKE1"/LAT_START=45/LON_START=100/LAT_DEST=50/LON_DEST=106/LON_SHIFT=0.1/LAT_SHIFT=0.02/DIRECTCOLOR=^snake1color/NOFLAGS/RT/NOTEMODETYPE=QA/RATE=^snakeRate OUT=_snake1{SIZE=10000} ELEM=Inf /bg
      nxm.sys.test.t5pathgenerator/FLIGHT/NAME="SNAKE2"/LAT_START=50/LON_START=150/LAT_DEST=20/LON_DEST=200/LON_SHIFT=0/LAT_SHIFT=0/DIRECTCOLOR=^snake2color/NOFLAGS/RT/NOTEMODETYPE=VA/RATE=^snakeRate OUT=_snake2{SIZE=10000} ELEM=Inf /bg
      nxm.sys.test.t5pathgenerator/TRAJECTORY/NAME="SNAKE3"/LAT_START=55/LON_START=50/LAT_DEST=30/LON_DEST=80/LON_SHIFT=0.1/LAT_SHIFT=0.02/NOELLIPSE/NOFLAGS/TIME/RT/NOTEMODETYPE=QA/RATE=^snakeRate OUT=_snake3{SIZE=10000} ELEM=Inf /bg

      plot/id=timepts ^{datDir}.world.prm|{LT="Drawable",name=t5Group,listlayers=true}|nxm.sys.test.ellipseflagstime5000.csv^{qualsEllipseCSV} &
                    readoutExt=+SHOW|-FEATURES|+POINTS readout=+OnAxis

      plot/id=timepipe ^{datDir}.world.prm|{LT="Drawable",name=t5Group2,listlayers=true}|_snake1{LAYER={^layerSnake1},TAG=SNAKE1}|&
                              _snake2{LAYER={^layerSnake2},TAG=SNAKE2}|_snake3{LAYER={^layerSnake3},TAG=SNAKE3} &
                              readoutExt=+SHOW|-FEATURES|+POINTS readout=+OnAxis view=latloncontinuous

      gcontrol label macro "Global Controls"
      gcontrol/nc=1 button AUTOTEST "" "Run Automated Test"
      gcontrol/nc=1 button VISIBILITYFILTER "" "Apply Visibility Filter"
      gcontrol/nc=1 button GETFILTEREDPOINTS "" "Get Filtered Points"
      gcontrol/nc=1 button NULLFILTER "" "Null Visibility Filter"
      gcontrol label macro "Top CSV Plot"
       gcontrol/nomsg  choice keyCSV         "Column/Key"     "^{keyCSVChoices}"  "^{defaults.csv.first.key}"
       gcontrol        choice predicateCSV   "Predicate"      "^{predCSVChoices}" "^{defaults.csv.first.pred}"
       gcontrol/nomsg  prompt valueCSV       "DVAL or String"                     "^{defaults.csv.first.val}"
       gcontrol/edit=f LVAL   numFilteredCSV "# pass filter" P1=0 P2=0 P3=-1
       gcontrol        text   filteredPtsCSV "Filtered CSV Points" "" 24 2 /title
      gcontrol label macro "Bottom Plot ^{snake1}"
       gcontrol/nomsg  choice keySnake1         "Column/Key"     "^{keySnakeChoices}"  "^{defaults.snake1.key}"
       gcontrol        choice predicateSnake1   "Predicate"      "^{predSnakeChoices}" "^{defaults.snake1.pred}"
       gcontrol/nomsg  prompt valueSnake1       "DVAL or String"                       "^{defaults.snake1.val}"
       gcontrol/edit=f LVAL   numFilteredSnake1 "# pass filter" P1=0 P2=0 P3=-1
       gcontrol        text   filteredPtsSnake1 "First ^numDisplay Filtered ^{snake1} Points" "" 24 2 /title
      gcontrol label macro "Bottom Plot ^{snake2}"
       gcontrol/nomsg  choice keySnake2          "Column/Key"     "^{keySnakeChoices}"  "^{defaults.snake2.key}"
       gcontrol        choice predicateSnake2    "Predicate"      "^{predSnakeChoices}" "^{defaults.snake2.pred}"
       gcontrol/nomsg  prompt valueSnake2        "DVAL or String"                       "^{defaults.snake2.val}"
       gcontrol/edit=f LVAL numFilteredSnake2    "# pass filter" P1=0 P2=0 P3=-1
       gcontrol        text filteredPtsSnake2    "First ^numDisplay Filtered ^{snake2} Points" "" 24 2 /title

      message send timepts NAME=ZOOM INFO=0 DATA={XMIN=25,XMAX=125,YMIN=-20,YMAX=80}

      invoke ptsCSV    reg.timepts.layers.t5group.getLayer(CSV)
      invoke ptsSnake1 reg.timepipe.layers.t5group2.getLayer(SNAKE1)
      invoke ptsSnake2 reg.timepipe.layers.t5group2.getLayer(SNAKE2)
    pipe off

  endif
  erase test_blue_file
endmacro

!******************************************************************************!
! Main process message for this macro. Should be right after the ENDMACRO.
!******************************************************************************!
procedure processMessage m:msg
  if msg.name eqs "MAIN" then
    assert/text="^reg.main.name: User indicated tests" msg.data eqss "PASS"
    pipe stop
  elseif msg.name starts "predicate" then
    invoke valueField msg.name.replace("PREDICATE","VALUE")
    if msg.data eqs "ANY" then
      invoke gc.^{valueField}.panel.setVisible(false)
    else
      invoke gc.^{valueField}.panel.setVisible(true)
    endif
  elseif msg.name eqss "AUTOTEST"
    ! testing
    invoke ,, ptsCSV.VisibilityFilter(defaultTopMapPT)
    invoke filteredCSVPoints ptsCSV.getPointsMatchingFilter()
    invoke nfilteredCSVPoints filteredCSVPoints.size()
    assert/text="^reg.main.name: Number CSV points as expected" nfilteredCSVPoints eq 6

    invoke ,, ptsSnake1.VisibilityFilter(defaultSnake1MapPT)
    pause 4
    invoke filteredSNAKE1Points ptsSnake1.getPointsMatchingFilter()
    invoke rationSNAKE1Points ptsSnake1.getPercentMatchingFilter()
    if rationSNAKE1Points GE 9 AND rationSNAKE1Points LE 11 then
      res z:pass true
    else
      res z:pass false
    endif
    assert/text="^reg.main.name: Ratio filtered to total points as expected on piped data" pass ISTRUE

    ! Test independent Point3D.getPointsMatchingFilter
    invoke allPts ptsCSV.getPointsAsList()
    invoke ptsIndependentFilter nxm.sys.libg.Point3D.getPointsMatchingFilter(allPts,independentTopMapPT)
    invoke nIndependentCSVPoints ptsIndependentFilter.size()
    assert/text="^reg.main.name: Number CSV points as expected" nIndependentCSVPoints eq 2

    invoke ,, ptsSnake2.VisibilityFilter(defaultSnake2MapPT)

    ! TODO: ideally we would add a call to code that called getPointsMatchingFilter at
    ! pretty much the same time in different threads, to make sure I am handling
    ! a second call before the first completes correctly

    call addPoint3DSymbols snake1MapPT snake2MapPT
  elseif msg.name eqss "VISIBILITYFILTER" then
    call createVisibilityFilters
    invoke ,, ptsCSV.VisibilityFilter(topMapPT)
    invoke ,, ptsSnake1.VisibilityFilter(snake1MapPT)
    invoke ,, ptsSnake2.VisibilityFilter(snake2MapPT)
  elseif msg.name eqss "GETFILTEREDPOINTS" then
    invoke filteredCSVPoints ptsCSV.getPointsMatchingFilter()
    invoke nfilteredCSVPoints filteredCSVPoints.size()
    res gc.numFilteredCSV.value ^nfilteredCSVPoints
    res gc.filteredPtsCSV.value ^filteredCSVPoints

    invoke filteredSNAKE1Points ptsSnake1.getPointsMatchingFilter()
    invoke nfilteredSNAKE1Points filteredSNAKE1Points.size()
    if nfilteredSNAKE1Points LT numDisplay then
      res numSnake1Display nfilteredSNAKE1Points
    else
      res numSnake1Display numDisplay
    endif
    invoke firstSnake1Points filteredSNAKE1Points.sublist(0,^numSnake1Display)
    res gc.numFilteredSnake1.value ^nfilteredSNAKE1Points
    res gc.filteredPtsSNAKE1.value ^firstSnake1Points

    invoke filteredSNAKE2Points ptsSnake2.getPointsMatchingFilter()
    invoke nfilteredSNAKE2Points filteredSNAKE2Points.size()
    if nfilteredSNAKE2Points LT numDisplay then
      res numSnake2Display nfilteredSNAKE2Points
    else
      res numSnake2Display numDisplay
    endif
    invoke firstSnake2Points filteredSNAKE2Points.sublist(0,^numSnake2Display)
    res gc.numFilteredSnake2.value ^nfilteredSNAKE2Points
    res gc.filteredPtsSNAKE2.value ^firstSnake2Points
  elseif msg.name eqss "NULLFILTER" then
    invoke ,, ptsCSV.nullVisibilityFilter()
    invoke ,, ptsSnake1.nullVisibilityFilter()
    invoke ,, ptsSnake2.nullVisibilityFilter()
  else
    say "Unhandled message name: ^msg.name from fid: ^msg.fid data: ^msg.data"
  endif
return

!******************************************************************************!
! If an unexpected exception is caught, mark test as failed, unless it is expected
!******************************************************************************!
procedure processException m:emsg
res msg.name
  if expectedException REXISTS then
    assert/text="^reg.main.name: Expected ERROR starts with: ^{expectedException}" expectedException EQSS emsg.data.message
    remove expectedException
  else
    assert/text="^reg.main.name: Caught unexpected exception ^{emsg.data}" "FALSE" isTrue
    if "Trace" SUBS ENV.DEBUG then invoke ,, emsg.data.printStackTrace()
  endif
return

!******************************************************************************!
! Set the dat directory
!******************************************************************************!
procedure setDatDir
  invoke nxmMajor env.nmversion.subString(0,1)
  invoke major java.lang.Integer.valueOf(nxmMajor)
  if major GE 4 then
    invoke s:datDir           nxm.sys.test.DatLocator.getDatDirLocation()
    invoke s:datDirSlash      nxm.sys.test.DatLocator.getDatDirSlashLocation()
    invoke z:sysDatFull       nxm.sys.test.DatLocator.sysDatFull()
    invoke z:sysTestDatExists nxm.sys.test.DatLocator.sysTestDatExists()
  else
    set s:datDir           "nxm.sys.dat"
    set s:datDirSlash      "nxm/sys/dat"
    set z:sysDatFull       true
    set z:sysTestDatExists false
  endif
return

!*****************************************************************************
! create blue file for this test
!*****************************************************************************
procedure createBlue

  %CONVERT_CSV_TO_T5000 ^csvFile ^grpTable ^test_blue_file rf=ecr

return

!*****************************************************************************
! create visibility filters for this test
!*****************************************************************************
procedure createVisibilityFilters z:useDefaults[false]
  invoke topMapPT    nxm.sys.lib.PredicateType.getNewPredicateTypeMap()
  invoke snake1MapPT nxm.sys.lib.PredicateType.getNewPredicateTypeMap()
  invoke snake2MapPT nxm.sys.lib.PredicateType.getNewPredicateTypeMap()

  if useDefaults isFalse then
    invoke ,, nxm.sys.lib.PredicateType.addKeyFunction(topMapPT,^{gc.keyCSV.value},"^{gc.predicateCSV.value} ^{gc.valueCSV.value}")
    invoke ,, nxm.sys.lib.PredicateType.addKeyFunction(snake1MapPT,^{gc.keySnake1.value},"^{gc.predicateSnake1.value} ^{gc.valueSnake1.value}")
    invoke ,, nxm.sys.lib.PredicateType.addKeyFunction(snake2MapPT,^{gc.keySnake2.value},"^{gc.predicateSnake2.value} ^{gc.valueSnake2.value}")
  else
    invoke ,, nxm.sys.lib.PredicateType.addKeyFunction(topMapPT,^{defaults.csv.first.key},"^{defaults.csv.first.pred} ^{defaults.csv.first.val}")
    invoke ,, nxm.sys.lib.PredicateType.addKeyFunction(snake1MapPT,^{defaults.snake1.key},"^{defaults.snake1.pred} ^{defaults.snake1.val}")
    invoke ,, nxm.sys.lib.PredicateType.addKeyFunction(snake2MapPT,^{defaults.snake2.key},"^{defaults.snake2.pred} ^{defaults.snake2.val}")
  endif
return

!*****************************************************************************
! create default visibility filters for this test
!*****************************************************************************
procedure createDefaultVisibilityFilters
  invoke defaultTopMapPT    nxm.sys.lib.PredicateType.getNewPredicateTypeMap()
  invoke defaultSnake1MapPT nxm.sys.lib.PredicateType.getNewPredicateTypeMap()
  invoke defaultSnake2MapPT nxm.sys.lib.PredicateType.getNewPredicateTypeMap()

  invoke ,, nxm.sys.lib.PredicateType.addKeyFunction(defaultTopMapPT,^{defaults.csv.first.key},"^{defaults.csv.first.pred} ^{defaults.csv.first.val}")
  invoke ,, nxm.sys.lib.PredicateType.addKeyFunction(defaultTopMapPT,^{defaults.csv.second.key},"^{defaults.csv.second.pred} ^{defaults.csv.second.val}")
  invoke ,, nxm.sys.lib.PredicateType.addKeyFunction(defaultSnake1MapPT,^{defaults.snake1.key},"^{defaults.snake1.pred} ^{defaults.snake1.val}")
  invoke ,, nxm.sys.lib.PredicateType.addKeyFunction(defaultSnake2MapPT,^{defaults.snake2.key},"^{defaults.snake2.pred} ^{defaults.snake2.val}")
return

!*****************************************************************************
! create independent filter for use with Point3D static method
!*****************************************************************************
procedure createIndependentFilter
  invoke independentTopMapPT    nxm.sys.lib.PredicateType.getNewPredicateTypeMap()
  invoke ,, nxm.sys.lib.PredicateType.addKeyFunction(independentTopMapPT,NAME,"STARTS","JE")
  invoke ,, nxm.sys.lib.PredicateType.addKeyFunction(independentTopMapPT,POS.LAT,"GT 5")
return

!*****************************************************************************
! add Point3D symbols to symbols layer
!*****************************************************************************
procedure addPoint3DSymbols
  ! set up basic LayerT5
  new nxm.sys.libg.LayerT5(20) symbolPts
  invoke mp ptsSnake1.getMPlot()
  invoke ,, symbolPts.setMPlot(mp)
  invoke ,, symbolPts.setAutoselectEnable(CLICK)
  invoke ,, symbolPts.setAutoselectMode(PIXEL)
  invoke ,, symbolPts.setSelectColor(YELLOW)

  ! add symbol points
  res t:symTable {POS={ALT=0.0,LAT=10,LON=100},POSP={ALT=0.0,LAT=10,LON=90},SYMBOL="^{datDir}.Arrow.png"}
  invoke ,, symbolPts.setPoint(-1,symTable)
  res t:sym2Table {POS={ALT=0.0,LAT=30,LON=110},POSP={ALT=0.0,LAT=30,LON=120},COLOR=AQUA,SYMBOL="AERO"}
  invoke ,, symbolPts.setPoint(-1,sym2Table)
  invoke ,, reg.timepipe.layers.t5group2.addDrawable(symbolPts)
return