//
//  DefaultSFSDataSerializer.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 "DefaultSFSDataSerializer.h"
#import "SFSCodecException.h"
#import "SFSDataType.h"
#import "SFSDataWrapper.h"
#import "SFSArray.h"
#import "SFSObject.h"
#import "SerializableSFSType.h"
#import "Data.h"
#import "Categories.h"

@implementation DefaultSFSDataSerializer

static NSString * const CLASS_MARKER_KEY = @"$C";
static NSString * const CLASS_FIELDS_KEY = @"$F";
static NSString * const FIELD_NAME_KEY = @"N";
static NSString * const FIELD_VALUE_KEY = @"V";

static DefaultSFSDataSerializer *_instance = nil;


+(id)getInstance {
	
	if (_instance == nil) {
		_instance = [DefaultSFSDataSerializer alloc];
	}
	
	return _instance;
}

-(NSInteger)getTypedArraySize:(Data *)buffer{
	
	short size = [buffer readShort];
	
	if (size < 0) {
		@throw [SFSCodecException exceptionWithReason:[NSString stringWithFormat:@"Array negative size: %d", size]];
	}
	
	return size;
}

#pragma mark -
#pragma mark binDecode
#pragma mark -



-(SFSDataWrapper *)binDecode_NULL:(Data *)buffer{
	return [[SFSDataWrapper alloc] initWithType:SFSDataType_NULL data:nil];
}

-(SFSDataWrapper *)binDecode_BOOL:(Data *)buffer{
	return [[SFSDataWrapper alloc] initWithType:SFSDataType_BOOL data:[NSNumber numberWithBool:[buffer readBool]]];
}

-(SFSDataWrapper *)binDecode_BYTE:(Data *)buffer{
	return [[SFSDataWrapper alloc] initWithType:SFSDataType_BYTE data:[NSNumber numberWithInt:[buffer readByte]]];
}

-(SFSDataWrapper *)binDecode_SHORT:(Data *)buffer{
	return [[SFSDataWrapper alloc] initWithType:SFSDataType_SHORT data:[NSNumber numberWithInt:[buffer readShort]]];
}

-(SFSDataWrapper *)binDecode_INT:(Data *)buffer{
	return [[SFSDataWrapper alloc] initWithType:SFSDataType_INT data:[NSNumber numberWithInt:[buffer readInt]]];
}

-(SFSDataWrapper *)binDecode_LONG:(Data *)buffer{
	return [[SFSDataWrapper alloc] initWithType:SFSDataType_LONG data:[NSNumber numberWithLongLong:[buffer readLong]]];
}

-(SFSDataWrapper *)binDecode_FLOAT:(Data *)buffer{
	
	return [[SFSDataWrapper alloc] initWithType:SFSDataType_FLOAT data:[NSNumber numberWithFloat:[buffer readFloat]]];
}

-(SFSDataWrapper *)binDecode_DOUBLE:(Data *)buffer{
	
	return [[SFSDataWrapper alloc] initWithType:SFSDataType_DOUBLE data:[NSNumber numberWithDouble:[buffer readDouble]]];
	
}

-(SFSDataWrapper *)binDecode_UTF_STRING:(Data *)buffer{
	
	return [[SFSDataWrapper alloc] initWithType:SFSDataType_UTF_STRING data:[buffer readUTF]];
}

-(SFSDataWrapper *)binDecode_TEXT:(Data *)buffer
{
    return [[SFSDataWrapper alloc] initWithType:SFSDataType_TEXT data:[buffer readText]];
}

-(SFSDataWrapper *)binDecode_BOOL_ARRAY:(Data *)buffer{
	
	int size = (int)[self getTypedArraySize:buffer];
	NSMutableArray *array = [NSMutableArray array];
	
	for (int j = 0; j < size; j++) {
		[array addObject:[NSNumber numberWithBool:[buffer readBool]]];
	}
	
	return [[SFSDataWrapper alloc] initWithType:SFSDataType_BOOL_ARRAY data:array];
	
}

