#import "JitterQueueTest.h"
#import "JitterQueue.h"
#import "TestUtil.h"
#import "EncodedAudioPacket.h"
#import "Util.h"
#import "DiscardingLog.h"
#import "Queue.h"

#define testLoggedNothing(q) test([q->messageQueue count] == 0)
#define testLogged(q, s) test([q->messageQueue count] > 0 && [s isEqualToString:[q->messageQueue dequeue]])
#define testLoggedArrival(q, n) testLogged(q, ([NSString stringWithFormat:@"%d", n]))
#define testLoggedBadArrival(q, sequenceNumber, arrivalType) testLogged(q, ([NSString stringWithFormat:@"bad +%d: %d", sequenceNumber, arrivalType]))
#define testLoggedBadDequeueOfType(q, type) testLogged(q, ([NSString stringWithFormat:@"-%d", type]))
#define testLoggedDequeue(q, sequenceNumber, remainingCount) testLogged(q, ([NSString stringWithFormat:@"bad -%d: %d", sequenceNumber, remainingCount]))
#define testLoggedDiscard(q, sequenceNumber, oldReadHeadSequenceNumber, newReadHeadSequenceNumber) testLogged(q, ([NSString stringWithFormat:@"discard %d,%d,%d", sequenceNumber, oldReadHeadSequenceNumber, newReadHeadSequenceNumber]))
#define testLoggedResync(q, oldReadHeadSequenceNumber, newReadHeadSequenceNumber) testLogged(q, ([NSString stringWithFormat:@"resync %d to %d", oldReadHeadSequenceNumber,newReadHeadSequenceNumber]))

@implementation JitterQueueTest

