! Tests the DUAL5000 command.
!
! @author  J. Czechowski IV
! @since   NeXtMidas 3.3.0
startmacro
  res T:allRecDefs {R={NAME="R",FORMAT="SD",TYPE="UNDEFINED",UNITS="DISTANCE_M"},&
                    RR={NAME="RR",FORMAT="SD",TYPE="UNDEFINED",UNITS="SPEED_M_S"},&
                    DELT={NAME="DELT",FORMAT="SD",TYPE="UNDEFINED",UNITS="DELAY_S"},&
                    DELF={NAME="DELF",FORMAT="SD",TYPE="UNDEFINED",UNITS="DOPPLER_HZ"},&
                    SPRF={NAME="SPRF",FORMAT="SD",TYPE="UNDEFINED",UNITS="DIMENSIONLESS"},&
                    AZ={NAME="AZ",FORMAT="SD",TYPE="UNDEFINED",UNITS="ANGLE_DEG"},&
                    EL={NAME="EL",FORMAT="SD",TYPE="UNDEFINED",UNITS="ANGLE_DEG"}}

  noop sv3.prm(0:1){AUX=DAT} sv0.prm{AUX=RAM}
  noop sv2.prm{AUX=DAT}      rf2.prm{AUX=RAM}
  keyword rf2.prm{AUX=RAM} ADD RF 1e3

  ! Special case of a file compared to its self
  call testSameFile "sv0.prm{AUX=RAM}" ! <-- hits the special 1-element + 1-element branch
  call testSameFile "sv1.prm{AUX=DAT}"
  call testSameFile "sv2.prm{AUX=DAT}"
  call testSameFile "sv3.prm{AUX=DAT}"
  
  ! For the next tests we use sample files we created using X-Midas with various /RF= values
  call testSampleFile "sv1.prm{AUX=DAT}" "sv2.prm{AUX=DAT}" "nxm.ucl.test.test_dual5000_sv1_sv2.prm" 1e6
  call testSampleFile "sv2.prm{AUX=DAT}" "sv1.prm{AUX=DAT}" "nxm.ucl.test.test_dual5000_sv2_sv1.prm" 1e3
  call testSampleFile "sv0.prm{AUX=RAM}" "sv1.prm{AUX=DAT}" "nxm.ucl.test.test_dual5000_sv0_sv1.prm" 1e6
  call testSampleFile "sv1.prm{AUX=DAT}" "sv0.prm{AUX=RAM}" "nxm.ucl.test.test_dual5000_sv1_sv0.prm" 1e9

  ! Tests for RF keyword
  call testSampleFile "sv1.prm{AUX=DAT}" "rf2.prm{AUX=RAM}" "nxm.ucl.test.test_dual5000_sv1_sv2.prm" 1e6 ! <-- override RF keyword
  call testSampleFile "rf2.prm{AUX=RAM}" "sv1.prm{AUX=DAT}" "nxm.ucl.test.test_dual5000_sv2_sv1.prm" -1  ! <-- use RF keyword

  ! Tests for time alignment
  call testSampleFile "sv1.prm{AUX=DAT}" "rf2.prm{AUX=RAM}" "nxm.ucl.test.test_dual5000_sv1_sv2.prm" -1 10 1 10   ! /START=(10*xdelta+time)                   /SIZE=10
  call testSampleFile "rf2.prm{AUX=RAM}" "sv1.prm{AUX=DAT}" "nxm.ucl.test.test_dual5000_sv2_sv1.prm" -1  0 4 10   !                         /DELTA=(4*xdelta) /SIZE=10
  call testSampleFile "rf2.prm{AUX=RAM}" "sv1.prm{AUX=DAT}" "nxm.ucl.test.test_dual5000_sv2_sv1.prm" -1 10 4 10   ! /START=(10*xdelta+time) /DELTA=(4*xdelta) /SIZE=10

  ! Other tests
  call test_time_column
  ! Note: test_statevector_ltc in the SYS option tree tests DUAL5000 with /LTC and /LTCTOL - Since NeXtMidas 3.5.3 Bug 2866,2871
endmacro