-(SFSDataWrapper *)binDecode_BYTE_ARRAY:(Data *)buffer{
	
	int size = [buffer readInt];
	
	if (size < 0) {
		@throw [NSException exceptionWithName:@"SFSCodecError" reason:[NSString stringWithFormat:@"Array negative size: %d", size] userInfo:nil];
	}
	
	return [[SFSDataWrapper alloc] initWithType:SFSDataType_BYTE_ARRAY data:[buffer readDataWithLength:size]];
}

-(SFSDataWrapper *)binDecode_SHORT_ARRAY:(Data *)buffer{
	
	int size = (int)[self getTypedArraySize:buffer];
	
	NSMutableArray *array = [NSMutableArray array];
	
	for (int j = 0; j < size; j++) {
		
		[array addObject:[NSNumber numberWithShort:[buffer readShort]]];
		
	}
	
	return [[SFSDataWrapper alloc] initWithType:SFSDataType_SHORT_ARRAY data:array];
	
}

-(SFSDataWrapper *)binDecode_INT_ARRAY:(Data *)buffer{
	
	int size = (int)[self getTypedArraySize:buffer];
	NSMutableArray *array = [NSMutableArray array];
	
	for (int j = 0; j < size; j++) {
		
		[array addObject:[NSNumber numberWithInt:[buffer readInt]]];
	}
	
	return [[SFSDataWrapper alloc] initWithType:SFSDataType_INT_ARRAY data:array];
}

-(SFSDataWrapper *)binDecode_LONG_ARRAY:(Data *)buffer{
	int size = (int)[self getTypedArraySize:buffer];
	NSMutableArray *array = [NSMutableArray array];
	
	for (int j = 0; j < size; j++) {
		[array addObject:[NSNumber numberWithLongLong:[buffer readLong]]];
	}
	
	return [[SFSDataWrapper alloc] initWithType:SFSDataType_LONG_ARRAY data:array];
}

-(SFSDataWrapper *)binDecode_FLOAT_ARRAY:(Data *)buffer{
	int size = (int)[self getTypedArraySize:buffer];
	NSMutableArray *array = [NSMutableArray array];
	
	for (int j = 0; j < size; j++) {
		[array addObject:[NSNumber numberWithFloat:[buffer readFloat]]];
	}
	
	return [[SFSDataWrapper alloc] initWithType:SFSDataType_FLOAT_ARRAY data:array];
}

-(SFSDataWrapper *)binDecode_DOUBLE_ARRAY:(Data *)buffer{
	int size = (int)[self getTypedArraySize:buffer];
	
	
	NSMutableArray *array = [NSMutableArray array];
	
	for (int j = 0; j < size; j++) {
				
		[array addObject:[NSNumber numberWithDouble:[buffer readDouble]]];
	}
	
	return [[SFSDataWrapper alloc] initWithType:SFSDataType_DOUBLE_ARRAY data:array];
}

-(SFSDataWrapper *)binDecode_UTF_STRING_ARRAY:(Data *)buffer{
	
	int size = (int)[self getTypedArraySize:buffer];
	
	NSMutableArray *array = [NSMutableArray array];
	
	for (int j = 0; j < size; j++) {
		[array addObject:[buffer readUTF]];
	}
	
	return [[SFSDataWrapper alloc] initWithType:SFSDataType_UTF_STRING_ARRAY data:array];
	
}

-(NSArray *)rebuildArray:(id <ISFSArray>)sfsArr {
	
	NSMutableArray *arr = [NSMutableArray array];
	
	for (int j = 0; j < [sfsArr size]; j++) {
		[arr addObject:[self unwrapObjcField:[sfsArr getWrappedElementAt:j]]];
	}
	
	return arr;
}
-(NSDictionary *)rebuildDict:(id <ISFSObject>)sfsObj {
	
	NSMutableDictionary *dict = [NSMutableDictionary dictionary];
	
	for (NSString *key in [sfsObj getKeys]) {
		[dict setObject:[self unwrapObjcField:[sfsObj getData:key]] forKey:key];
	}
	
	return dict;
}

-(id)unwrapObjcField:(SFSDataWrapper *)wrapper {
	
	id obj;
	int type = (int)wrapper.type;
	
	if (type <= SFSDataType_UTF_STRING) {
		obj = wrapper.data;
	}
	else if(type == SFSDataType_SFS_ARRAY) {
		obj = [self rebuildArray:(SFSArray *)wrapper.data];
	}
	else if(type == SFSDataType_SFS_OBJECT) {
		obj = [self rebuildDict:(SFSObject *)wrapper.data];
	}
	else if(type == SFSDataType_CLASS){
		obj = wrapper.data;
	}
    else
    {
        obj = nil;
    }

	return obj;
}

