package nxm.net.test.junit.prim;

import static nxm.net.lib.DatagramData.ICE3_PACKET;
import static nxm.net.lib.DatagramData.MAC_PACKET;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

import nxm.net.lib.RTPHeader;
import nxm.net.lib.RTPICEPacketsCache;
import nxm.net.lib.dgnetsend;
import nxm.net.prim.sourcedg;
import nxm.sys.lib.Args;
import nxm.sys.lib.Command;
import nxm.sys.lib.Data;
import nxm.sys.lib.DataFile;
import nxm.sys.lib.ICEPacket;
import nxm.sys.lib.Message;
import nxm.sys.lib.Midas;
import nxm.sys.lib.Shell;
import nxm.sys.lib.Time;
import nxm.sys.prim.plot;
import nxm.sys.test.junit.NMTestCase;

/**
 * Tests nxm.net.prim.sourcedg
 * @author Kevin Esterheld
 * @since NeXtMidas 3.8.1
 */
public class sourcedgTest extends NMTestCase {

  /** OS name for testing conditions */
  private static String OS = System.getProperty("os.name").toLowerCase();

  private static final String XDELTA_RESTART = "xdeltarestart";
  private static final String XSTART_RESTART = "xstartrestart";

  /** SSRC ID used in tests */
  private static final int RTP_HEADER_SSRCID = 1234;
  /** CSRC0 ID used in tests */
  private static final int RTP_HEADER_CSRC0ID = 9876;

  private static final String INET_ADDR = "127.0.0.1";
  private static final int    PORT      = 9001;

  /**
   * boolean value to tell if the tests should open a plot when finished running for manual view of the test case
   * result.
   */
  private static boolean openPlotAfterTest = false;

  private static boolean isWindows () {
    return OS.indexOf("win") >= 0;
  }

  /**
   * The data file to be read in for transfer
   */
  private DataFile df;

  /**
   * The data file to be read in for transfer
   */
  private DataFile dfFrameSizeChange;

  /**
   * variable to hold the buffers to be sent out per test case.
   */
  private byte[][] buffers;

  private sourcedg sdg;