-(void) testJitterStartsFromAnyIndex {
    JitterQueue* r1 = [JitterQueue jitterQueue];
    JitterQueue* r2 = [JitterQueue jitterQueue];
    
    EncodedAudioPacket* q1 = [EncodedAudioPacket encodedAudioPacketWithAudioData:[NSData dataWithLength:1] andSequenceNumber:100];
    test([r1 count] == 0);
    test([r1 tryEnqueue:q1]);
    test([r1 count] == 1);
    test([r1 tryDequeue] == q1);
    test([r1 count] == 0);

    EncodedAudioPacket* q2 = [EncodedAudioPacket encodedAudioPacketWithAudioData:[NSData dataWithLength:1] andSequenceNumber:0xFF00];
    test([r2 count] == 0);
    test([r2 tryEnqueue:q2]);
    test([r2 count] == 1);
    test([r2 tryDequeue] == q2);
    test([r2 count] == 0);
}
-(void) testJitterAdvances {
    JitterQueue* r = [JitterQueue jitterQueue];
    
    for (uint16_t i = 0; i < 10; i++) {
        EncodedAudioPacket* q = [EncodedAudioPacket encodedAudioPacketWithAudioData:[NSData dataWithLength:1] andSequenceNumber:i];
        test([r tryEnqueue:q]);
        test([r count] == i+1);
    }
    
    for (uint16_t i = 0; i < 10; i++) {
        test([[r tryDequeue] sequenceNumber] == i);
        test([r count] == 9-i);
    }
    test([r tryDequeue] == nil);
}
-(void) testJitterAdvancesWithHoles {
    JitterQueue* r = [JitterQueue jitterQueue];
    
    for (uint16_t i = 0; i < 10; i++) {
        EncodedAudioPacket* q = [EncodedAudioPacket encodedAudioPacketWithAudioData:[NSData dataWithLength:1] andSequenceNumber:i*2+1];
        test([r tryEnqueue:q]);
    }
    
    for (uint16_t i = 0; i < 20; i++) {
        EncodedAudioPacket* p = [r tryDequeue];
        test((p == nil) == (i%2==1));
        test(p == nil || [p sequenceNumber] == i+1);
    }
    test([r tryDequeue] == nil);
}
-(void) testJitterAdvancesIncrementally {
    JitterQueue* r = [JitterQueue jitterQueue];
    
    for (uint16_t i = 0; i < 20; i++) {
        for (uint16_t j = 0; j < 2; j++) {
            EncodedAudioPacket* q = [EncodedAudioPacket encodedAudioPacketWithAudioData:[NSData dataWithLength:1] andSequenceNumber:i*2+j];
            test([r tryEnqueue:q]);
        }
 
        test([[r tryDequeue] sequenceNumber] == i);
    }
    test([[r tryDequeue] sequenceNumber] == 20);
}
-(void) testJitterQueueRejectsDuplicateSequenceNumbers {
    JitterQueue* r = [JitterQueue jitterQueue];

    EncodedAudioPacket* p = [EncodedAudioPacket encodedAudioPacketWithAudioData:[NSData dataWithLength:1] andSequenceNumber:0];
    test([r tryEnqueue:p]);

    for (uint16_t i = 0; i < 10; i++) {
        EncodedAudioPacket* q = [EncodedAudioPacket encodedAudioPacketWithAudioData:[NSData dataWithLength:1] andSequenceNumber:0];
        test(![r tryEnqueue:q]);
    }
    
    test([r tryDequeue] == p);
    test([r tryDequeue] == nil);
}
-(void) testJitterQueueRejectsOldSequenceNumbers {
    JitterQueue* r = [JitterQueue jitterQueue];
    
    EncodedAudioPacket* p = [EncodedAudioPacket encodedAudioPacketWithAudioData:[NSData dataWithLength:1] andSequenceNumber:50];
    test([r tryEnqueue:p]);
    
    for (uint16_t i = 1; i < 10; i++) {
        EncodedAudioPacket* q = [EncodedAudioPacket encodedAudioPacketWithAudioData:[NSData dataWithLength:1] andSequenceNumber:50 - i];
        test(![r tryEnqueue:q]);
    }
    
    test([r tryDequeue] == p);
    test([r tryDequeue] == nil);
}
-(void) testJitterQueueRejectsFarOffSequenceNumbers {
    JitterQueue* r = [JitterQueue jitterQueue];
    
    EncodedAudioPacket* p = [EncodedAudioPacket encodedAudioPacketWithAudioData:[NSData dataWithLength:1] andSequenceNumber:0];
    test([r tryEnqueue:p]);
    
    for (uint16_t i = 0; i < 10; i++) {
        EncodedAudioPacket* q = [EncodedAudioPacket encodedAudioPacketWithAudioData:[NSData dataWithLength:1] andSequenceNumber:0x7000+i];
        test(![r tryEnqueue:q]);
    }
    
    test([r tryDequeue] == p);
    test([r tryDequeue] == nil);
}
-(void) testJitterQueueResyncsSequenceNumber {
    JitterQueue* r = [JitterQueue jitterQueue];
    
    EncodedAudioPacket* p = [EncodedAudioPacket encodedAudioPacketWithAudioData:[NSData dataWithLength:1] andSequenceNumber:1];
    test([r tryEnqueue:p]);
    test([r tryDequeue] == p);

    EncodedAudioPacket* q = [EncodedAudioPacket encodedAudioPacketWithAudioData:[NSData dataWithLength:1] andSequenceNumber:0];
    test(![r tryEnqueue:q]);

    // cause desync to be detected
    for (uint16_t i = 0; i < 5000; i++) {
        test([r tryDequeue] == nil);
    }
    
    // resync at q, before p's index
    test([r tryEnqueue:q]);
    test([r tryDequeue] == q);
}
-(void) testLoopAround {
    JitterQueue* r = [JitterQueue jitterQueue];
    
    for (int i = 0; i < 1 << 17; i++) {
        EncodedAudioPacket* q = [EncodedAudioPacket encodedAudioPacketWithAudioData:[NSData dataWithLength:1] andSequenceNumber:(uint16_t)(i & 0xFFFF)];
        test([r tryEnqueue:q]);
        test([r tryDequeue] == q);
    }
    test([r tryDequeue] == nil);
}
-(void) testJitterQueueAvoidsRacingAhead {
    JitterQueue* r = [JitterQueue jitterQueue];
    EncodedAudioPacket* p1 = [EncodedAudioPacket encodedAudioPacketWithAudioData:increasingData(1) andSequenceNumber:0];
    EncodedAudioPacket* p2 = [EncodedAudioPacket encodedAudioPacketWithAudioData:increasingData(1) andSequenceNumber:1];

    test([r tryEnqueue:p1]);
    test([r tryDequeue] == p1);
    
    test([r tryDequeue] == nil);
    
    test([r tryEnqueue:[EncodedAudioPacket encodedAudioPacketWithAudioData:increasingData(1) andSequenceNumber:10]]);
    test([r tryDequeue] == nil);
    
    test([r tryEnqueue:p2]);
    test([r tryDequeue] == p2);

    test([r tryDequeue] == nil);
}

-(void) testJitterQueueMeasurement {
    JitterQueue* q = [JitterQueue jitterQueue];
    [q tryEnqueue:[EncodedAudioPacket encodedAudioPacketWithAudioData:increasingData(20) andSequenceNumber:1]];
    test([q currentBufferDepth] == 0);
    [q tryEnqueue:[EncodedAudioPacket encodedAudioPacketWithAudioData:increasingData(20) andSequenceNumber:2]];
    test([q currentBufferDepth] == 1);
    [q tryEnqueue:[EncodedAudioPacket encodedAudioPacketWithAudioData:increasingData(20) andSequenceNumber:4]];
    test([q currentBufferDepth] == 3);
    [q tryDequeue];
    test([q currentBufferDepth] == 2);
    [q tryDequeue];
    test([q currentBufferDepth] == 1);
    [q tryDequeue];
    test([q currentBufferDepth] == 0);
    [q tryDequeue];
    test([q currentBufferDepth] == -1);
    [q tryEnqueue:[EncodedAudioPacket encodedAudioPacketWithAudioData:increasingData(20) andSequenceNumber:8]];
    test([q currentBufferDepth] == 3);
    
    // resyncs to 0
    for (int i = 0; i < 500; i++) {
        [q tryDequeue];        
    }
    [q tryEnqueue:[EncodedAudioPacket encodedAudioPacketWithAudioData:increasingData(20) andSequenceNumber:9000]];
    test([q currentBufferDepth] == 0);
}
@end