-(void)convertSFSObject:(id <ISFSArray>)fieldList objcObj:(id <SerializableSFSType>)objcObj {
	
	id <ISFSObject> fieldDescriptor;
	
	NSString *fieldName;
	id fieldValue;
	
	for (int j = 0; j < [fieldList size]; j++) {
		fieldDescriptor = [fieldList getSFSObject:j];
		fieldName = [fieldDescriptor getUtfString:FIELD_NAME_KEY];
		fieldValue = [self unwrapObjcField:[fieldDescriptor getData:FIELD_VALUE_KEY]];
		
		[[objcObj dictionary] setObject:fieldValue forKey:fieldName];
	}
	
}

-(id)sfs2objc:(id <ISFSObject>)sfsObj {
	
	id objcObj;
	
	if (![sfsObj containsKey:CLASS_MARKER_KEY] && ![sfsObj containsKey:CLASS_FIELDS_KEY]) {
		@throw [SFSCodecException exceptionWithReason:@"The SFSObject passed does not represent any serialized class."];
	}
	
	NSString *className = [sfsObj getUtfString:CLASS_MARKER_KEY];
	Class theClass = NSClassFromString(className);
	
	objcObj = [[theClass alloc] init];
	
	if (![objcObj conformsToProtocol:@protocol(SerializableSFSType)]) {
		@throw [SFSCodecException exceptionWithReason:[NSString stringWithFormat:@"Cannot deserialize object type: %@. It doesn't implement the SerializableSFSType interface", className]];
	}
	
	[self convertSFSObject:[sfsObj getSFSArray:CLASS_FIELDS_KEY] objcObj:objcObj];
	
	
	return objcObj;
}


-(id <ISFSArray>)decodeSFSArray:(Data *)buffer{
	
	id <ISFSArray> sfsArray = [SFSArray newInstance];
	
	int8_t headerByte = [buffer readByte];
	
	if (headerByte != SFSDataType_SFS_ARRAY) {
		@throw [SFSCodecException exceptionWithReason:[NSString stringWithFormat:@"Invalid SFSDataType. Expected: %ld, found: %ld", (long)SFSDataType_SFS_ARRAY, (long)headerByte]];
	}
	
	short size = [buffer readShort];
	
	
	if (size < 0) {
		@throw [SFSCodecException exceptionWithReason:[NSString stringWithFormat:@"Can't decode SFSArray. Size is negative: %ld", (long)size]];
	}
	
	@try {
		for (int i = 0; i < size; i++)
        {
			SFSDataWrapper *decodedObject = [self decodeObject:buffer];
			
			if (decodedObject != nil)
            {
				[sfsArray add:decodedObject];
			}
			else
            {
				@throw [SFSCodecException exceptionWithReason:[NSString stringWithFormat:@"Could not decode SFSArray item at index: %ld", (long)i]];
			}
		}
	}
	@catch (SFSCodecException * e)
    {
		@throw e;
	}
	
	return sfsArray;
}


-(id <ISFSObject>)decodeSFSObject:(Data *)buffer{
	
	id <ISFSObject> sfsObject = [SFSObject newInstance];
	
	int8_t headerByte = [buffer readByte];
	
	if (headerByte != SFSDataType_SFS_OBJECT)
    {
		@throw [SFSCodecException exceptionWithReason:[NSString stringWithFormat:@"Invalid SFSDataType. Expected: %ld, found: %ld", (long)SFSDataType_SFS_OBJECT, (long)headerByte]];
	}
	
	short size = [buffer readShort];
	
	if (size < 0)
    {
		@throw [SFSCodecException exceptionWithReason:[NSString stringWithFormat:@"Can't decode SFSObject. Size is negative: %ld", (long)size]];
	}
	
	@try {
		
		for (int i = 0; i < size; i++)
        {
			NSString *key = [buffer readUTF];
			
			SFSDataWrapper *decodedObject = [self decodeObject:buffer];
			
			if (decodedObject != nil) {
				[sfsObject put:key value:decodedObject];
			}
			else
            {
				@throw [SFSCodecException exceptionWithReason:[NSString stringWithFormat:@"Could not decode value for SFSObject with key: %@", key]];
			}
		}
	}
	@catch (SFSCodecException * e)
    {
		@throw e;
	}
	
	return sfsObject;
}



