!******************************************************************************!
! Tests the writing and reading of high precision timeline keywords:
!   BLUE TC keywords and Platinum TIMETAG Keywords
!
! Also test the ability to replace a DataFile's timeline with an alternate timeline
!
! Arguments: None
!
! @bug      NXM-3010 TimeLine does not support time code (TC) Keywords
! @since    3.7.2
! @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)

  call setDatDir

  ! Number of time codes to check on the tests where time codes are checked individually
  res numTimeCodesToCheck 3

  ! File names of files used in these tests
  res pennytimecode     ram:pennytimecode.tmp
  res timelinepenny     ram:timelinepenny.tmp
  res tcpenny           ram:tcpenny.tmp
  res tcpennyconv       ram:tcpennyconv.tmp
  res timetagpenny      ram:timetagpenny.tmp
  res timetagpennyconv  ram:timetagpennyconv.tmp
  res timelinefromtcpenny       ram:timelinefromtcpenny.tmp
  res timelinefromtimetagpenny  ram:timelinefromtimetagpenny.tmp

  ! create file with timecode entry
  noop ^{datDir}.apenny.prm pennytimecode
  headermod pennytimecode TC=1970:01:01::00:00:00
  
  !***************************************************************************  
  ! Create initial file with BLUE TIMELINE Keywords
  !***************************************************************************
  ! Note: This sets timeline size via the DataFile.setTimeLineLength method.
  ! Qualifiers can be user to set fields directly or via their setter methods.
  ! The setter method setTimeLineLength is found and used (though tll is not a
  ! DataFile field)
  nxm.sys.test.timelinetester/wrap=2 IN=pennytimecode{YU=TIME,XU=FREQ} &
     OUT=timelinepenny{TLL=-1} TSTART=1970:01:01::00:00:00 TDELTA=.25 JITTER=0.125

  !***************************************************************************
  ! Save the TIMELINE values to be used as expected
  !*************************************************************************** 
  key timelinepenny get NAME=TIMELINE VALUE=origBlueTimeline
  status timelinepenny FrameSize=fs TIMECODE=timeCode


  !***************************************************************************
  ! Test writing of BLUE TC Keywords based on file qualifier
  !***************************************************************************

  ! Read in BLUE TIMELINE file and write out matching file with BLUE TC Keywords
  convert timelinepenny KWCONV tcpenny{USEBLUETCTIMELINE=true} OPTIONS={}

  ! Validate timeline Keywords in BLUE TC Keywords file
  call validateBlueTCKeywords numTimeCodesToCheck origBlueTimeline fs tcpenny &
      "TC Keywords written due to qualifier"

  !***************************************************************************
  ! Test writing of BLUE TC Keywords based on TimeLine.Format.BLUE_HIGH_PRECISION
  !***************************************************************************
  convert tcpenny KWCONV tcpennyconv options={}
  assert/text="^reg.main.name: TC Keywords written due to TimeLine.Format" tcpenny FEQ/K tcpennyconv


  !***************************************************************************
  ! Test writing of Platinum Timeline Keywords based on file qualifier
  !***************************************************************************
  ! Read in BLUE TIMELINE file and write out matching file with Platinum timeline keywords
  !   (Note: This is still a BLUE file with Platinum timeline keywords)
  convert timelinepenny KWCONV timetagpenny{USEPLATINUMTIMELINE=true} OPTIONS={}
  
  ! Validate timeline Keywords in file with Platinum timeline keywords
  call validatePlatinumTimelineKeywords numTimeCodesToCheck origBlueTimeline fs timeCode timetagpenny &
      "TIMETAG Keywords written due to qualifier"

  !***************************************************************************
  ! Test writing of Platinum Keywords based on TimeLine.Format.USEPLATINUMTIMELINE
  !***************************************************************************
  convert timetagpenny KWCONV timetagpennyconv options={}
  assert/text="^reg.main.name: TIMETAG Keywords written due to TimeLine.Format" timetagpenny FEQ/K timetagpennyconv


  !***************************************************************************
  ! Test reading of BLUE TC Keywords based on keywords in file
  !***************************************************************************
  ! Read in BLUE TC keywords file and write out matching file with BLUE TIMELINE Keyword
  convert tcpenny KWCONV timelinefromtcpenny{USEBLUETCTIMELINE=false} OPTIONS={}
  
  ! Check that TIMELINE Byte Offset / Time values match original file
  key timelinefromtcpenny get NAME=TIMELINE VALUE=blueTimelinefromTC
  call validateTimeLineOffsetTimePairs numTimeCodesToCheck origBlueTimeline blueTimelinefromTC &
     "TC Keywords read based on Keywords in file"


  !***************************************************************************
  ! Test reading of Platinum Keywords based on keywords in file
  !***************************************************************************
  ! Read in Platinum timeline keywords file and write out matching file with BLUE TIMELINE Keyword
  convert timetagpenny KWCONV timelinefromtimetagpenny{USEPLATINUMTIMELINE=false} OPTIONS={}
  
  ! Check that TIMELINE Byte Offset / Time values match original file
  key timelinefromtimetagpenny get NAME=TIMELINE VALUE=blueTimelineFromTIMETAG
  call validateTimeLineOffsetTimePairs numTimeCodesToCheck origBlueTimeline blueTimelineFromTIMETAG &
       "TIMETAG Keywords read based on Keywords in file"


  !***************************************************************************
  ! Test substitution of DataFile timeline object
  !***************************************************************************
  call testTimelineSubstitution timelinepenny
  
  !***************************************************************************
  ! Erase files
  !***************************************************************************
  erase pennytimecode timelinepenny
  erase tcpenny tcpennyconv timelinefromtcpenny
  erase timetagpenny timetagpennyconv timelinefromtimetagpenny

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
  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
  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

