// Path: dragonite-sdk/src/main/java/com/vecsight/dragonite/sdk/msg/types/ACKMessage.java
// public class ACKMessage implements Message {
// 
//     private static final byte VERSION = DragoniteGlobalConstants.PROTOCOL_VERSION;
// 
//     private static final MessageType TYPE = MessageType.ACK;
// 
//     public static final int FIXED_LENGTH = 8;
// 
//     private int[] sequenceList;
// 
//     private int consumedSeq;
// 
//     public ACKMessage(final int[] sequenceList, final int consumedSeq) {
//         this.sequenceList = sequenceList;
//         this.consumedSeq = consumedSeq;
//     }
// 
//     public ACKMessage(final byte[] msg) throws IncorrectMessageException {
//         final BinaryReader reader = new BinaryReader(msg);
// 
//         try {
// 
//             final byte remoteVersion = reader.getSignedByte();
//             final byte remoteType = reader.getSignedByte();
// 
//             if (remoteVersion != VERSION) {
//                 throw new IncorrectMessageException("Incorrect version (" + remoteVersion + ", should be " + VERSION + ")");
//             }
//             if (remoteType != TYPE.getValue()) {
//                 throw new IncorrectMessageException("Incorrect type (" + remoteType + ", should be " + TYPE + ")");
//             }
// 
//             consumedSeq = reader.getSignedInt();
// 
//             final int seqCount = reader.getUnsignedShort();
// 
//             sequenceList = new int[seqCount];
//             for (int i = 0; i < seqCount; i++) {
//                 sequenceList[i] = reader.getSignedInt();
//             }
// 
//         } catch (final BufferUnderflowException e) {
//             throw new IncorrectMessageException("Incorrect message length");
//         }
//     }
// 
//     @Override
//     public byte getVersion() {
//         return VERSION;
//     }
// 
//     @Override
//     public MessageType getType() {
//         return TYPE;
//     }
// 
//     @Override
//     public byte[] toBytes() {
//         final BinaryWriter writer = new BinaryWriter(getFixedLength() + sequenceList.length * Integer.BYTES);
// 
//         writer.putSignedByte(VERSION)
//                 .putSignedByte(TYPE.getValue())
//                 .putSignedInt(consumedSeq)
//                 .putUnsignedShort(sequenceList.length);
// 
//         for (final int seq : sequenceList) {
//             writer.putSignedInt(seq);
//         }
// 
//         return writer.toBytes();
//     }
// 
//     @Override
//     public int getFixedLength() {
//         return FIXED_LENGTH;
//     }
// 
//     public int[] getSequenceList() {
//         return sequenceList;
//     }
// 
//     public void setSequenceList(final int[] sequenceList) {
//         this.sequenceList = sequenceList;
//     }
// 
//     public int getConsumedSeq() {
//         return consumedSeq;
//     }
// 
//     public void setConsumedSeq(final int consumedSeq) {
//         this.consumedSeq = consumedSeq;
//     }
// }


import com.vecsight.dragonite.sdk.msg.types.ACKMessage;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/*
 * The Dragonite Project
 * -------------------------
 * See the LICENSE file in the root directory for license information.
 */


package com.vecsight.dragonite.sdk.socket;



public class ACKSender {

    private final DragoniteSocket socket;

    private final int MTU;

    private final PacketSender action;

    private final Thread sendThread;

    private final int delayMS;

    private final Set<Integer> ackList = new HashSet<>();

    private volatile int receivedSeq = 0;

    private volatile boolean receivedSeqChanged = false;

    private volatile boolean running = true;

    private volatile boolean enableLoopLock = false;

    private final Object ackLoopLock = new Object();