-(SFSDataWrapper *)decodeObject:(Data *)buffer{
	
	SFSDataWrapper *decodedObject;
	int8_t headerByte = [buffer readByte];
	
	if (headerByte == SFSDataType_NULL) {
		decodedObject = [self binDecode_NULL:buffer];
	}
	else if (headerByte == SFSDataType_BOOL) {
		decodedObject = [self binDecode_BOOL:buffer];
	}
	else if (headerByte == SFSDataType_BYTE) {
		decodedObject = [self binDecode_BYTE:buffer];
	}
	else if (headerByte == SFSDataType_SHORT) {
		decodedObject = [self binDecode_SHORT:buffer];
	}
	else if (headerByte == SFSDataType_INT) {
		decodedObject = [self binDecode_INT:buffer];
	}
	else if (headerByte == SFSDataType_LONG) {
		decodedObject = [self binDecode_LONG:buffer];
	}
	else if (headerByte == SFSDataType_FLOAT) {
		decodedObject = [self binDecode_FLOAT:buffer];
	}
	else if (headerByte == SFSDataType_DOUBLE) {
		decodedObject = [self binDecode_DOUBLE:buffer];
	}
	else if (headerByte == SFSDataType_UTF_STRING) {
		decodedObject = [self binDecode_UTF_STRING:buffer];
	}
	else if (headerByte == SFSDataType_BOOL_ARRAY) {
		decodedObject = [self binDecode_BOOL_ARRAY:buffer];
	}
	else if (headerByte == SFSDataType_BYTE_ARRAY) {
		decodedObject = [self binDecode_BYTE_ARRAY:buffer];
	}
	else if (headerByte == SFSDataType_SHORT_ARRAY) {
		decodedObject = [self binDecode_SHORT_ARRAY:buffer];
	}
	else if (headerByte == SFSDataType_INT_ARRAY) {
		decodedObject = [self binDecode_INT_ARRAY:buffer];
	}
	else if (headerByte == SFSDataType_LONG_ARRAY) {
		decodedObject = [self binDecode_LONG_ARRAY:buffer];
	}
	else if (headerByte == SFSDataType_FLOAT_ARRAY) {
		decodedObject = [self binDecode_FLOAT_ARRAY:buffer];
	}
	else if (headerByte == SFSDataType_DOUBLE_ARRAY) {
		decodedObject = [self binDecode_DOUBLE_ARRAY:buffer];
	}
	else if (headerByte == SFSDataType_UTF_STRING_ARRAY) {
		decodedObject = [self binDecode_UTF_STRING_ARRAY:buffer];
	}
    else if (headerByte == SFSDataType_TEXT) {
        decodedObject = [self binDecode_TEXT:buffer];
    }
	else if (headerByte == SFSDataType_SFS_ARRAY) {
		[buffer decreasePosition];
		decodedObject = [[SFSDataWrapper alloc] initWithType:SFSDataType_SFS_ARRAY data:[self decodeSFSArray:buffer]];
	}
	else if (headerByte == SFSDataType_SFS_OBJECT) {
		
		[buffer decreasePosition];
		
		id <ISFSObject> sfsObj = [self decodeSFSObject:buffer];
		
		int type = (int)SFSDataType_SFS_OBJECT;
		id finalSfsObj = sfsObj;
		
		if ([sfsObj containsKey:CLASS_MARKER_KEY] && [sfsObj containsKey:CLASS_FIELDS_KEY]) {
			type = (int)SFSDataType_CLASS;
			finalSfsObj = [self sfs2objc:sfsObj];
		}
		
		decodedObject = [[SFSDataWrapper alloc] initWithType:type data:finalSfsObj];
		
	}
	else {
		@throw [SFSCodecException exceptionWithReason:[NSString stringWithFormat:@"Unknown SFSDataType ID: %d", headerByte]];
	}
	
	
	return decodedObject;
}