!******************************************************************************!
! Validate that file has BLUE TC Keywords with correct values
!******************************************************************************!
!
procedure validateBlueTCKeywords L:numValidate O:origTimeline D:fs fn:tcFN S:msg
  res n 1                        ! TC index (one based)
  res timelineByteOffsetIdx 2    ! Location of first byte offset value in timeline table
  res timelineTimeIdx       3    ! Location of time value in timeline table
  res noFailures            true ! No test failures

  while n LE numValidate
  
    ! Convert the TIMELINE offset to element offset
    res timelineByteOffset ^origTimeline.^timelineByteOffsetIdx
    calc timelineElemOffset timelineByteOffset fs /
    res oneBasedTimelineOffset timelineElemOffset+1
    
    ! Get the TIMELINE time
    res timelineTime origTimeline.^timelineTimeIdx

    ! Get BLUE TC Keyword values for this n
    key ^tcFN get TCSAMPLE_^n TCSampleOffset TC_WHOLE_^n TCWhole TC_FRAC_^n TCFrac

    ! Calculate TC Time is seconds from whole and fractional parts
    calc TC_Time TCWhole TCFrac +

    if ^TCSampleOffset NEQT ^oneBasedTimelineOffset
      res noFailures false
      warn "^reg.main.name: TCSAMPLE_^{n}:^TCSampleOffset == ^oneBasedTimelineOffset - TEST FAILED"
    endif
    
    if/delta=1e-5 ^TC_Time NEQT ^timelineTime
      res noFailures false
      warn "^reg.main.name: TC_WHOLE_^{n}+TC_FRAC_^{n}:^TC_Time == ^timelineTime - TEST FAILED"
    endif

    ! Indexes for next values
    res timelineByteOffsetIdx timelineByteOffsetIdx+2
    res timelineTimeIdx       timelineTimeIdx+2
    res n                     n+1
    
  endwhile

  assert/text="^reg.main.name: ^msg"  ^noFailures ISTRUE
 
return


!******************************************************************************!
! Validate that file has Platinum TIMETAG Keywords with correct values
!
! Note: Currently this is only designed to test Platinum files where the time
! values are in seconds
!******************************************************************************!
procedure validatePlatinumTimelineKeywords L:numValidate O:origTimeline D:fs timeCode fn:platinumFN S:msg

  key platinumFN get TIMETAG.OFFSET timetagOffset TIMETAG.TIME  timetagTime
  key platinumFN get TIMETAG.TIME.UNITS  timetagUnits  TIME_EPOCH timetagEpoch
  
  res TIME_S 1 !value of package private nxm.sys.inc.Units.TIME_S
  if timetagUnits NEQ TIME_S
    warn "^reg.main.name: validatePlatinumTimelineKeywords is only designed to test files with time in seconds"
    return
  endif

  ! Covert Epoch time to seconds since 1950
  timex ^timetagEpoch d:epochTime /infmt="ISO8601"

  res timelineByteOffsetIdx 2    ! Location of first byte offset value in timeline table
  res timelineTimeIdx       3    ! Location of time value in timeline table
  res ttIdx                 0    ! TIMETAG Keywords are 0 based indexed
  res noFailures            true ! No test failures

  while ttIdx LT numValidate
  
    ! Convert the TIMELINE offset to element offset
    res timelineByteOffset origTimeline.^timelineByteOffsetIdx
    calc timelineElemOffset timelineByteOffset fs /
    
    ! Get the TIMELINE time
    res timelineTime origTimeline.^timelineTimeIdx
    
    ! Convert TIMETAG.TIME to 1950 time
    calc convertedTimetagTime ^timetagTime.^ttIdx ^epochTime +

    if/DELTA=1e-5 ^timetagOffset.^ttIdx NEQT ^timelineElemOffset
      res noFailures false
      warn "^reg.main.name: TIMETAG.OFFSET.^{ttIdx}:^timetagOffset.^ttIdx == &
       ^timelineElemOffset - TEST FAILED"
    endif

    if/DELTA=1e-5 convertedTimetagTime NEQT origTimeline.^timelineTimeIdx
      res noFailures false
      warn "^reg.main.name: TIMETAG.TIME.^{ttIdx}:^timetagTime.^ttIdx matches &
       ^origBlueTimeline.^timelineTimeIdx - TEST FAILED"
    endif

    res timelineByteOffsetIdx timelineByteOffsetIdx+2
    res timelineTimeIdx       timelineTimeIdx+2
    res ttIdx                 ttIdx+1

  endwhile
  
  assert/text="^reg.main.name: ^msg"  ^noFailures ISTRUE
 