  /** public constructor to allow running this test case directly with JUnit.
   *  @param midas      The midas context to use when running the tests.
  */
  public sourcedgTest (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 sourcedgTest (String methodName, Midas midas) {
    super(methodName, midas);
    df = new DataFile(getMidasContext(), "nxm.net.test.test_outoforder.tmp");
    dfFrameSizeChange = new DataFile(getMidasContext(), "nxm.net.test.test_framesizerestart.tmp");
    Shell.runCommand(getMidas(), "path add net");
  }

  /**
   * Runs test cases that will simulate the dropping of old packets
   */
  public void testDroppingOldPackets () {
    if (Shell.isGradleBuild()) return;
    String testFileName = "testFileDrop.tmp{USEBLUETCTIMELINE=true, aux=ram}";
    readIcePacketDropOld(false, testFileName);
    executePlot(testFileName);

    readIcePacketDropOld(true, testFileName);
    executePlot(testFileName);
  }

  /**
   * Runs all tests related to the reordering of packets
   */
  public void testReadIcePacketOutOfOrder () {
    if (Shell.isGradleBuild()) return;
    String testFileName = "testfilethree.tmp{USEBLUETCTIMELINE=true, aux=ram}";

    // Testing out of order with 3 packets per frame (using and not using packet size pattern)
    // (also not dropping and then dropping packets - header, then middle, then last)
   for (int usePktSizePattern = 0; usePktSizePattern <= 1; usePktSizePattern++) {
     // where usePktSizePattern == 0 means false
     readIcePacketOutOfOrderThreePackets(21, usePktSizePattern != 0, testFileName, false);
     readIcePacketOutOfOrderThreePackets(23, usePktSizePattern != 0, testFileName, false);
     readIcePacketOutOfOrderThreePackets(6, usePktSizePattern != 0, testFileName, true); // Header
     readIcePacketOutOfOrderThreePackets(7, usePktSizePattern != 0, testFileName, true); // Middle
     readIcePacketOutOfOrderThreePackets(8, usePktSizePattern != 0, testFileName, true); // Last
    }
    executePlot(testFileName);

    // add test for drop and swap with 4 packets per frame
    // (every swapnum is swapped with the next packet and every dropnum is dropped)
    testFileName = "testfilefour.tmp{USEBLUETCTIMELINE=true, aux=ram}";
    int[] dataPerPkt = {512, 640, 256, 640};
    readIcePacketsSwapAndDrop(1,0,true, 5, 20, dataPerPkt, testFileName, 1);
    executePlot(testFileName);
    // 4 packets per frame - no swaps or drops
    readIcePacketsSwapAndDrop(1,0,true, 10000000, 1000000, dataPerPkt, testFileName, 0); // This should never manipulate the byte buffers
    executePlot(testFileName);

    //********** Tests with single packet per frame **********************
    // in order
    testFileName = "testfilesingle.tmp{USEBLUETCTIMELINE=true, aux=ram}";
    readIcePacketOutOfOrderSinglePacketPerFrame(-1, true, testFileName, false);
    readIcePacketOutOfOrderSinglePacketPerFrame(-1, false, testFileName, false);

    // Testing out of order with 1 packet per frame (using and not using packet size pattern)
    // (also not dropping and then dropping packett)
    for (int usePktSizePattern = 0; usePktSizePattern <= 1; usePktSizePattern++) {
      // where usePktSizePattern == 0 means false
      readIcePacketOutOfOrderSinglePacketPerFrame(5, usePktSizePattern != 0, testFileName, false);
      readIcePacketOutOfOrderSinglePacketPerFrame(9, usePktSizePattern != 0, testFileName, false);
      readIcePacketOutOfOrderSinglePacketPerFrame(6, usePktSizePattern != 0, testFileName, true); // Header
    }
    executePlot(testFileName);
  }


  /**
   * Will run all tests related to specific types of restarts based on certain checks
   */
  public void testReadIcePacketWithRestart () {
    if (Shell.isGradleBuild()) return;
    String testFileName = "testFileRestart.tmp{USEBLUETCTIMELINE=true, aux=ram}";
    // XDELTA change
    readIcePacketRestart(false, testFileName, XDELTA_RESTART);
    executePlot(testFileName);
    readIcePacketRestart(true, testFileName, XDELTA_RESTART);
    executePlot(testFileName);

    // XSTART change
    readIcePacketRestart(true, testFileName, XSTART_RESTART);
    executePlot(testFileName);
    readIcePacketRestart(false, testFileName, XSTART_RESTART);
    executePlot(testFileName);

    // FRAME SIZE change (and XDelta change)
    readIcePacketFrameSize(true, testFileName);
    executePlot(testFileName);
    readIcePacketFrameSize(false, testFileName);
    executePlot(testFileName);

    // YDELTA_CHANGE
    readIcePacketYDeltaRestart(true, testFileName, false);
    executePlot(testFileName);
    readIcePacketYDeltaRestart(false, testFileName, false);
    executePlot(testFileName);
    readIcePacketYDeltaRestart(true, testFileName, true);
    executePlot(testFileName);
    readIcePacketYDeltaRestart(false, testFileName, true);
    executePlot(testFileName);
  }

  /**
   * Will run all tests related to stream restarts
   */
  public void testStreamRestart () {
    if (Shell.isGradleBuild()) return;
    String testFileName = "testStreamRestart.tmp{USEBLUETCTIMELINE=true, aux=ram}";

    //Restart based on larger sequence number with lower ICE count
    readStreamRestart(true, testFileName);
    readStreamRestart(false, testFileName);
    executePlot(testFileName);

    //Restart based on change in ICE Header - timestampOutOfRange
    readStreamRestartTimecode(true, testFileName);
    readStreamRestartTimecode(false, testFileName);
    executePlot(testFileName);

    //Restart based on change in ICE Header - timestampOutOfRange
    readStreamRestartResetSequenceNumber(true, testFileName);
    readStreamRestartResetSequenceNumber(false, testFileName);
    executePlot(testFileName);

    //Restart based on change in ICE Header - XDelta change
    readStreamRestartOutOfOrderPackets(true, testFileName);
  }

  public void testWrapConditions () {
    if (Shell.isGradleBuild()) return;
    String testFileName = "testfilefour.tmp{USEBLUETCTIMELINE=true, aux=ram}";
    int[] dataPerPkt = {512, 640, 256, 640};
    int startSeqNum = 0xFFFE;
    readIcePacketsSwapAndDrop(startSeqNum,0,true, 10000000, 1000000, dataPerPkt, testFileName, 0);
    executePlot(testFileName);
  }

  /**
   * Simulates a frame related change (frame size or packets per frame) with a restarted sequence number.
   * @return The array of buffers
   */
  private byte[][] createFrameChange (int sequence, int[] pktSizePattern, int sequenceNew, int[] newPktSizePattern, int restartFrame) {
    int rtpTimestamp = 0;
    int pktInFrame = 0;
    DataFile dfSource = df;
    dfSource.xferLength = 1;
    Data fileData = dfSource.getDataBuffer(1);
    byte[][] buffers = new byte[10 * pktSizePattern.length][];
    int element = 0;
    int bufferCount = 0;
    long count = 0;
    int frameNum = 0;

    while (true) {
      int ret = dfSource.read(fileData);
      int start = 0;
      if (ret <= 0) break;
      for (int i = 0; i < pktSizePattern.length; i++) {
        ICEPacket icePacket = null;
        if (i == 0) {
          frameNum++;
          count = (count + 1) & ICEPacket.MAX_COUNT;
          icePacket = formICEPacket(element++, count, dfSource, 0);
        }
        rtpTimestamp += pktSizePattern[pktInFrame];
        byte[] bufToSend = formBufToSend(icePacket,start,sequence,rtpTimestamp, fileData, pktSizePattern[pktInFrame]);
        sequence = (sequence + 1) & 0xFFFF;
        start += pktSizePattern[pktInFrame] * df.getBPA();
        pktInFrame = (pktInFrame + 1) % pktSizePattern.length;
        int bufNum = bufferCount;
        bufferCount++;
        if (buffers.length <= bufNum) {
          byte[][] bufferTemp = new byte[buffers.length + 1][];
          System.arraycopy(buffers, 0, bufferTemp, 0, buffers.length);
          buffers = bufferTemp;
        }
        buffers[bufNum++] = bufToSend;

      }
      if (frameNum == restartFrame) {
        dfSource = dfFrameSizeChange;
        dfSource.xferLength = 1;
        pktSizePattern = newPktSizePattern;
        sequence = sequenceNew;
        count = 0;
        element = 0;
      }
    }
    return buffers;
  }

  /**
   * This will create an array of buffers base on the type of restart requested.
   * @param restartType The type of restart to be tested {@link sourcedgTest#XDELTA_RESTART} or
   *          {@link sourcedgTest#XSTART_RESTART}
   * @return The array of buffers.
   */
  private byte[][] createRestartData (String restartType) {
    int sequence = 100;
    int rtpTimestamp = 0;
    int pktInFrame = 0;
    int[] pktSizePattern = {512, 896, 640}; // file has 2k frame size

    df.xferLength = 1;
    Data fileData = df.getDataBuffer(1);
    byte[][] buffers = new byte[10 * pktSizePattern.length][];
    int element = 0;
    int bufferCount = 0;
    long count = 0;
    int frameNum = 0;
    double valueToChange = -1;
    int positionToChange = -1;
    if (restartType.equalsIgnoreCase(XDELTA_RESTART)) {
      valueToChange = df.getXDelta();
      positionToChange = ICEPacket.XDELTA_POSITION_IN_USER_DATA;
    }
    else {
      valueToChange = df.getXStart();
      positionToChange = ICEPacket.XSTART_POSITION_IN_USER_DATA;
    }

    while (true) {
      int ret = df.read(fileData);
      int start = 0;
      if (ret <= 0) break;
      for (int i = 0; i < pktSizePattern.length; i++) {
        ICEPacket icePacket = null;
        if (i == 0) {
          frameNum++;
          count = (count + 1) & ICEPacket.MAX_COUNT;
          icePacket = formICEPacket(element++, count, df, 0);
          // Change the value at this frame
          if (frameNum == 4) {
            valueToChange += 2;
          }
          icePacket.setUserData(valueToChange, positionToChange);

        }
        rtpTimestamp += pktSizePattern[pktInFrame];
        byte[] bufToSend = formBufToSend(icePacket,start,sequence,rtpTimestamp, fileData, pktSizePattern[pktInFrame]);
        sequence = (sequence + 1) & 0xFFFF;
        start += pktSizePattern[pktInFrame] * df.getBPA();
        pktInFrame = (pktInFrame + 1) % pktSizePattern.length;
        buffers[bufferCount++] = bufToSend;
      }
    }
    return buffers;
  }

  /**
   * This will create an array of buffers. Each buffer is a packet and size.length packets will make one frame.
   * @param startSeq       initial sequence number
   * @param startIceCount  initial ICE Count (normally 0)
   * @param pktSizePattern amount of data (in elements) for each packet in a frame
   * @return The array of buffers
   */
  private byte[][] createTestDataPackets (int startSeq,int startIceCount, int[] pktSizePattern) {
    int sequence = startSeq;
    int rtpTimestamp = 0;
    int pktInFrame = 0;

    df.xferLength = 1;
    Data fileData = df.getDataBuffer(1);
    byte[][] buffers = new byte[10 * pktSizePattern.length][];
    int element = 0;
    int bufferCount = 0;
    int iceCount = startIceCount;

    while (true) {
      int ret = df.read(fileData);
      int start = 0;
      if (ret <= 0) break;
      for (int i = 0; i < pktSizePattern.length; i++) {
        ICEPacket icePacket = null;
        if (i == 0) {
          iceCount = (iceCount + 1) & 0x7FFFFFFF;
          icePacket = formICEPacket(element++, iceCount, df, 0);
        }
        rtpTimestamp += pktSizePattern[pktInFrame];
        byte[] bufToSend = formBufToSend(icePacket,start,sequence,rtpTimestamp, fileData, pktSizePattern[pktInFrame]);
        sequence = (sequence + 1) & 0xFFFF;
        start += pktSizePattern[pktInFrame] * df.getBPA();
        pktInFrame = (pktInFrame + 1) % pktSizePattern.length;
        buffers[bufferCount++] = bufToSend;
      }
    }
    return buffers;
  }

  /**
   * Create test data to be used to simulate a stream change, or a timecode restart based on the boolean sent in
   * @param isTimeCodeTest is this a timecode change test or not
   * @return The array of buffers
   */
  private byte[][] createTestDataStreamRestart (boolean isTimeCodeTest) {
    int sequence = 100;
    int sequenceStreamChange = 900;

    int rtpTimestamp = 0;
    int pktInFrame = 0;
    int[] pktSizePattern = {512, 896, 640}; // file has 2k frame size
    int[] newPktSizePattern = {512, 512}; // file has 1k frame size
    DataFile dfSource = df;
    dfSource.xferLength = 1;
    Data fileData = dfSource.getDataBuffer(1);
    byte[][] buffers = new byte[10 * pktSizePattern.length][];
    int element = 0;
    int bufferCount = 0;
    long count = 0;
    int frameNum = 0;

    while (true) {
      int ret = dfSource.read(fileData);
      int start = 0;
      if (ret <= 0) break;
      for (int i = 0; i < pktSizePattern.length; i++) {
        ICEPacket icePacket = null;
        if (i == 0) {
          frameNum++;

          if (i == 0) {
            Time timeAtFirst = dfSource.getTimeAt(element++, new Time());
            double wsec = timeAtFirst.getWSec();
            double fsec = timeAtFirst.getFSec();
            if (isTimeCodeTest && frameNum >= 4) {
              // Simulates a timecode restart as time has gone backwards while sequence is moving forward
              wsec -= 1000;
            }
            count = (count + 1) & ICEPacket.MAX_COUNT;
            icePacket = formICEPacket(element++, count, dfSource, 0);
            icePacket.setTC(0, 0, 0, wsec,fsec);
          }
        }
        rtpTimestamp += pktSizePattern[pktInFrame];
        byte[] bufToSend = formBufToSend(icePacket,start,sequence,rtpTimestamp, fileData, pktSizePattern[pktInFrame]);
        sequence = (sequence + 1) & 0xFFFF;
        start += pktSizePattern[pktInFrame] * df.getBPA();
        pktInFrame = (pktInFrame + 1) % pktSizePattern.length;
        int bufNum = bufferCount;
        bufferCount++;
        if (buffers.length <= bufNum) {
          byte[][] bufferTemp = new byte[buffers.length + 1][];
          System.arraycopy(buffers, 0, bufferTemp, 0, buffers.length);
          buffers = bufferTemp;
        }
        buffers[bufNum++] = bufToSend;

      }

      if (!isTimeCodeTest && frameNum == 4) {
        count = 0;
        sequence = sequenceStreamChange;
        dfSource = dfFrameSizeChange;
        dfSource.xferLength = 1;
        pktSizePattern = newPktSizePattern;
        element = 0;
      }
    }
    return buffers;
  }

  /**
   * This will create test data that will do a sequence number restart. When the sequence number is reset, it will show
   * a timecode issue as well. The time code will be in the future but it will be a packet that should be in the past
   * @return The array of buffers
   */
  private byte[][] createTestDataStreamRestartSeq () {
    int sequence = 100;
    int sequenceStreamChange = 1;

    int rtpTimestamp = 0;
    int pktInFrame = 0;
    int[] pktSizePattern = {512, 896, 640};
    df.xferLength = 1;
    Data fileData = df.getDataBuffer(1);
    byte[][] buffers = new byte[10 * pktSizePattern.length][];
    int element = 0;
    int bufferCount = 0;
    int count = 0;
    int frameNum = 0;

    while (true) {
      int ret = df.read(fileData);
      int start = 0;
      if (ret <= 0) break;
      for (int i = 0; i < pktSizePattern.length; i++) {
        ICEPacket icePacket = null;
        if (i == 0) {
          frameNum++;
          count = (count + 1) & 0xFFFF;

          icePacket = formICEPacket(element++, count, df, 0);
        }
        rtpTimestamp += pktSizePattern[pktInFrame];
        byte[] bufToSend = formBufToSend(icePacket,start,sequence,rtpTimestamp, fileData, pktSizePattern[pktInFrame]);
        sequence = (sequence + 1) & 0xFFFF;
        start += pktSizePattern[pktInFrame] * df.getBPA();
        pktInFrame = (pktInFrame + 1) % pktSizePattern.length;
        int bufNum = bufferCount;
        bufferCount++;
        if (buffers.length <= bufNum) {
          byte[][] bufferTemp = new byte[buffers.length + 1][];
          System.arraycopy(buffers, 0, bufferTemp, 0, buffers.length);
          buffers = bufferTemp;
        }
        buffers[bufNum++] = bufToSend;

      }

      if (frameNum == 4) {
        // Simulates a timecode restart by having sequence number in past and time in future
        sequence = sequenceStreamChange;
      }
    }
    return buffers;
  }

  /**
   * This will create an array of buffers. Each buffer is a packet and 3 packets will make one frame.
   * @return The array of buffers
   */
  private byte[][] createTestDataThreePacketsPerFrame (int sequence, int iceCount) {
    int rtpTimestamp = 0;
    int pktInFrame = 0;
    int[] pktSizePattern = {512, 896, 640}; // 2k frame size

    df.xferLength = 1;
    Data fileData = df.getDataBuffer(1);
    byte[][] buffers = new byte[10 * pktSizePattern.length][];
    int element = 0;
    int bufferCount = 0;

    while (true) {
      int ret = df.read(fileData);
      int start = 0;
      if (ret <= 0) break;
      for (int i = 0; i < pktSizePattern.length; i++) {
        ICEPacket icePacket = null;
        if (i == 0) {
          iceCount = (iceCount + 1) & 0x7FFFFFFF;
          icePacket = formICEPacket(element++, iceCount, df, 0);
        }
        rtpTimestamp += pktSizePattern[pktInFrame];
        byte[] bufToSend = formBufToSend(icePacket,start,sequence,rtpTimestamp, fileData, pktSizePattern[pktInFrame]);
        sequence = (sequence + 1) & 0xFFFF;
        start += pktSizePattern[pktInFrame] * df.getBPA();
        pktInFrame = (pktInFrame + 1) % pktSizePattern.length;
        buffers[bufferCount++] = bufToSend;
      }
    }
    return buffers;
  }

  /**
   * This will create test data that will simulate a change in ydelta to kick off restart..
   * @return array of bufferes containing data to simulate a change in ydelta
   */
  private byte[][] createTestDataYdelta (boolean swapFirstAndThird) {
    int sequence = 100;
    int rtpTimestamp = 0;
    int pktInFrame = 0;
    int[] pktSizePattern = {512, 512}; // 1k frame size
    DataFile dfSource = dfFrameSizeChange;
    dfSource.xferLength = 1;
    Data fileData = dfSource.getDataBuffer(1);
    byte[][] buffers = new byte[10 * pktSizePattern.length][];
    int element = 0;
    int bufferCount = 0;
    long count = 0;
    double yDeltaTimeDif = 0;
    int factor = 1;
    while (true) {

      if (count >= 4) {
        yDeltaTimeDif = 0.0005 * factor++; // frame 4, lets kick off restart with xdelta diff
      }
      int ret = dfSource.read(fileData);
      int start = 0;
      if (ret <= 0) break;
      for (int i = 0; i < pktSizePattern.length; i++) {
        ICEPacket icePacket = null;
        // Set up ICE Header
        if (i == 0) {
          count = (count + 1) & ICEPacket.MAX_COUNT;
          icePacket = formICEPacket(element++, count, dfSource, yDeltaTimeDif);
        }
        rtpTimestamp += pktSizePattern[pktInFrame];
        byte[] bufToSend = formBufToSend(icePacket,start,sequence,rtpTimestamp, fileData, pktSizePattern[pktInFrame]);
        sequence = (sequence + 1) & 0xFFFF;
        start += pktSizePattern[pktInFrame] * df.getBPA();
        pktInFrame = (pktInFrame + 1) % pktSizePattern.length;
        buffers[bufferCount++] = bufToSend;
      }
    }
    if (swapFirstAndThird) {
      byte[] bufTemp = buffers[0];
      buffers[0] = buffers[2];
      buffers[2] = bufTemp;
    }
    return buffers;
  }

  /**
   * Form buffer with all the data for this packet
   * @param icePacket        ICE Header information
   * @param start
   * @param sequence         sequence number for this packet
   * @param rtpTimestamp     RTP timestamp that is really bytes of data previously sent
   * @param fileData         Data object containing file data
   * @param numElementsInPkt Amount of data (in elements) being sent with this packet
   * @return
   */
  private byte[] formBufToSend (ICEPacket icePacket, int start, int sequence,
      int rtpTimestamp, Data fileData, int numElementsInPkt) {
    int icePacketLen = icePacket != null ? ICEPacket.headerLength : 0; // 218
    byte[] icePacketBuf = icePacket != null ? icePacket.buf : null;
    byte[] rtpBuf = new byte[dgnetsend.MAXPKT];
    dgnetsend dg = new dgnetsend();
    int len = dg.setRTPHeader(rtpBuf, icePacketBuf, icePacketLen, (byte) ICE3_PACKET, null, 0, (byte) MAC_PACKET,
        (short) RTPHeader.PAYLOADTYPE_SPECTRA, RTP_HEADER_SSRCID, sequence, rtpTimestamp, RTP_HEADER_CSRC0ID);
    byte[] bufToSend = new byte[len + numElementsInPkt * df.getBPA()]; // 2 is bpa
    System.arraycopy(rtpBuf, 0, bufToSend, 0, len);
    System.arraycopy(fileData.buf, start, bufToSend, len, numElementsInPkt * df.getBPA());
    return bufToSend;
  }

  /**
   * 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
   * @param yDeltaTimeDif A means of forcing an yDelta change (normally 0)
   */
  private ICEPacket formICEPacket (int element, long count, DataFile dfSource, double yDeltaTimeDif) {
    Time timeAtFirst = dfSource.getTimeAt(element, new Time());
    timeAtFirst.addSec(yDeltaTimeDif);
    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;
  }

  /**
   * Thread that will send the data across the network to be read in by sourcedg.
   * @param packetToDrop The packet number to be dropped.
   * @param dropPackets If the test will drop packets
   * @param timeout The timeout to wait for sourcedg to read the data before killing the process.
   * @return The thread to be run.
   */
  private Thread getDataSocketThread (final int packetToDrop, final boolean dropPackets, final double timeout) {
    return new Thread(new Runnable() {
      @Override
      public void run () {
        try {
          DatagramSocket ds = new DatagramSocket();
          for (int i = 0; i < buffers.length; i++) {
            DatagramPacket dpack = new DatagramPacket(buffers[i], buffers[i].length, InetAddress.getByName(INET_ADDR),
                PORT);
            if (dropPackets) {
              if (i != packetToDrop) {
                ds.send(dpack);
              }
            }
            else {
              ds.send(dpack);
            }
            if (isWindows()) Time.sleep(.1); // Windows needs time to catch up with processing of packets

          }
          Time.sleep(timeout); // Needed to give sourcedg time to process the data coming over the socket. I don't like
                               // this but since we do not use a thread to read from the socket and a thread to process
                               // data, we need to do something to give the parser time to read in data.
          sdg.processMessage(new Message("EXITNET", 0, null)); // this will write to file
          sdg.state = Command.FINISH; // This will stop the thread
          ds.close();
        } catch (IOException e) {
          e.printStackTrace();
        }

      }
    });
  }

  /**
   * Thread that will send the data across the network to be read in by sourcedg.
   * @param packetToDrop The packet number to be dropped.
   * @param dropPackets If the test will drop packets
   * @param timeout The timeout to wait for sourcedg to read the data before killing the process.
   * @return The thread to be run.
   */
  private Thread getDataSocketThread (final int dropMod, final double timeout) {
    return new Thread(new Runnable() {
      @Override
      public void run () {
        try {
          DatagramSocket ds = new DatagramSocket();
          for (int i = 0; i < buffers.length; i++) {
            DatagramPacket dpack = new DatagramPacket(buffers[i], buffers[i].length, InetAddress.getByName(INET_ADDR),
                PORT);
            int seq = RTPHeader.parseSequenceNumber(buffers[i], 0);
            if ((seq % dropMod) != 0) {
              // System.out.println("... Sending sequence number "+ seq +" ....");
              ds.send(dpack);
            }
            else {
              // System.out.println("Dropping sequence number "+seq);
            }
            if (isWindows()) Time.sleep(.1); // Windows needs time to catch up with processing of packets

          }
          Time.sleep(timeout); // Needed to give sourcedg time to process the data coming over the socket. I don't like
                               // this but since we do not use a thread to read from the socket and a thread to process
                               // data, we need to do something to give the parser time to read in data.
          sdg.processMessage(new Message("EXITNET", 0, null)); // this will write to file
          sdg.state = Command.FINISH; // This will stop the thread
          ds.close();
        } catch (IOException e) {
          e.printStackTrace();
        }

      }
    });
  }

  /**
   * This returns a generic thread that will not do any reordering or dropping of packets
   * @param timeout The timeout to wait for sourcedg to read the data before killing the process.
   * @return The thread to be run.
   */
  private Thread getGenericThread (final double timeout) {
    return new Thread(new Runnable() {
      @Override
      public void run () {
        try {
          DatagramSocket ds = new DatagramSocket();
          for (int i = 0; i < buffers.length; i++) {
            DatagramPacket dpack = new DatagramPacket(buffers[i], buffers[i].length, InetAddress.getByName(INET_ADDR),
                PORT);
//            int seq = RTPHeader.parseSequenceNumber(buffers[i], 0);
            // System.out.println("Sending sequence number "+seq);
            ds.send(dpack);
            Time.sleep(.1); // Windows needs time to catch up with processing of packets

          }
          Time.sleep(timeout); // Needed to give sourcedg time to process the data coming over the socket. I don't like
                               // this but since we do not use a thread to read from the socket and a thread to process
                               // data, we need to do something to give the parser time to read in data.
          sdg.processMessage(new Message("EXITNET", 0, null)); // this will write to file
          sdg.state = Command.FINISH; // This will stop the thread
          ds.close();
        } catch (IOException e) {
          e.printStackTrace();
        }

      }
    });
  }

  /**
   * Creates a thread to drop the ice packets of the first 3 packets.
   * @param timeout length of time to wait before killing thread.
   * @return The new runnable
   */
  private Thread getPacketDropOldThread (final double timeout) {
    return new Thread(new Runnable() {
      @Override
      public void run () {
        try {
          DatagramSocket ds = new DatagramSocket();
          for (int i = 0; i < buffers.length; i++) {
            DatagramPacket dpack = new DatagramPacket(buffers[i], buffers[i].length, InetAddress.getByName(INET_ADDR),
                PORT);

            if (i <= 6 && i % 3 == 0) {
              continue;
            }
            else {
              ds.send(dpack);
            }
            if (isWindows()) Time.sleep(.1); // Windows needs time to catch up with processing of packets

          }
          Time.sleep(timeout); // Needed to give sourcedg time to process the data coming over the socket. I don't like
                               // this but since we do not use a thread to read from the socket and a thread to process
                               // data, we need to do something to give the parser time to read in data.
          sdg.processMessage(new Message("EXITNET", 0, null)); // this will write to file
          sdg.state = Command.FINISH; // This will stop the thread
          ds.close();
        } catch (IOException e) {
          e.printStackTrace();
        }

      }
    });
  }

  /**
   * Thread that will run for restart testing.
   * @param timeout The timeout to wait for sourcedg to read the data before killing the process.
   * @return The thread to be run.
   */
  private Thread getRestartThread (final double timeout) {
    return new Thread(new Runnable() {
      @Override
      public void run () {
        try {
          DatagramSocket ds = new DatagramSocket();
          for (int i = 0; i < buffers.length; i++) {
            if (buffers[i] == null) break;
            DatagramPacket dpack = new DatagramPacket(buffers[i], buffers[i].length, InetAddress.getByName(INET_ADDR),
                PORT);
            ds.send(dpack);
            Time.sleep(timeout); // Need the timeout incase of restarts
          }
          Time.sleep(timeout); // Needed to give sourcedg time to process the data coming over the socket. I don't like
          // this but since we do not use a thread to read from the socket and a thread to process
          // data, we need to do something to give the parser time to read in data.
          sdg.processMessage(new Message("EXITNET", 0, null)); // this will write to file
          sdg.state = Command.FINISH; // This will stop the thread
          ds.close();
        } catch (IOException e) {
          e.printStackTrace();
        }

      }
    });
  }

  /**
   * Gets thread to use of outoforder ice headers
   * @param timeout The timeout to wait for sourcedg to read the data before killing the process.
   * @return The thread to be run.
   */
  private Thread getStreamRestartOutOfOrderIceHeadersThread (final double timeout) {
    return new Thread(new Runnable() {
      @Override
      public void run () {
        try {
          DatagramSocket ds = new DatagramSocket();
          for (int i = 0; i < buffers.length; i++) {
            DatagramPacket dpack = new DatagramPacket(buffers[i], buffers[i].length, InetAddress.getByName(INET_ADDR),
                PORT);
            int seq = RTPHeader.parseSequenceNumber(buffers[i], 0);
            if (!(seq > 1 && seq < 5)) {
              ds.send(dpack);

            }
            // System.out.println("Sending sequence number "+seq);
            Time.sleep(.1); // Windows needs time to catch up with processing of packets

          }
          Time.sleep(timeout); // Needed to give sourcedg time to process the data coming over the socket. I don't like
                               // this but since we do not use a thread to read from the socket and a thread to process
                               // data, we need to do something to give the parser time to read in data.
          sdg.processMessage(new Message("EXITNET", 0, null)); // this will write to file
          sdg.state = Command.FINISH; // This will stop the thread
          ds.close();
        } catch (IOException e) {
          e.printStackTrace();
        }

      }
    });
  }

  /**
   * Test the packets are dropped when the max packets amount is hit
   * @param usePacketSizePattern
   * @param testFile
   */
  private void readIcePacketDropOld (boolean usePacketSizePattern, String testFile) {
    df.open();
    buffers = createTestDataThreePacketsPerFrame(100,0); // starting seq # 100, ICE Count 0
    df.close();
    String switchCommand = usePacketSizePattern ? "" : "/USEPACKETSIZEPATTERN=false";
    String cmdline = getCommand(switchCommand) + testFile;
    Args args = Args.parseCommand(getMidas(), cmdline);
    sdg = (sourcedg) Shell.loadCommand(getMidas(), args);
    Shell.execCommand(getMidas(), sdg);
    Thread t = getPacketDropOldThread(0.1);
    t.start();
    while (t.isAlive()) {
      Time.sleep(.1);
    }

    DataFile dfResult = new DataFile(getMidas(), testFile);
    dfResult.open();
    assertEquals("These files should be the same bytes per element", df.getBPE(), dfResult.getBPE());
    assertEquals("These files should have 3 less the original size.", df.getSize() - 3, dfResult.getSize());
    assertEquals("The resulting file should Y Units of TIME_S.", "TIME_S", dfResult.getYUnitsName());
    dfResult.close();
  }

  /**
   * Test to read ice packet with frame size restart
   * @param usePacketSizePattern set {@link RTPICEPacketsCache#isUsePacketSizePattern()} for testing purposes.
   * @param testFile The test file for the data to be saved
   */
  private void readIcePacketFrameSize (boolean usePacketSizePattern, String testFile) {
    df.open();
    dfFrameSizeChange.open();
    int sequence = 100;
    int sequenceNew = 100;
    int[] pktSizePattern = {512, 896, 640}; // initial data per packet within a frame (2k frame size)
    int[] newPktSizePattern = {512, 512}; // data per packet within a frame after frame size change (1k frame size)
    int restartFrame = 4; // frame on which to do stream switch / frame size change
    buffers = createFrameChange(sequence, pktSizePattern, sequenceNew, newPktSizePattern, restartFrame);
    df.close();
    dfFrameSizeChange.close();
    String switchCommand = "";
    switchCommand = usePacketSizePattern ? switchCommand : switchCommand + "/USEPACKETSIZEPATTERN=false";
    String cmdline = getCommand(switchCommand) + testFile;
    Args args = Args.parseCommand(getMidas(), cmdline);
    sdg = (sourcedg) Shell.loadCommand(getMidas(), args);
    Shell.execCommand(getMidas(), sdg);
    Thread t = getRestartThread(0.1);// Can we do this after we start sourcedg? I have
                                     // to use a timeout so it can load properly before sending
                                     // data.
    t.start();

    while (t.isAlive()) {
      Time.sleep(.1);
    }

    DataFile dfResult = new DataFile(getMidas(), testFile);
    dfResult.open();
    assertEquals("These files should be the same bytes per element", dfFrameSizeChange.getBPE(), dfResult.getBPE());
    assertEquals("These files should have 3 less the original size.", dfFrameSizeChange.getSize(), dfResult.getSize());
    dfResult.close();
  }

  /**
   * Test out of order or dropped packets using 1 packet per frame
   * @param swapNum number of packet to swap. This will swap with packet 18, if it is too far the data will be dropped,
   *          if it is close it will be reordered.
   * @param usePacketSizePattern set {@link RTPICEPacketsCache#isUsePacketSizePattern()} for testing purposes.
   * @param testFile The test file for the data to be saved
   * @param dropPackets Boolean telling to drop packets or not. If so, the swapNum value will be the packet dropped.
   */
  private void readIcePacketOutOfOrderSinglePacketPerFrame (int swapNum, boolean usePacketSizePattern,
      String testFile, boolean dropPackets) {
    df.open();
    int[] pktSizePattern = {2048}; // single packet per frame
    // Need to call each time to recreate the buffers (question: have a reset buffer that puts the packets back in order?)
    buffers = createTestDataPackets(100, 0, pktSizePattern);
    df.close();
    if (!dropPackets && swapNum >= 0) {// Do not swap numbers if we are dropping packets or if swapNum==-1
      swapBuffers(0, 2);
      swapBuffers(5, 3);
      swapBuffers(swapNum, 1);
      swapBuffers(7, 8);
    }
    String cmdline = usePacketSizePattern ? getCommand("") : getCommand("/USEPACKETSIZEPATTERN=FALSE");
    cmdline += testFile;
    Args args = Args.parseCommand(getMidas(), cmdline);
    sdg = (sourcedg) Shell.loadCommand(getMidas(), args);
    Shell.execCommand(getMidas(), sdg);
    Thread t = getDataSocketThread(swapNum, dropPackets, 0.1);
    t.start();
    while (t.isAlive()) {
      Time.sleep(.1); // Wait for the thread to finish. May not need this
    }
    DataFile dfResult = new DataFile(getMidas(), testFile);
    dfResult.open();
    assertEquals("These files should be the same bytes per element", df.getBPE(), dfResult.getBPE());
    if (df.getSize() != dfResult.getSize()) {
      // Using a max difference of 2.1 because the MAXMISSINGFRAMES value is 2 meaning we could go 2 frames without
      // writing and then the process is stopped.
      assertEquals("The sizes should be less than or equal to 2 apart", df.getSize(), dfResult.getSize(), 2.1);
    }
    else {
      // This will be true if we managed to fill the old frames or it was a clean run
      assertTrue(DataFile.compare(df, dfResult, 0d, dfResult.getSize(), DataFile.EQ_DATA));

    }

  }

  /**
   * Test the reading of out of order or dropping of packets using 3 packets per frame
   * @param swapNum number of packet to swap. This will swap with packet 18, if it is too far the data will be dropped,
   *          if it is close it will be reordered.
   * @param usePacketSizePattern set {@link RTPICEPacketsCache#isUsePacketSizePattern()} for testing purposes.
   * @param testFile The test file for the data to be saved
   * @param dropPackets Boolean telling to drop packets or not. If so, the swapNum value will be the packet dropped.
   */
  private void readIcePacketOutOfOrderThreePackets (int swapNum, boolean usePacketSizePattern, String testFile,
      boolean dropPackets) {
    df.open();
    buffers = createTestDataThreePacketsPerFrame(100,0); // starting seq num 100 and ICE Count 0
    df.close();
    if (!dropPackets && swapNum >= 0) {// Do not swap numbers if we are dropping packets
      swapBuffers(0, 2);
      swapBuffers(13, 12);
      swapBuffers(swapNum, 18);
      swapBuffers(25, 27);
    }
    String cmdline = usePacketSizePattern ? getCommand("") : getCommand("/USEPACKETSIZEPATTERN=FALSE");
    cmdline += testFile;
    Args args = Args.parseCommand(getMidas(), cmdline);
    sdg = (sourcedg) Shell.loadCommand(getMidas(), args);
    Shell.execCommand(getMidas(), sdg);
    Thread t = getDataSocketThread(swapNum, dropPackets, 0.5);// Can we do this after we start sourcedg? I have
                                                              // to use a timeout so it can load properly before sending
                                                              // data.
    t.start();

    while (t.isAlive()) {
      Time.sleep(.1);
    }

    DataFile dfResult = new DataFile(getMidas(), testFile);
    dfResult.open();
    assertEquals("These files should be the same bytes per element", df.getBPE(), dfResult.getBPE());
    // This may need to be addressed. IF MAXMISSINGFRAMES=2, we are good. We fill in frames that are missing. If it is
    // higher, we do not get enough frames and will fail
    assertTrue(DataFile.compare(df, dfResult, 0d, dfResult.getSize(), DataFile.EQ_DATA));
    dfResult.close();
  }

  /**
   * @param usePacketSizePattern set {@link RTPICEPacketsCache#isUsePacketSizePattern()} for testing purposes.
   * @param testFile The test file for the data to be saved
   * @param restartType The type of restart to be tested {@link sourcedgTest#XDELTA_RESTART} or
   *          {@link sourcedgTest#XSTART_RESTART}
   */
  private void readIcePacketRestart (boolean usePacketSizePattern, String testFile,
      String restartType) {
    df.open();
    buffers = createRestartData(restartType);
    df.close();
    String switchCommand = "";
    if (restartType.equalsIgnoreCase(XDELTA_RESTART)) {
      switchCommand = "/XDELTA_RESTART_TOLERANCE=1";
    }
    else
      if (restartType.equalsIgnoreCase(XSTART_RESTART)) {
        switchCommand = "/XSTART_RESTART_TOLERANCE=1";
      }

    switchCommand = usePacketSizePattern ? switchCommand : switchCommand + "/USEPACKETSIZEPATTERN=false";

    String cmdline = getCommand(switchCommand) + testFile;
    Args args = Args.parseCommand(getMidas(), cmdline);
    sdg = (sourcedg) Shell.loadCommand(getMidas(), args);
    Shell.execCommand(getMidas(), sdg);
    Thread t = getRestartThread(0.1);// Can we do this after we start sourcedg? I have
                                     // to use a timeout so it can load properly before sending
                                     // data.
    t.start();

    while (t.isAlive()) {
      Time.sleep(.1);
    }

    DataFile dfResult = new DataFile(getMidas(), testFile);
    dfResult.open();
    assertEquals("These files should be the same bytes per element", df.getBPE(), dfResult.getBPE());
    assertEquals("These files should have 3 less the original size.", df.getSize() - 3, dfResult.getSize());
    dfResult.close();
  }

  /**
   * Test the reading of out of order or dropping of packets using 3 packets per frame
   * @param startSeq             initial sequence number
   * @param startIceCount        initial ICE Count
   * @param usePacketSizePattern set {@link RTPICEPacketsCache#isUsePacketSizePattern()} for testing purposes.
   * @param swapMod              modulo for swaps with next packet
   * @param dropMod              modulo for packet drops
   * @param dataPerPkt           amount of frame data in each packet (elements)
   * @param testFile             test file for the data to be saved
   * @param framesMissing        expected number of frames not to be in final file
   */
  private void readIcePacketsSwapAndDrop (int startSeq, int startIceCount, boolean usePacketSizePattern,
      int swapMod, int dropMod, int[] dataPerPkt, String testFile, int framesMissing) {
    df.open();
    buffers = createTestDataPackets(startSeq, startIceCount, dataPerPkt);
    df.close();
    for (int i = 0; i < buffers.length - 1; i++) {
      int seq = RTPHeader.parseSequenceNumber(buffers[i], 0);
      if (seq % swapMod == 0) {
        swapBuffers(i, i + 1);
        i++;
      }
    }
    String cmdline = getCommand("");
    cmdline += testFile;
    Args args = Args.parseCommand(getMidas(), cmdline);
    sdg = (sourcedg) Shell.loadCommand(getMidas(), args);

    Shell.execCommand(getMidas(), sdg);
    Thread t = getDataSocketThread(dropMod, 0.5);// Can we do this after we start sourcedg? I have
                                                 // to use a timeout so it can load properly before sending
                                                 // data.
    t.start();

    while (t.isAlive()) {
      Time.sleep(.1);
    }

    DataFile dfResult = new DataFile(getMidas(), testFile);
    dfResult.open();
    assertEquals("These files should be the same bytes per element", df.getBPE(), dfResult.getBPE());
    // This may need to be addressed. IF MAXMISSINGFRAMES=2, we are good. We fill in frames that are missing. If it is
    // higher, we do not get enough frames and will fail
    assertEquals("These files should have the same size less the framesMissing value of " + framesMissing,
        df.getSize() - framesMissing,
        dfResult.getSize());
    dfResult.close();
  }

  /**
   * Test the restart by using ydelta change
   * @param usePacketSizePattern set {@link RTPICEPacketsCache#isUsePacketSizePattern()} for testing purposes.
   * @param testFile The file for output.
   */
  private void readIcePacketYDeltaRestart (boolean usePacketSizePattern, String testFile, boolean swapFirstAndThird) {

    dfFrameSizeChange.open();
    buffers = createTestDataYdelta(swapFirstAndThird);
    dfFrameSizeChange.close();
    String switchCommand = "/YDELTA_RESTART_TOLERANCE=0.0004";

    switchCommand = usePacketSizePattern ? switchCommand : switchCommand + "/USEPACKETSIZEPATTERN=false";

    String cmdline = getCommand(switchCommand) + testFile;
    Args args = Args.parseCommand(getMidas(), cmdline);
    sdg = (sourcedg) Shell.loadCommand(getMidas(), args);
    Shell.execCommand(getMidas(), sdg);
    Thread t = getRestartThread(0.1);// Can we do this after we start sourcedg? I have
                                     // to use a timeout so it can load properly before sending
                                     // data.
    t.start();

    while (t.isAlive()) {
      Time.sleep(.1);
    }

    DataFile dfResult = new DataFile(getMidas(), testFile);
    dfResult.open();
    assertEquals("These files should be the same bytes per element", dfFrameSizeChange.getBPE(), dfResult.getBPE());
    assertEquals("These files should have 4 less the original size.", dfFrameSizeChange.getSize() - 4,
        dfResult.getSize());
    dfResult.close();

  }

  /**
   * Tests the ability of the parser to recognize new stream and restart.
   * @param usePacketSizePattern set {@link RTPICEPacketsCache#isUsePacketSizePattern()} for testing purposes.
   * @param testFile The file for output.
   */
  private void readStreamRestart (boolean usePacketSizePattern, String testFile) {

    df.open();
    dfFrameSizeChange.open();
    // This uses the same test as frameSizeRestart but will reset the sequence number and change the count back down to
    // 1
    buffers = createTestDataStreamRestart(false);
    dfFrameSizeChange.close();
    df.close();
    String switchCommand = usePacketSizePattern ? "" : "/USEPACKETSIZEPATTERN=false";
    // switchCommand += "/REORDERDEBUG";
    String cmdline = getCommand(switchCommand) + testFile;
    Args args = Args.parseCommand(getMidas(), cmdline);
    sdg = (sourcedg) Shell.loadCommand(getMidas(), args);
    Shell.execCommand(getMidas(), sdg);
    Thread t = getGenericThread(.1);
    t.start();
    while (t.isAlive()) {
      Time.sleep(.1);
    }
    DataFile dfResult = new DataFile(getMidas(), testFile);
    dfResult.open();
    assertEquals("These files should be the same bytes per element", dfFrameSizeChange.getBPE(), dfResult.getBPE());
    assertEquals("These files should have 3 less the original size.", dfFrameSizeChange.getSize(), dfResult.getSize());
    dfResult.close();

  }

  /**
   * Tests the ability of the parser to reorder packets
   * @param usePacketSizePattern set {@link RTPICEPacketsCache#isUsePacketSizePattern()} for testing purposes.
   * @param testFile The file for output.
   */
  private void readStreamRestartOutOfOrderPackets (boolean usePacketSizePattern, String testFile) {

    df.open();
    dfFrameSizeChange.open();
    // This uses the same test as frameSizeRestart but will reset the sequence number and change the count back down to 1
    int sequence = 100;
    int sequenceNew = 1;
    int[] pktSizePattern = {1024, 1024}; // initial data per packet within a frame (2k frame size)
    int[] newPktSizePattern = {512, 128, 256, 128}; // data per packet within a frame after frame size change
    int restartFrame = 4; // frame on which to restart
    buffers = createFrameChange(sequence, pktSizePattern, sequenceNew, newPktSizePattern, restartFrame);
    dfFrameSizeChange.close();
    df.close();
    for (int i = 0; i < buffers.length - 1; i++) {
      int seq = RTPHeader.parseSequenceNumber(buffers[i], 0);
      if (seq % 5 == 0 && seq < 100) {
        swapBuffers(i, i + 1);
        i++;
      }
    }
    // swapBuffers(11,12 );
    String switchCommand = usePacketSizePattern ? "" : "/USEPACKETSIZEPATTERN=false";
    // switchCommand += "/REORDERDEBUG";
    String cmdline = getCommand(switchCommand) + testFile;
    Args args = Args.parseCommand(getMidas(), cmdline);
    sdg = (sourcedg) Shell.loadCommand(getMidas(), args);
    Shell.execCommand(getMidas(), sdg);
    Thread t = getStreamRestartOutOfOrderIceHeadersThread(.1);
    t.start();
    while (t.isAlive()) {
      Time.sleep(.1);
    }
    DataFile dfResult = new DataFile(getMidas(), testFile);
    dfResult.open();
    assertEquals("These files should be the same bytes per element", dfFrameSizeChange.getBPE(), dfResult.getBPE());
    assertEquals("These files should have the same size", dfFrameSizeChange.getSize(), dfResult.getSize());
    dfResult.close();

  }

  /**
   * Kicks off restart based on sequence number and stream data change
   * @param usePacketSizePattern set {@link RTPICEPacketsCache#isUsePacketSizePattern()} for testing purposes.
   * @param testFile The file for output.
   */
  private void readStreamRestartResetSequenceNumber (boolean usePacketSizePattern, String testFile) {

    df.open();
    // This uses the same test as frameSizeRestart but will reset the sequence number and change the count back down to
    // 1
    buffers = createTestDataStreamRestartSeq();
    df.close();
    String switchCommand = usePacketSizePattern ? "" : "/USEPACKETSIZEPATTERN=false";
    // switchCommand += "/REORDERDEBUG";
    String cmdline = getCommand(switchCommand) + testFile;
    Args args = Args.parseCommand(getMidas(), cmdline);
    sdg = (sourcedg) Shell.loadCommand(getMidas(), args);
    Shell.execCommand(getMidas(), sdg);
    Thread t = getGenericThread(.1);
    t.start();
    while (t.isAlive()) {
      Time.sleep(.1);
    }
    DataFile dfResult = new DataFile(getMidas(), testFile);
    dfResult.open();
    assertEquals("These files should be the same bytes per element", df.getBPE(), dfResult.getBPE());
    assertEquals("These files should have 4 less the original size.", df.getSize() - 4, dfResult.getSize());
    dfResult.close();

  }

  /**
   * Runs test that will kick off restart based on timecode change
   * @param usePacketSizePattern set {@link RTPICEPacketsCache#isUsePacketSizePattern()} for testing purposes.
   * @param testFile The file for output.
   */
  private void readStreamRestartTimecode (boolean usePacketSizePattern, String testFile) {

    df.open();
    // This uses the same test as frameSizeRestart but will reset the sequence number and change the count back down to
    // 1
    buffers = createTestDataStreamRestart(true);
    df.close();
    String switchCommand = usePacketSizePattern ? "" : "/USEPACKETSIZEPATTERN=false";
    // switchCommand += "/REORDERDEBUG";
    String cmdline = getCommand(switchCommand) + testFile;
    Args args = Args.parseCommand(getMidas(), cmdline);
    sdg = (sourcedg) Shell.loadCommand(getMidas(), args);
    Shell.execCommand(getMidas(), sdg);
    Thread t = getGenericThread(.1);
    t.start();
    while (t.isAlive()) {
      Time.sleep(.1);
    }
    DataFile dfResult = new DataFile(getMidas(), testFile);
    dfResult.open();
    assertEquals("These files should be the same bytes per element", df.getBPE(), dfResult.getBPE());
    assertEquals("These files should have 3 less the original size.", df.getSize() - 3, dfResult.getSize());
    dfResult.close();

  }

  /**
   * This will plot the file that is requested.
   * @param fileName The file to be plotted
   */
  private void executePlot (String fileName) {
    if (!openPlotAfterTest) return;
    String plotCommand = "plot/READOUT=Show|OnAxis|X|T|ExtPrec|Units|DateString/DATEFORMAT=\"HMS\" " + fileName
        + " view=YX";
    Args plotArgs = Args.parseCommand(getMidas(), plotCommand);
    plot plotProcess = (plot) Shell.loadCommand(getMidas(), plotArgs);
    Shell.execCommand(getMidas(), plotProcess);
  }

  /**
   * Used to get the command generated with a switch added.
   * @param switchCommand Switch to be added to the command.
   * @return The command
   */
  private String getCommand (String switchCommand) {
    return "sourcedg/ON" + switchCommand + "/bg/FTYPE=2000/OUTOFORDER/MAXMISSINGFRAMES=2/id=SDG HOSTPORT=:"+ PORT+ " OUT=";
  }

  /**
   * Swap the buffers location for reordering sequence numbers and data.
   * @param first index of the first buffer
   * @param second index of the second buffer
   * @param buffers buffer array holding the buffers
   */
  private void swapBuffers (int first, int second) {
    byte[] buf1 = buffers[first];
    buffers[first] = buffers[second];
    buffers[second] = buf1;
  }

}