-(id <ISFSObject>)unrollDictionary:(NSDictionary *)dict {
	
	SFSObject *sfsObject = [SFSObject newInstance];
	
	for (NSString *key in [dict allKeys]) {
		[sfsObject put:key value:[self wrapObjcField:[dict objectForKey:key]]];
	}
	
	return sfsObject;
}

-(id <ISFSArray>)unrollArray:(NSArray *)arr {
	
	SFSArray *sfsArray = [SFSArray newInstance];
	
	for (int j = 0; j < [arr count]; j++) {
		[sfsArray add:[self wrapObjcField:[arr objectAtIndex:j]]];
	}
	
	return sfsArray;
}

-(SFSDataWrapper *)wrapObjcField:(id)value {
	
	if (value == nil) {
		return [[SFSDataWrapper alloc] initWithType:SFSDataType_NULL data:nil];
	}
	
	SFSDataWrapper *wrapper;
	
	if ([value isKindOfClass:[NSNumber class]]) {
		
		if (strcmp([(NSNumber *)value objCType], @encode(BOOL)) == 0) {
			wrapper = [[SFSDataWrapper alloc] initWithType:SFSDataType_BOOL data:value];
		}
		else if(strcmp([(NSNumber *)value objCType], @encode(int)) == 0){
			wrapper = [[SFSDataWrapper alloc] initWithType:SFSDataType_INT data:value];
		}
		else {
			wrapper = [[SFSDataWrapper alloc] initWithType:SFSDataType_DOUBLE data:value];
		}
	}
	else if ([value isKindOfClass:[NSString class]]) {
		wrapper = [[SFSDataWrapper alloc] initWithType:SFSDataType_UTF_STRING data:value];
	}
	else if ([value isKindOfClass:[NSArray class]]) {
		wrapper = [[SFSDataWrapper alloc] initWithType:SFSDataType_SFS_ARRAY data:[self unrollArray:value]];
	}
	else if ([value conformsToProtocol:@protocol(SerializableSFSType)]) {
		wrapper = [[SFSDataWrapper alloc] initWithType:SFSDataType_SFS_OBJECT data:[self objc2sfs:value]];
	}
	else if ([value isKindOfClass:[NSDictionary class]]) {
		wrapper = [[SFSDataWrapper alloc] initWithType:SFSDataType_SFS_OBJECT data:[self unrollDictionary:value]];
	}
    else {
        wrapper = nil;
    }
	
	
	return wrapper;
}


-(void)convertObjcObj:(id)objcObj sfsObj:(id <ISFSObject>)sfsObj {
	
	NSString *className = NSStringFromClass([objcObj class]);
	
	if(className == nil) {
		@throw [SFSCodecException exceptionWithReason:@"Cannot detect class name"];
	}
	
	if(![objcObj conformsToProtocol:@protocol(SerializableSFSType)]) {
		@throw [SFSCodecException exceptionWithReason:[NSString stringWithFormat:@"Cannot serialize object: %@. It doesn't implement the SerializableSFSType interface", className]];
	}
	
	SFSArray *fieldList = [SFSArray newInstance];
	
	[sfsObj putUtfString:CLASS_MARKER_KEY value:className];
	[sfsObj putSFSArray:CLASS_FIELDS_KEY value:fieldList];
	
	NSDictionary *dict = [(id <SerializableSFSType>)objcObj dictionary];
	
	for (NSString *key in [dict allKeys]) {
		
		id fieldValue = [dict objectForKey:key]; 
		
		SFSObject *fieldDescriptor = [SFSObject newInstance];
		[fieldDescriptor putUtfString:FIELD_NAME_KEY value:key];
		[fieldDescriptor put:FIELD_VALUE_KEY value:[self wrapObjcField:fieldValue]];
		
		[fieldList addSFSObject:fieldDescriptor];
        
        
		
	}
    
						  
}

-(id <ISFSObject>)objc2sfs:(id)objcObj {
	id <ISFSObject> sfsObj = [SFSObject newInstance];
	[self convertObjcObj:objcObj sfsObj:sfsObj];
	
	return sfsObj;
}