    protected ACKSender(final DragoniteSocket socket, final PacketSender action, final int delayMS, final int MTU) {
        this.socket = socket;
        this.MTU = MTU;
        this.action = action;
        this.delayMS = delayMS;
        sendThread = new Thread(() -> {
            try {
                while (running && socket.isAlive()) {
                    int[] ackarray = null;
                    synchronized (ackList) {
                        if (ackList.size() > 0) {
                            ackarray = toIntArray(ackList);
                            ackList.clear();
                        }
                    }
                    if (ackarray != null && ackarray.length > 0) {
                        receivedSeqChanged = false;

// Path: dragonite-sdk/src/main/java/com/vecsight/dragonite/sdk/msg/types/ACKMessage.java
// public class ACKMessage implements Message {
// 
//     private static final byte VERSION = DragoniteGlobalConstants.PROTOCOL_VERSION;
// 
//     private static final MessageType TYPE = MessageType.ACK;
// 
//     public static final int FIXED_LENGTH = 8;
// 
//     private int[] sequenceList;
// 
//     private int consumedSeq;
// 
//     public ACKMessage(final int[] sequenceList, final int consumedSeq) {
//         this.sequenceList = sequenceList;
//         this.consumedSeq = consumedSeq;
//     }
// 
//     public ACKMessage(final byte[] msg) throws IncorrectMessageException {
//         final BinaryReader reader = new BinaryReader(msg);
// 
//         try {
// 
//             final byte remoteVersion = reader.getSignedByte();
//             final byte remoteType = reader.getSignedByte();
// 
//             if (remoteVersion != VERSION) {
//                 throw new IncorrectMessageException("Incorrect version (" + remoteVersion + ", should be " + VERSION + ")");
//             }
//             if (remoteType != TYPE.getValue()) {
//                 throw new IncorrectMessageException("Incorrect type (" + remoteType + ", should be " + TYPE + ")");
//             }
// 
//             consumedSeq = reader.getSignedInt();
// 
//             final int seqCount = reader.getUnsignedShort();
// 
//             sequenceList = new int[seqCount];
//             for (int i = 0; i < seqCount; i++) {
//                 sequenceList[i] = reader.getSignedInt();
//             }
// 
//         } catch (final BufferUnderflowException e) {
//             throw new IncorrectMessageException("Incorrect message length");
//         }
//     }
// 
//     @Override
//     public byte getVersion() {
//         return VERSION;
//     }
// 
//     @Override
//     public MessageType getType() {
//         return TYPE;
//     }
// 
//     @Override
//     public byte[] toBytes() {
//         final BinaryWriter writer = new BinaryWriter(getFixedLength() + sequenceList.length * Integer.BYTES);
// 
//         writer.putSignedByte(VERSION)
//                 .putSignedByte(TYPE.getValue())
//                 .putSignedInt(consumedSeq)
//                 .putUnsignedShort(sequenceList.length);
// 
//         for (final int seq : sequenceList) {
//             writer.putSignedInt(seq);
//         }
// 
//         return writer.toBytes();
//     }
// 
//     @Override
//     public int getFixedLength() {
//         return FIXED_LENGTH;
//     }
// 
//     public int[] getSequenceList() {
//         return sequenceList;
//     }
// 
//     public void setSequenceList(final int[] sequenceList) {
//         this.sequenceList = sequenceList;
//     }
// 
//     public int getConsumedSeq() {
//         return consumedSeq;
//     }
// 
//     public void setConsumedSeq(final int consumedSeq) {
//         this.consumedSeq = consumedSeq;
//     }
// }


// Path: dragonite-sdk/src/main/java/com/vecsight/dragonite/sdk/socket/ACKSender.java
import com.vecsight.dragonite.sdk.msg.types.ACKMessage;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/*
 * The Dragonite Project
 * -------------------------
 * See the LICENSE file in the root directory for license information.
 */


package com.vecsight.dragonite.sdk.socket;



public class ACKSender {

    private final DragoniteSocket socket;

    private final int MTU;

    private final PacketSender action;

    private final Thread sendThread;

    private final int delayMS;

    private final Set<Integer> ackList = new HashSet<>();

    private volatile int receivedSeq = 0;

    private volatile boolean receivedSeqChanged = false;

    private volatile boolean running = true;

    private volatile boolean enableLoopLock = false;

    private final Object ackLoopLock = new Object();

    protected ACKSender(final DragoniteSocket socket, final PacketSender action, final int delayMS, final int MTU) {
        this.socket = socket;
        this.MTU = MTU;
        this.action = action;
        this.delayMS = delayMS;
        sendThread = new Thread(() -> {
            try {
                while (running && socket.isAlive()) {
                    int[] ackarray = null;
                    synchronized (ackList) {
                        if (ackList.size() > 0) {
                            ackarray = toIntArray(ackList);
                            ackList.clear();
                        }
                    }
                    if (ackarray != null && ackarray.length > 0) {
                        receivedSeqChanged = false;

                        if (ACKMessage.FIXED_LENGTH + ackarray.length * Integer.BYTES > MTU) {