//
//  ChunkList.m
//  CogAudio Framework
//
//  Created by Christopher Snowhill on 2/5/22.
//

#import "ChunkList.h"

@implementation ChunkList

@synthesize listDuration;
@synthesize maxDuration;

- (id)initWithMaximumDuration:(double)duration {
	self = [super init];

	if(self) {
		chunkList = [[NSMutableArray alloc] init];
		listDuration = 0.0;
		maxDuration = duration;

		inAdder = NO;
		inRemover = NO;
		inPeeker = NO;
		stopping = NO;
	}

	return self;
}

- (void)dealloc {
	stopping = YES;
	while(inAdder || inRemover || inPeeker) {
		usleep(500);
	}
}

- (void)reset {
	@synchronized(chunkList) {
		[chunkList removeAllObjects];
		listDuration = 0.0;
	}
}

- (BOOL)isEmpty {
	@synchronized(chunkList) {
		return [chunkList count] == 0;
	}
}

- (BOOL)isFull {
	return (maxDuration - listDuration) < 0.05;
}

- (void)addChunk:(AudioChunk *)chunk {
	if(stopping) return;

	inAdder = YES;

	const double chunkDuration = [chunk duration];

	@synchronized(chunkList) {
		[chunkList addObject:chunk];
		listDuration += chunkDuration;
	}

	inAdder = NO;
}

- (AudioChunk *)removeSamples:(size_t)maxFrameCount {
	if(stopping) {
		return [[AudioChunk alloc] init];
	}

	@synchronized(chunkList) {
		inRemover = YES;
		if(![chunkList count]) {
			inRemover = NO;
			return [[AudioChunk alloc] init];
		}
		AudioChunk *chunk = [chunkList objectAtIndex:0];
		if([chunk frameCount] <= maxFrameCount) {
			[chunkList removeObjectAtIndex:0];
			listDuration -= [chunk duration];
			inRemover = NO;
			return chunk;
		}
		NSData *removedData = [chunk removeSamples:maxFrameCount];
		AudioChunk *ret = [[AudioChunk alloc] init];
		[ret setFormat:[chunk format]];
		[ret setChannelConfig:[chunk channelConfig]];
		[ret assignSamples:[removedData bytes] frameCount:maxFrameCount];
		listDuration -= [ret duration];
		inRemover = NO;
		return ret;
	}
}

- (BOOL)peekFormat:(AudioStreamBasicDescription *)format channelConfig:(uint32_t *)config {
	if(stopping) return NO;
	@synchronized(chunkList) {
		if([chunkList count]) {
			AudioChunk *chunk = [chunkList objectAtIndex:0];
			*format = [chunk format];
			*config = [chunk channelConfig];
			return YES;
		}
	}
	return NO;
}

@end