-(NSString *)encodeClassName:(NSString *)name {
	return [name stringByReplacingOccurrencesOfString:@"::" withString:@"."];
}



#pragma mark -
#pragma mark binEncode
#pragma mark -

-(NSMutableData *)binEncode_NULL:(NSMutableData *)buffer {
	
	[buffer writeByte:0];
	return buffer;
}

-(NSMutableData *)binEncode_BOOL:(NSMutableData *)buffer value:(BOOL)value{
	
	[buffer writeByte:SFSDataType_BOOL];
	[buffer writeBool:value];
	
	return buffer;
}
-(NSMutableData *)binEncode_BYTE:(NSMutableData *)buffer value:(NSInteger)value	{
	
	[buffer writeByte:SFSDataType_BYTE];
	[buffer writeByte:value];
	
	return buffer;
}

-(NSMutableData *)binEncode_SHORT:(NSMutableData *)buffer value:(NSInteger)value {

	[buffer writeByte:SFSDataType_SHORT];
	[buffer writeShort:value];
	
	return buffer;
}

-(NSMutableData *)binEncode_INT:(NSMutableData *)buffer	value:(NSInteger)value {
	
	[buffer writeByte:SFSDataType_INT];
	[buffer writeInt:(int)value];
	
	return buffer;
}

-(NSMutableData *)binEncode_LONG:(NSMutableData *)buffer value:(NSNumber *)value {
	
	[buffer writeByte:SFSDataType_LONG];
	[buffer writeLong:[value longLongValue]];
	
	return buffer;
}

-(NSMutableData *)binEncode_FLOAT:(NSMutableData *)buffer value:(NSNumber *)value {
	
	[buffer writeByte:SFSDataType_FLOAT];
	[buffer writeFloat:[value floatValue]];
	 
	return buffer;
}

-(NSMutableData *)binEncode_DOUBLE:(NSMutableData *)buffer value:(NSNumber *)value {
	
	[buffer writeByte:SFSDataType_DOUBLE];
	[buffer writeDouble:[value doubleValue]];

	return buffer;
}

-(NSMutableData *)binEncode_UTF_STRING:(NSMutableData *)buffer value:(NSString *)value {
	
	[buffer writeByte:SFSDataType_UTF_STRING];
	[buffer writeUTF:value];
    //[buffer writeUTF:[value stringBy]];
    
	
	return buffer;
}

-(NSMutableData *)binEncode_TEXT:(NSMutableData *)buffer value:(NSString *)value
{
    
    [buffer writeByte:SFSDataType_TEXT];
    [buffer writeText:value];
    
    return buffer;
}

-(NSMutableData *)binEncode_BOOL_ARRAY:(NSMutableData *)buffer value:(NSArray *)value {
	
	[buffer writeByte:SFSDataType_BOOL_ARRAY];
	
	short size = [value count];
	[buffer writeShort:size];
	
	for (int i = 0; i < size; i++) {
		int8_t boolV = [(NSNumber *)[value objectAtIndex:i] boolValue];
		[buffer writeBool:boolV];
	}
	
	return buffer;
}

-(NSMutableData *)binEncode_BYTE_ARRAY:(NSMutableData *)buffer value:(NSData *)value {
	
	[buffer writeByte:SFSDataType_BYTE_ARRAY];
	
	int len = (int)[value length];
	[buffer writeInt:len];
	[buffer appendData:value];
	
	return buffer;
}

-(NSMutableData *)binEncode_SHORT_ARRAY:(NSMutableData *)buffer value:(NSArray *)value {
	
	[buffer writeByte:SFSDataType_SHORT_ARRAY];
	
	short sh = [value count];
	[buffer writeShort:sh];
	
	for (int i = 0; i < sh; i++) {
		short shortV = [(NSNumber *)[value objectAtIndex:i] shortValue];
		[buffer writeShort:shortV];
	}
	
	return buffer;
	
}

-(NSMutableData *)binEncode_INT_ARRAY:(NSMutableData *)buffer value:(NSArray *)value {
	
	[buffer writeByte:SFSDataType_INT_ARRAY];
	
	short sh = [value count];
	[buffer writeShort:sh];
	
	for (int i = 0; i < sh; i++) {
		int intV = [(NSNumber *)[value objectAtIndex:i] intValue];
		[buffer writeInt:intV];
	}
	return buffer;
}

