#import "RingBuffer.h"

@implementation RingBuffer

- (id)initWithData:(NSMutableData*)data {
  if ((self = [super init])) {
    buffer = [data retain];
  }
	return self;
}


- (NSUInteger)freeSpace {
	return (position < tail ? tail - position : (buffer.length - position) + tail) - (tail ? 1 : 0);
}


- (BOOL)appendByte:(uint8_t)byte {
	if (self.freeSpace < 1) return NO;
	((uint8_t*)buffer.mutableBytes)[position++] = byte;
	return YES;
}


- (NSInteger)appendData:(const NSData*)value offset:(NSInteger)offset length:(NSInteger)length {
	NSInteger totalWritten = 0;
	const uint8_t *input = value.bytes;
	uint8_t *data = buffer.mutableBytes;
	
	if (position >= tail) {
		totalWritten = MIN(buffer.length - position, length);
		memcpy(data + position, input + offset, totalWritten);
		position += totalWritten;
		if (totalWritten == length) return length;
		length -= totalWritten;
		offset += totalWritten;
	}
	
	NSUInteger freeSpace = self.freeSpace;
	if (!freeSpace) return totalWritten;
	
	if (position == buffer.length) {
		position = 0;
	}
	
	// position < tail
	int32_t written = (int32_t)MIN(freeSpace, length);
	memcpy(data + position, input + offset, written);
	position += written;
	totalWritten += written;
	
	return totalWritten;
}


- (NSInteger)flushToOutputStream:(NSOutputStream*)stream {
	NSInteger totalWritten = 0;
	const uint8_t *data = buffer.bytes;
	
	if (tail > position) {
		int32_t written = (int32_t)[stream write:data + tail maxLength:buffer.length - tail];
        if (written <= 0) return totalWritten;
        totalWritten += written;
		tail += written;
		if (tail == buffer.length) {
			tail = 0;
		}
	}

	if (tail < position) {
		int32_t written = (int32_t)[stream write:data + tail maxLength:position - tail];
		if (written <= 0) return totalWritten;
		totalWritten += written;
		tail += written;
	}

    if (tail == position) {
        tail = position = 0;
    }

    if (position == buffer.length && tail > 0) {
        position = 0;
    }

    if (tail == buffer.length) {
        tail = 0;
    }
	
	return totalWritten;
}


- (void)dealloc {
	[buffer release];
	[super dealloc];
}

@end
