//
//  Categories.m
//  SFS2X
//
//  Original development by Infosfer Game Technologies Ltd. | http://www.infosfer.com.
//
//  Maintained and developed by A51 Integrated.
//  Copyright 2012 A51 Integrated | http://a51integrated.com. All rights reserved.
//

#import "Categories.h"
#import <CommonCrypto/CommonDigest.h>
#include <zlib.h>

@implementation NSString (MD5)
- (NSString *) md5
{
    const char *cStr = [self UTF8String];
    unsigned char result[16];
    CC_MD5( cStr, (int)strlen(cStr), result ); // This is the md5 call
    return [NSString stringWithFormat:
			@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
			result[0], result[1], result[2], result[3], 
			result[4], result[5], result[6], result[7],
			result[8], result[9], result[10], result[11],
			result[12], result[13], result[14], result[15]
    ]; 
}

@end

@implementation NSStream (MyAdditions)

+ (void)getStreamsToHostNamed:(NSString *)hostName port:(NSInteger)port inputStream:(NSInputStream **)inputStream outputStream:(NSOutputStream **)outputStream
{
    CFHostRef           host;
    CFReadStreamRef     readStream;
    CFWriteStreamRef    writeStream;
	
    readStream = NULL;
    writeStream = NULL;
    
    host = CFHostCreateWithName(NULL, (__bridge CFStringRef) hostName);
    if (host != NULL) {
        (void) CFStreamCreatePairWithSocketToCFHost(NULL, host, (int)port, &readStream, &writeStream);
        CFRelease(host);
    }
    
    if (inputStream == NULL) {
        if (readStream != NULL) {
            CFRelease(readStream);
        }
    } else {
        *inputStream = (NSInputStream *) CFBridgingRelease(readStream);
    }
    if (outputStream == NULL) {
        if (writeStream != NULL) {
            CFRelease(writeStream);
        }
    } else {
        *outputStream = (NSOutputStream *) CFBridgingRelease(writeStream);
    }
    
}

@end

@implementation NSMutableData (NSMutableDataExtension)

-(void)writeBool:(BOOL)value {
	
	[self appendBytes:&value length:1];
	
}
-(void)writeByte:(uint8_t)value {
	
	
	[self appendBytes:&value length:1];
	
}

-(void)writeShort:(short)value {
	
	value = CFSwapInt16(value);
	[self appendBytes:&value length:2];
	
}
-(void)writeInt:(int)value {
	
	value = CFSwapInt32(value);
	[self appendBytes:&value length:4];
}
-(void)writeLong:(long long)value {
	
	value = CFSwapInt64(value);
	[self appendBytes:&value length:8];
}

-(void)writeFloat:(float)value {
	
	uint8_t *p = (uint8_t *)&value;
	
	uint32_t f;
	uint8_t *fp = (uint8_t *)&f;
	
	for (int i = 0; i < 4; i++) {
		*(fp++) = *(p++);
	}
	f = CFSwapInt32(f);
	[self appendBytes:&f length:4];
}

-(void)writeDouble:(double)value {
	
	uint8_t *p = (uint8_t *)&value;
	
	uint64_t f;
	uint8_t *fp = (uint8_t *)&f;
	
	for (int i = 0; i < 8; i++) {
		*(fp++) = *(p++);
	}
	f = CFSwapInt64(f);
	[self appendBytes:&f length:8];
}

-(void)writeUTF:(NSString *)value
{
	NSData *strData = [value dataUsingEncoding:NSUTF8StringEncoding];
	
	[self writeShort:[strData length]];
	
	[self appendData:strData];
}

-(void)writeText:(NSString *)value
{
    NSData *strData = [value dataUsingEncoding:NSUTF8StringEncoding];
    
    [self writeInt:(int)[strData length]];
    
    [self appendData:strData];
}

@end

@implementation Data (DataExtension)

-(NSString *) readText
{
    NSInteger textLen = [self readInt];
    NSData* textData = [self readDataWithLength: (int)textLen];
    
    return [NSString stringWithUTF8String:[textData bytes]];
}

@end

@implementation NSData (NSDataExtension)