-(NSMutableData *)binEncode_LONG_ARRAY:(NSMutableData *)buffer value:(NSArray *)value {
	
	[buffer writeByte:SFSDataType_LONG_ARRAY];
	
	short sh = [value count];
	[buffer writeShort:sh];
	
	for (int i = 0; i < sh; i++) {
		[buffer writeLong:[(NSNumber *)[value objectAtIndex:i] longLongValue]];
	}
	
	return buffer;
}

-(NSMutableData *)binEncode_FLOAT_ARRAY:(NSMutableData *)buffer value:(NSArray *)value {
	
	[buffer writeByte:SFSDataType_FLOAT_ARRAY];
	
	short sh = [value count];
	[buffer writeShort:sh];
	
	for (int i = 0; i < sh; i++) {
		
		float floatV = [(NSNumber *)[value objectAtIndex:i] floatValue];
		[buffer writeFloat:floatV];
	}
	
	return buffer;
}

-(NSMutableData *)binEncode_DOUBLE_ARRAY:(NSMutableData *)buffer value:(NSArray *)value	{
	
	[buffer writeByte:SFSDataType_DOUBLE_ARRAY];
	
	short sh = [value count];
	[buffer writeShort:sh];
	
	for (int i = 0; i < sh; i++) {
		double doubleV = [(NSNumber *)[value objectAtIndex:i] doubleValue];
		[buffer writeDouble:doubleV];
	}

	return buffer;
}

-(NSMutableData *)binEncode_UTF_STRING_ARRAY:(NSMutableData *)buffer value:(NSArray *)value {
	[buffer writeByte:SFSDataType_UTF_STRING_ARRAY];
	
	short sh = [value count];
	[buffer writeShort:sh];
	
	for (int i = 0; i < sh; i++) {
		NSString *stringValue = (NSString *)[value objectAtIndex:i];
		
		[buffer writeUTF:stringValue];
	}
	return buffer;
}


			
        
#pragma mark -
#pragma mark ISFSDataSerializer
#pragma mark -


-(id <ISFSObject>)binary2object:(NSData *)data {
	if ([data length] < 3) {
		@throw [SFSCodecException exceptionWithReason:[NSString stringWithFormat:@"Can't decode an SFSObject. Byte data is insufficient. Size: %ld byte(s)", (long)[data length]]];
	}
	return [self decodeSFSObject:[Data dataWithData:data]];
}

-(id <ISFSArray>)binary2array:(NSData *)data {
	
	if ([data length] < 3) {
		@throw [SFSCodecException exceptionWithReason:[NSString stringWithFormat:@"Can't decode an SFSArray. Byte data is insufficient. Size: %ld byte(s)", (long)[data length]]];
	}
	return [self decodeSFSArray:[Data dataWithData:data]];
}