! ==============================================================================
! Tests for case of a TIME column where the times don't exactly match up with
! each other and/or the delta in the file.
procedure test_time_column
  file copy/l sv1.prm{AUX=DAT} ram:file1{TIME_NAME=TIME} /PROPMASK=ALL
  file copy/l sv2.prm{AUX=DAT} ram:file2{TIME_NAME=TIME} /PROPMASK=ALL

  res TIMES1 {0="2013:01:01::20:46:39.706",1="2013:01:01::20:46:39.961",&
              2="2013:01:01::20:46:40.215",3="2013:01:01::20:46:40.469",&
              4="2013:01:01::20:46:40.723",5="2013:01:01::20:46:40.978",&
              6="2013:01:01::20:46:41.232",7="2013:01:01::20:46:41.622"}
  res TIMES2 {0="2013:01:01::20:46:39.570",1="2013:01:01::20:46:39.825",&
              2="2013:01:01::20:46:40.079",3="2013:01:01::20:46:40.333",&
              4="2013:01:01::20:46:40.587",5="2013:01:01::20:46:40.842",&
              6="2013:01:01::20:46:41.096",7="2013:01:01::20:46:41.486"}
  for i inTable TIMES1
    timex TIMES1.^{i} TIMES1.^{i}
  endfor
  for i inTable TIMES2
    timex TIMES2.^{i} TIMES2.^{i}
  endfor
  invoke diff1 TIMES1.7.diff(TIMES1.0)
  invoke diff2 TIMES2.7.diff(TIMES2.0)

  calc xdelta1 diff1 7 /
  calc xdelta2 diff2 7 /
  calc xdelta  xdelta1 xdelta2 + 2 /

  file open/w f1 ram:file1
  file open/w f2 ram:file2
    set   f1.size   8
    set   f1.xdelta 0.25428
    timex TIMES1.0  f1.time

    set   f2.size   8
    set   f2.xdelta 0.25428
    timex TIMES2.0  f2.time

    do i 0 7
      invoke f1.setData(i,"TIME",^{TIMES1.^{i}})
      invoke f2.setData(i,"TIME",^{TIMES2.^{i}})
    enddo
  file close f1
  file close f2

  dual5000/RF=1e6 ram:file1 ram:file2 ram:file3
  call checkOutput "Two files with TIME columns" ram:file3 8 TIMES1.0 xdelta ""
return

! ==============================================================================
! Tests the special case of in1=in2
procedure testSameFile FN:inFile
  res outFile   "ram:out_file"       ! use ram: rather than AUX=DAT, since 3.7.2
  res expSize   FILE(inFile).size
  res expXStart FILE(inFile).xstart
  res expXDelta FILE(inFile).xdelta

  dual5000/RF=1e6 inFile inFile outFile
  call checkZeroFile   "^inFile & ^inFile" outFile expSize expXStart expXDelta ""

  ! Make sure keys are provided in-order
  dual5000/RF=1e6 inFile inFile outFile EL,AZ,SPRF,R,RR,DELT,DELF
  call checkZeroFile   "^inFile & ^inFile" outFile expSize expXStart expXDelta "EL,AZ,SPRF,R,RR,DELT,DELF"

  ! Make sure only the requested keys are present
  dual5000/RF=1e6 inFile inFile outFile RR,AZ,EL
  call checkZeroFile   "^inFile & ^inFile" outFile expSize expXStart expXDelta "RR,AZ,EL"
return