-(NSData *)gzipInflate {
	if ([self length] == 0) return self;
	
	unsigned full_length = (int)[self length];
	unsigned half_length = (int)[self length] / 2;
	
	NSMutableData *decompressed = [NSMutableData dataWithLength: full_length + half_length];
	BOOL done = NO;
	int status;
	
	z_stream strm;
	strm.next_in = (Bytef *)[self bytes];
	strm.avail_in = (int)[self length];
	strm.total_out = 0;
	strm.zalloc = Z_NULL;
	strm.zfree = Z_NULL;
	
	if (inflateInit2(&strm, (15+32)) != Z_OK) return nil;
	while (!done)
	{
		// Make sure we have enough room and reset the lengths.
		if (strm.total_out >= [decompressed length])
			[decompressed increaseLengthBy: half_length];
		strm.next_out = [decompressed mutableBytes] + strm.total_out;
		strm.avail_out = (int)[decompressed length] - (int)strm.total_out;
		
		// Inflate another chunk.
		status = inflate (&strm, Z_SYNC_FLUSH);
		if (status == Z_STREAM_END) done = YES;
		else if (status != Z_OK) break;
	}
	if (inflateEnd (&strm) != Z_OK) return nil;
	
	// Set real length.
	if (done)
	{
		[decompressed setLength: strm.total_out];
		return [NSData dataWithData: decompressed];
	}
	else return nil;
}

-(NSData *)gzipDeflate {
	if ([self length] == 0) return self;
	
	z_stream strm;
	
	strm.zalloc = Z_NULL;
	strm.zfree = Z_NULL;
	strm.opaque = Z_NULL;
	strm.total_out = 0;
	strm.next_in=(Bytef *)[self bytes];
	strm.avail_in = (int)[self length];
	
	// Compresssion Levels:
	//   Z_NO_COMPRESSION
	//   Z_BEST_SPEED
	//   Z_BEST_COMPRESSION
	//   Z_DEFAULT_COMPRESSION
	
	if (deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY) != Z_OK) return nil;
	
	NSMutableData *compressed = [NSMutableData dataWithLength:16384];  // 16K chunks for expansion
	
	do {
		
		if (strm.total_out >= [compressed length])
			[compressed increaseLengthBy: 16384];
		
		strm.next_out = [compressed mutableBytes] + strm.total_out;
		strm.avail_out = (int)[compressed length] - (int)strm.total_out;
		
		deflate(&strm, Z_FINISH);  
		
	} while (strm.avail_out == 0);
	
	deflateEnd(&strm);
	
	[compressed setLength: strm.total_out];
	return [NSData dataWithData:compressed];
}

- (NSData *)zlibDeflate
{
	if ([self length] == 0) return self;
	
	z_stream strm;
	
	strm.zalloc = Z_NULL;
	strm.zfree = Z_NULL;
	strm.opaque = Z_NULL;
	strm.total_out = 0;
	strm.next_in=(Bytef *)[self bytes];
	strm.avail_in = (int)[self length];
	
	// Compresssion Levels:
	//   Z_NO_COMPRESSION
	//   Z_BEST_SPEED
	//   Z_BEST_COMPRESSION
	//   Z_DEFAULT_COMPRESSION
	
	if (deflateInit(&strm, Z_DEFAULT_COMPRESSION) != Z_OK) return nil;
	
	NSMutableData *compressed = [NSMutableData dataWithLength:16384];  // 16K chuncks for expansion
	
	do {
		
		if (strm.total_out >= [compressed length])
			[compressed increaseLengthBy: 16384];
		
		strm.next_out = [compressed mutableBytes] + strm.total_out;
		strm.avail_out = (int)[compressed length] - (int)strm.total_out;
		
		deflate(&strm, Z_FINISH);  
		
	} while (strm.avail_out == 0);
	
	deflateEnd(&strm);
	
	[compressed setLength: strm.total_out];
	return [NSData dataWithData: compressed];
}

-(NSData *)zlibInflate
{
	if ([self length] == 0) return self;
	
	unsigned full_length = (int)[self length];
	unsigned half_length = (int)[self length] / 2;
	
	NSMutableData *decompressed = [NSMutableData dataWithLength: full_length + half_length];
	BOOL done = NO;
	int status;
	
	z_stream strm;
	strm.next_in = (Bytef *)[self bytes];
	strm.avail_in = (int)[self length];
	strm.total_out = 0;
	strm.zalloc = Z_NULL;
	strm.zfree = Z_NULL;
	
	if (inflateInit (&strm) != Z_OK) return nil;
	
	while (!done)
	{
		// Make sure we have enough room and reset the lengths.
		if (strm.total_out >= [decompressed length])
			[decompressed increaseLengthBy: half_length];
		strm.next_out = [decompressed mutableBytes] + strm.total_out;
		strm.avail_out = (int)[decompressed length] - (int)strm.total_out;
		
		// Inflate another chunk.
		status = inflate (&strm, Z_SYNC_FLUSH);
		if (status == Z_STREAM_END) done = YES;
		else if (status != Z_OK) break;
	}
	if (inflateEnd (&strm) != Z_OK) return nil;
	
	// Set real length.
	if (done)
	{
		[decompressed setLength: strm.total_out];
		return [NSData dataWithData: decompressed];
	}
	else return nil;
}


@end