-(NSMutableData *)encodeObject:(NSMutableData *)buffer typeId:(NSInteger)typeId data:(id)data {
	
	if (typeId == SFSDataType_NULL) {
		buffer = [self binEncode_NULL:buffer];
	}
	else if (typeId == SFSDataType_BOOL) {
		buffer = [self binEncode_BOOL:buffer value:[(NSNumber *)data boolValue]];
	}
	else if (typeId == SFSDataType_BYTE) {
		buffer = [self binEncode_BYTE:buffer value:[(NSNumber *)data intValue]];
	}
	else if (typeId == SFSDataType_SHORT) {
		buffer = [self binEncode_SHORT:buffer value:[(NSNumber *)data intValue]];
	}
	else if (typeId == SFSDataType_INT) {
		buffer = [self binEncode_INT:buffer value:[(NSNumber *)data intValue]];
	}
	else if (typeId == SFSDataType_LONG) {
		buffer = [self binEncode_LONG:buffer value:data];
	}
	else if (typeId == SFSDataType_FLOAT) {
		buffer = [self binEncode_FLOAT:buffer value:data];
	}
	else if (typeId == SFSDataType_DOUBLE) {
		buffer = [self binEncode_DOUBLE:buffer value:data];
	}
	else if (typeId == SFSDataType_UTF_STRING) {
		buffer = [self binEncode_UTF_STRING:buffer value:data];
	}
    else if (typeId == SFSDataType_TEXT) {
        buffer = [self binEncode_TEXT:buffer value:data];
    }
	else if (typeId == SFSDataType_BOOL_ARRAY) {
		buffer = [self binEncode_BOOL_ARRAY:buffer value:data];
	}
	else if (typeId == SFSDataType_BYTE_ARRAY) {
		buffer = [self binEncode_BYTE_ARRAY:buffer value:data];
	}
	else if (typeId == SFSDataType_SHORT_ARRAY) {
		buffer = [self binEncode_SHORT_ARRAY:buffer value:data];
	}
	else if (typeId == SFSDataType_INT_ARRAY) {
		buffer = [self binEncode_INT_ARRAY:buffer value:data];
	}
	else if (typeId == SFSDataType_LONG_ARRAY) {
		buffer = [self binEncode_LONG_ARRAY:buffer value:data];
	}
	else if (typeId == SFSDataType_FLOAT_ARRAY) {
		buffer = [self binEncode_FLOAT_ARRAY:buffer value:data];
	}
	else if (typeId == SFSDataType_DOUBLE_ARRAY) {
		buffer = [self binEncode_DOUBLE_ARRAY:buffer value:data];
	}
	else if (typeId == SFSDataType_UTF_STRING_ARRAY) {
		buffer = [self binEncode_UTF_STRING_ARRAY:buffer value:data];
	}
	else if (typeId == SFSDataType_SFS_ARRAY) {
		[buffer appendData:[self array2binary:data]];
	}
	else if (typeId == SFSDataType_SFS_OBJECT) {
		[buffer appendData:[self object2binary:data]];
	}
	else if (typeId == SFSDataType_CLASS) {
		[buffer appendData:[self object2binary:[self objc2sfs:data]]];
	}
	else {
		@throw [SFSCodecException exceptionWithReason:[NSString stringWithFormat:@"Unrecognized type in SFSObject serialization: %ld", (long)typeId]];
	}
	
	
	return buffer;
}



-(NSData *)arr2bin:(id <ISFSArray>)array buffer:(NSMutableData *)buffer
{	
	SFSDataWrapper *wrapper = nil;
	
	for (int i = 0; i < [array size]; i++)
    {
		wrapper = [array getWrappedElementAt:i];
		buffer = [self encodeObject:buffer typeId:wrapper.type data:wrapper.data];
	}
	
	return buffer;
}

-(NSData *)array2binary:(id <ISFSArray>)array
{	
	NSMutableData *buffer = [NSMutableData data];
	
	int8_t type = SFSDataType_SFS_ARRAY;
	[buffer appendBytes:&type length:1];
	
	short size = [array size];
	size = CFSwapInt16(size);
	[buffer appendBytes:&size length:2];
	
	return [self arr2bin:array buffer:buffer];
}

-(NSMutableData *)encodeSFSObjectKey:(NSMutableData *)buffer value:(NSString *)value
{
	NSMutableData *data = [NSMutableData data];
	
	NSData *strData = [value dataUsingEncoding:NSUTF8StringEncoding];
	short len = [strData length];
	
	len = CFSwapInt16(len);
	[data appendBytes:&len length:2];
	
	
	[data appendData:strData];
	
	[buffer appendData:data];
	
	return buffer;
}

-(NSData *)obj2bin:(id <ISFSObject>)obj buffer:(NSMutableData *)buffer
{
	NSArray *keys = [obj getKeys];
	SFSDataWrapper *wrapper;
	
	for (NSString *key in keys)
    {
		wrapper = [obj getData:key];
		
		// store the key
		buffer = [self encodeSFSObjectKey:buffer value:key];
		buffer = [self encodeObject:buffer typeId:wrapper.type data:wrapper.data];
	}
	return buffer;
}

-(NSData *)object2binary:(id <ISFSObject>)object
{

	NSMutableData *buffer = [NSMutableData data];
	int8_t dataType = SFSDataType_SFS_OBJECT;
	[buffer appendBytes:&dataType length:1];
	short size = [object size];
	size = CFSwapInt16(size);
	[buffer appendBytes:&size length:2];
	return [self obj2bin:object buffer:buffer];
}

@end