return

!******************************************************************************!
! Validate TIMELINE Offset / Time pairs match
!
! Note: This does not check the delta or tolerance entries
!******************************************************************************!
procedure validateTimeLineOffsetTimePairs L:numValidate O:expectedTimeline O:actualTimeline S:msg

  key platinumFN get TIMETAG.OFFSET timetagOffset TIMETAG.TIME  timetagTime
  key platinumFN get TIMETAG.TIME.UNITS  timetagUnits  TIME_EPOCH timetagEpoch
  
  res TIME_S 1 !value of package private nxm.sys.inc.Units.TIME_S
  if timetagUnits NEQ TIME_S
    warn "^reg.main.name: validatePlatinumTimelineKeywords is only designed to test files with time in seconds"
    return
  endif

  ! Covert Epoch time to seconds since 1950
  timex ^timetagEpoch d:epochTime /infmt="ISO8601"

  res timelineByteOffsetIdx 2    ! Location of first byte offset value in timeline table
  res timelineTimeIdx       3    ! Location of time value in timeline table
  res pairNum               0    ! TIMELINE Byte Offset / Time Pair # (zero based)
  res noFailures            true ! No test failures

  while pairNum LT numValidate

    ! Get the TIMELINE byte offsets
    res expectedByteOffset expectedTimeline.^timelineByteOffsetIdx
    res actualByteOffset expectedTimeline.^timelineByteOffsetIdx
    
    ! Get the TIMELINE time
    res expectedTime expectedTimeline.^timelineTimeIdx
    res actualTime actualTimeline.^timelineTimeIdx

    if ^expectedByteOffset NEQ ^actualByteOffset
      res noFailures false
      warn "^reg.main.name: TIMELINE byte offset ^{pairNum}:^actualByteOffset == &
       ^expectedByteOffset - TEST FAILED"
    endif
    
    if/DELTA=1e-5 actualTime NEQT ^expectedTime
      res noFailures false
      warn "^reg.main.name: TIMELINE TIME ^{pairNum}:^actualTime matches &
       ^expectedTime"
    endif

    res timelineByteOffsetIdx timelineByteOffsetIdx+2
    res timelineTimeIdx       timelineTimeIdx+2
    res pairNum               pairNum+1
 endwhile

 assert/text="^reg.main.name: ^msg"  ^noFailures ISTRUE
 
return

!******************************************************************************!
! Tests the ability to substitute an alternate timeline for a DataFile
!******************************************************************************!
procedure testTimelineSubstitution fn:origtimelinefile

  ! Create file with different timeline
  ! Note: due to the use of random in timelinetester this file should be distinct,
  ! but this uses a different jitter to ensure a timeline that is different from
  ! our original file 
  res origtimelinemicrosec ram:origtimelinemicrosec.tmp
  res timelinepenny2 ram:timelinepenny2.tmp ! name of file
  ! Note: using the /TLL switch is the documented way to set timeline size
  nxm.sys.test.timelinetester/wrap=2 IN=^{datDir}.apenny.prm{YU=TIME,XU=FREQ} &
     OUT=timelinepenny2 TSTART=1950:01:01::00:00:00 TDELTA=.25 JITTER=0.2 /TLL=-1

  ! Replace this new file's timeline object
  if/DELTA=1e-5 origtimelinefile FEQT/K timelinepenny2
    warn "^reg.main.name: Skipping TimeLine Substitution Test - Second file is not distinct"
  else
    ! Grab the original file's timeline object
    file open/d file1 origtimelinefile
    invoke mytimeline file1.getTimeLineHandler()
    file close file1

    ! replace the second file's timeline object with the original file's timeline object
    file open/d file2 timelinepenny2{flags="|Input|Output|"}
    invoke ,, file2.setTimeLineObject(mytimeline)
    file close file2

    ! convert the original timeline file to just microseconds for comparison
    convert origtimelinefile KWCONV origtimelinemicrosec OPTIONS={}

    ! Check that the substitution was successful
    assert/text="^reg.main.name: TimeLine substitution" origtimelinemicrosec FEQ/K timelinepenny2

  endif
  
  erase timelinepenny2
return