! ==============================================================================
! Tests DUAL5000 with two different files.
!
! @param inFile1    1st input file.
! @param inFile2    2nd input file.
! @param expFile    Expected output (assuming entire file looked at).
! @param freq       Frequency to use (-1=Don't use /RF= switch).
! @param off        Offset to start at in terms of elements in output file.
!                       /START=(xdelta * off + time)
! @param mult       Multiplier in terms of elements in output file (1=n/a).
!                       /DELTA=(xdelta * mult)
! @param size       Number of output elements (-1=Don't use /SIZE= switch).
!
procedure testSampleFile FN:inFile1 FN:inFile2 FN:expFile D:freq[-1] L:off[0] L:mult[1] L:size[-1]
  res outFile  "ram:out_file"       ! use ram: rather than AUX=DAT, since 3.7.2
  res switches ""

  calc L:start off  FILE(expFile).xdelta * FILE(expFile).time +
  calc L:delta mult FILE(expFile).xdelta *

  timex start start

  if (freq  >= 0) then res switches "^{switches}/RF=^{freq}"
  if (size  >= 0) then res switches "^{switches}/SIZE=^{size}"
  if (start >  0) then res switches "^{switches}/START=^{start}"
  if (mult  >  1) then res switches "^{switches}/DELTA=^{delta}"

  dual5000^{switches} inFile1 inFile2 outFile

  file open/d act outFile
    file open/d exp expFile
      invoke expRecDefs=exp.getRecordDefs()
      table expRecDefs GetSize   expRecDefs_size
      table expRecDefs GetKeys/S expRecDefs_keys

      invoke inName1 inFile1.getRoot()
      invoke inName2 inFile2.getRoot()
      res msg "^inName1 & ^inName2 ^{switches}"

      if (size < 0) then
        calc size exp.size off -
      endif

      res T:fileTests {TEXT="^msg"}
      call checkOutputFile fileTests act size start delta expRecDefs_keys

      if (size EQ act.size) then
        do i 0 size-1
          calc L:j off i mult * +
          invoke expTbl=exp.getDataTable(j)
          invoke actTbl=act.getDataTable(i)

          foreach key inTable expTbl
            if (actTbl contains key) then
              assert/group=fileTests/test expTbl.^{key} EQT actTbl.^{key}
            else
              assert/group=fileTests/test actTbl contains key
            endif
          endfor
        enddo
      endif
    file close exp
  file close act
return

! ==============================================================================
! Checks the output file structure
! @param descr         A description to include
! @param fileName      The output file name.
! @param expectedSize  File size expected
! @param expectedStart Expected xstart+timecode
! @param expectedDelta Expected xdelta
! @param expectedKeys  Expected column names, in order, or "" implying all (but
!                      no particular order)
procedure checkOutput S:descr FN:fileName L:expectedSize D:expectedStart &
                      D:expectedDelta S:expectedKeys
  res T:fileTests {TEXT="^descr"}
  file open outFile fileName
    call checkOutputFile fileTests outFile expectedSize expectedStart expectedDelta expectedKeys
  file close outFile
  assert/group=fileTests/summary
return

! ==============================================================================
! Checks the output file to see that items are 0.0
! Checks the output file structure
! @param descr         A description to include
! @param fileName      The output file name.
! @param expectedSize  File size expected
! @param expectedStart Expected xstart+timecode
! @param expectedDelta Expected xdelta
! @param expectedKeys  Expected column names, in order, or "" implying all (but
!                      no particular order)
procedure checkZeroFile S:descr FN:fileName L:expectedSize D:expectedStart &
                        D:expectedDelta S:expectedKeys
  res T:fileTests {TEXT="^descr"}
  file open outFile fileName
    call checkOutputFile fileTests outFile expectedSize expectedStart expectedDelta expectedKeys
    foreach i inSize fileObj.size
      invoke tbl=fileObj.getDataTable(i)

      if (tbl CONTAINS "R"   ) then assert/group=fileTests/test 0.0 EQ tbl.R
      if (tbl CONTAINS "RR"  ) then assert/group=fileTests/test 0.0 EQ tbl.RR
      if (tbl CONTAINS "DELT") then assert/group=fileTests/test 0.0 EQ tbl.DELT
      if (tbl CONTAINS "DELF") then assert/group=fileTests/test 0.0 EQ tbl.DELF
      if (tbl CONTAINS "SPRF") then assert/group=fileTests/test Inf EQ tbl.SPRF
      if (tbl CONTAINS "AZ"  ) then assert/group=fileTests/test 0.0 EQ tbl.AZ
      if (tbl CONTAINS "EL"  ) then assert/group=fileTests/test 0.0 EQ tbl.EL
    endfor
  file close outFile
  assert/group=fileTests/summary
return

! ==============================================================================
! Checks the output file structure
! @param group         The group table to use for the ASSERT calls.
! @param fileObj       The file object (instanceof DataFile, already open)
! @param expectedSize  File size expected
! @param expectedStart Expected xstart+timecode
! @param expectedDelta Expected xdelta
! @param expectedKeys  Expected column names, in order, or "" implying all (but
!                      no particular order)
procedure checkOutputFile T:group O:fileObj L:expectedSize D:expectedStart &
                          D:expectedDelta S:expectedKeys
  invoke recDefs=fileObj.getRecordDefs()
  
  table recDefs GetSize   recDefs_size
  table recDefs GetKeys/S recDefs_keys

  if (expectedKeys EQS "") then
    ! If additional keys are added, we can increase this -- doing so is explicitly
    ! backwards-compatible, as per the explain file.
    res expectedKeys_size 7
    res expectedKeys      recDefs_keys   ! <-- trick to bypass order-of-keys check below
  else
    invoke expectedKeys_array=expectedKeys.split(",")
    res    expectedKeys_size expectedKeys_array.size
  endif

  assert/group=group/test ((fileObj.type GE 5000) AND (fileObj.type LT 6000))
  assert/group=group/test expectedSize  EQ  fileObj.size
  assert/group=group/test expectedStart EQ  fileObj.time
  assert/group=group/test expectedDelta EQ  fileObj.xdelta
  assert/group=group/test recDefs_size  EQ  expectedKeys_size
  assert/group=group/test recDefs_keys  EQS expectedKeys
  
  if (recDefs_keys EQS expectedKeys) then
    foreach key inList recDefs_keys
      res expectedRecDef allRecDefs.^{key}
      res actualRecDef   recDefs.^{key}
      
      assert/group=group/test expectedRecDef.NAME   EQS actualRecDef.NAME
      assert/group=group/test expectedRecDef.FORMAT EQS actualRecDef.FORMAT
      assert/group=group/test expectedRecDef.TYPE   EQS actualRecDef.TYPE
      assert/group=group/test expectedRecDef.UNITS  EQS actualRecDef.UNITS
    endfor
  endif
return
