package nxm.net.test.junit.lib;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import nxm.net.lib.RTPICEPacketsCache;
import nxm.sys.lib.DataFile;
import nxm.sys.lib.ICEPacket;
import nxm.sys.lib.Midas;
import nxm.sys.lib.Time;
import nxm.sys.test.junit.NMTestCase;

/**
 * Tests specific methods in RTPICEPacketsCache
 * (overall testing is done with sourcedgTest
 * @author Jeanette McCall
 * @since NeXtMidas 3.9.0
 */
public class RTPICEPacketsCacheTest extends NMTestCase {

  /** public constructor to allow running this test case directly with JUnit.
   *  @param midas      The midas context to use when running the tests.
  */
  public RTPICEPacketsCacheTest (String methodName) {

    this(methodName, null);
  }

  /** This constructor is required by NeXtMidas Unit Testing framework.
      @param methodName The name of the method being called.
      @param midas      The midas context to use when running the tests.
  */
  public RTPICEPacketsCacheTest (String methodName, Midas midas) {
    super(methodName, midas);
  }

  /** test numIcePacketsAhead */
  public void testNumIcePacketsAhead () {
    Method methodNumIcePacketsAhead;
    try {
      methodNumIcePacketsAhead = RTPICEPacketsCache.class.getDeclaredMethod("numIcePacketsAhead", Long.TYPE, Long.TYPE);
      methodNumIcePacketsAhead.setAccessible(true);
      long numAhead = (long)methodNumIcePacketsAhead.invoke(RTPICEPacketsCache.class,0xFFFFFFFE,5);
      assertEquals("RTPICEPacketsCache.numIcePacketsAhead current:0xFFFFFFFE previous:5", -7, numAhead);
      numAhead = (long)methodNumIcePacketsAhead.invoke(RTPICEPacketsCache.class,8,0xFFFFFFF8);
      assertEquals("RTPICEPacketsCache.numIcePacketsAhead current:8 previous:0xFFFFFFF8", 16, numAhead);
      numAhead = (long)methodNumIcePacketsAhead.invoke(RTPICEPacketsCache.class,20,21);
      assertEquals("RTPICEPacketsCache.numIcePacketsAhead current:20 previous:21", -1, numAhead);
    } catch (NoSuchMethodException | SecurityException | InvocationTargetException | IllegalAccessException e) {
      throw new RuntimeException("RTPICEPacketsCacheTest numIcePacketsAhead failed", e);
    }
  }

  /** test calcYDelta for conditions where default will be used */
  public void testCalcYDelta () {
    DataFile df2k = new DataFile(getMidasContext(), "nxm.sys.test.test_outoforder.tmp");  // file has 2k frame size (reuse test file from sourcdgTest)
    ICEPacket icePacketCurrent = formICEPacket(10, 0xFFFFFFFE, df2k);
    ICEPacket icePacketPrevious = formICEPacket(12, 10, df2k);
    double yDelta = RTPICEPacketsCache.calcYDelta(icePacketCurrent,icePacketPrevious, -123.0);
    assertEquals("RTPICEPacketsCache.calcYDelta current:0xFFFFFFFE previous:10", -123.0, yDelta);
    icePacketCurrent.setCount(4L);
    icePacketPrevious.setCount(4L);
    yDelta = RTPICEPacketsCache.calcYDelta(icePacketCurrent,icePacketPrevious, -123.0);
    assertEquals("RTPICEPacketsCache.calcYDelta current:4 previous:4", -123.0, yDelta);
  }

  /** test isValidICEPacketPairForYDelta */
  public void testIsValidICEPacketPairForYDelta () {
    Method methodIsValidICEPacketPairForYDelta;
    try {
      DataFile df2k = new DataFile(getMidasContext(), "nxm.sys.test.test_outoforder.tmp");  // file has 2k frame size (reuse test file from sourcdgTest)
      // test ICE Count wrap where counts are within range
      ICEPacket icePacketCurrent = formICEPacket(10, 0xFFFFFFFE, df2k);
      ICEPacket icePacketPrevious = formICEPacket(12, 2, df2k);
      methodIsValidICEPacketPairForYDelta = RTPICEPacketsCache.class.getDeclaredMethod("isValidICEPacketPairForYDelta", ICEPacket.class, ICEPacket.class);
      methodIsValidICEPacketPairForYDelta.setAccessible(true);
      boolean valid = (boolean)methodIsValidICEPacketPairForYDelta.invoke(RTPICEPacketsCache.class,icePacketCurrent,icePacketPrevious);
      assertTrue("RTPICEPacketsCache.isValidICEPacketPairForYDelta current:0xFFFFFFFE previous:2", valid);
      // test ICE Count wrap where counts are within range, but timestamps are identical
      Time now = Time.currentTime();
      icePacketCurrent.setTC(0, 0, 0, now.getWSec(), now.getFSec());
      icePacketPrevious.setTC(0, 0, 0, now.getWSec(), now.getFSec());
      valid = (boolean)methodIsValidICEPacketPairForYDelta.invoke(RTPICEPacketsCache.class,icePacketCurrent,icePacketPrevious);
      assertFalse("RTPICEPacketsCache.isValidICEPacketPairForYDelta where ICE Packets have same Timestamp value", valid);
      // test ICE Count wrap where counts are out of range
      icePacketCurrent.setCount(8L);
      icePacketPrevious.setCount(0xFFFFFFF8L);
      valid = (boolean)methodIsValidICEPacketPairForYDelta.invoke(RTPICEPacketsCache.class,icePacketCurrent,icePacketPrevious);
      assertFalse("RTPICEPacketsCache.isValidICEPacketPairForYDelta current:8 previous:0xFFFFFFF8", valid);
      // test ICE Counts that are identical
      icePacketCurrent.setCount(10L);
      icePacketPrevious.setCount(10L);
      valid = (boolean)methodIsValidICEPacketPairForYDelta.invoke(RTPICEPacketsCache.class,icePacketCurrent,icePacketPrevious);
      assertFalse("RTPICEPacketsCache.isValidICEPacketPairForYDelta current:10 previous:10", valid);
    } catch (NoSuchMethodException | SecurityException | InvocationTargetException | IllegalAccessException e) {
      throw new RuntimeException("RTPICEPacketsCacheTest numIcePacketsAhead failed", e);
    }
  }


  /**
   * Form the ICEPacket based on information from the DataFile in the df field
   * @param icePacket ICEPacket to fill out
   * @param element   Element of DataFile df used for timestamp
   * @param count     Count for this ICE Packet
   * @param dfSource  DataFile being used as source
   */
  private ICEPacket formICEPacket (int element, long count, DataFile dfSource) {
    Time timeAtFirst = dfSource.getTimeAt(element, new Time());
    ICEPacket icePacket = new ICEPacket(dfSource.getFormat(), dfSource.getFrameSize(), 0);
    icePacket.setTC(0, 0, 0, timeAtFirst.getWSec(), timeAtFirst.getFSec());
    icePacket.setBPA(dfSource.getBPA());
    icePacket.setFormat(dfSource.getFormat());
    icePacket.setUserData(dfSource.getXDelta(), ICEPacket.XDELTA_POSITION_IN_USER_DATA);
    icePacket.setUserData(dfSource.getXStart(), ICEPacket.XSTART_POSITION_IN_USER_DATA);
    icePacket.setCount(count);
    return icePacket;
  }


}
