//
//  XMPPIQ+JabberRPCResonse.m
//  XEP-0009
//
//  Created by Eric Chamberlain on 5/25/10.
//

#import "XMPPIQ+JabberRPCResonse.h"
#import "XMPPJabberRPCModule.h"
#import "XMPPLogging.h"
#import "NSData+XMPP.h"
#import "NSXMLElement+XMPP.h"

#if ! __has_feature(objc_arc)
#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
#endif

// Log levels: off, error, warn, info, verbose
#if DEBUG
  static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN;
#else
  static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN;
#endif

@implementation XMPPIQ(JabberRPCResonse)

-(NSXMLElement *)methodResponseElement {
	NSXMLElement *query = [self elementForName:@"query"];
	return [query elementForName:@"methodResponse"];
}

// is this a Jabber RPC method response
-(BOOL)isMethodResponse {
	/*
	<methodResponse>
	 <params>
		<param>
			<value><string>South Dakota</string></value>
		</param>
	 </params>
	</methodResponse>
	*/
	NSXMLElement *methodResponse = [self methodResponseElement];
	return methodResponse != nil;
}

-(BOOL)isFault {
	/*
	<methodResponse>
	 <fault>
		<value>
			<struct>
				<member>
					<name>faultCode</name>
					<value><int>4</int></value>
				</member>
				<member>
					<name>faultString</name>
					<value><string>Too many parameters.</string></value>
				</member>
			</struct>
		</value>
	 </fault>
	</methodResponse>
	*/
	NSXMLElement *methodResponse = [self methodResponseElement];
	
	return [methodResponse elementForName:@"fault"] != nil;
}

-(BOOL)isJabberRPC {
	/*
	 <query xmlns="jabber:iq:rpc">
		...
	 </query>
	*/
	NSXMLElement *rpcQuery = [self elementForName:@"query" xmlns:@"jabber:iq:rpc"];
	return rpcQuery != nil;
}

-(id)methodResponse:(NSError **)error {
	id response = nil;
	
	/*
	<methodResponse>
		<params>
			<param>
				<value><string>South Dakota</string></value>
			</param>
		</params>
	</methodResponse>
	*/
	
	// or
	
	/*
	<methodResponse>
		<fault>
			<value>
				<struct>
					<member>
						<name>faultCode</name>
						<value><int>4</int></value>
					</member>
					<member>
						<name>faultString</name>
						<value><string>Too many parameters.</string></value>
					</member>
				</struct>
			</value>
		</fault>
	</methodResponse>
	*/
	NSXMLElement *methodResponse = [self methodResponseElement];
	
	// parse the methodResponse
	
	response = [self objectFromElement:(NSXMLElement *)[methodResponse childAtIndex:0]];
	
	if ([self isFault]) {
		// we should produce an error
		// response should be a dict
		if (error) {
			*error = [NSError errorWithDomain:XMPPJabberRPCErrorDomain 
										 code:[[response objectForKey:@"faultCode"] intValue] 
									 userInfo:(NSDictionary *)response];
		}
		response = nil;
	} else {
		if (error) {
			*error = nil;
		}
	}

	return response;
}

-(id)objectFromElement:(NSXMLElement *)param {
	NSString *element = [param name];
	
	if ([element isEqualToString:@"params"] ||
		[element isEqualToString:@"param"] ||
		[element isEqualToString:@"fault"] ||
		[element isEqualToString:@"value"]) {
		
		NSXMLElement *firstChild = (NSXMLElement *)[param childAtIndex:0];
		if (firstChild) {
			return [self objectFromElement:firstChild];
		} else {
			// no child element, treat it like a string
			return [self parseString:[param stringValue]];
		}
	} else if ([element isEqualToString:@"string"] ||
			   [element isEqualToString:@"name"]) {
		return [self parseString:[param stringValue]];
	} else if ([element isEqualToString:@"member"]) {
		return [self parseMember:param];
	} else if ([element isEqualToString:@"array"]) {
		return [self parseArray:param];
	} else if ([element isEqualToString:@"struct"]) {
		return [self parseStruct:param];
	} else if ([element isEqualToString:@"int"]) {
		return [self parseInteger:[param stringValue]];
	} else if ([element isEqualToString:@"double"]) {
		return [self parseDouble:[param stringValue]];
	} else if ([element isEqualToString:@"boolean"]) {
		return [self parseBoolean:[param stringValue]];
	} else if ([element isEqualToString:@"dateTime.iso8601"]) {
		return [self parseDate:[param stringValue]];
	} else if ([element isEqualToString:@"base64"]) {
		return [self parseData:[param stringValue]];
	} else {
		// bad element
		XMPPLogWarn(@"%@: %@ - bad element: %@", THIS_FILE, THIS_METHOD, [param stringValue]);
	}
	return nil;
}				
	
				
#pragma mark -
				
-(NSArray *)parseArray:(NSXMLElement *)arrayElement {
	/*
	 <array>
		<data>
			<value><i4>12</i4></value>
			<value><string>Egypt</string></value>
			<value><boolean>0</boolean></value>
			<value><i4>-31</i4></value>
		</data>
	 </array>
	 */
	NSXMLElement *data = (NSXMLElement *)[arrayElement childAtIndex:0];
	NSArray *children = [data children];
	
	NSMutableArray *array = [NSMutableArray arrayWithCapacity:[children count]];
	
	for (NSXMLElement *child in children) {
		[array addObject:[self objectFromElement:child]];
	}
	
	return array;
}

-(NSDictionary *)parseStruct:(NSXMLElement *)structElement {
	/*
	<struct>
	 <member>
		<name>lowerBound</name>
		<value><i4>18</i4></value>
	 </member>
	 <member>
		<name>upperBound</name>
		<value><i4>139</i4></value>
	 </member>
	</struct>
	*/
	NSArray *children = [structElement children];
	NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:[children count]];
	
	for (NSXMLElement *child in children) {
		[dict addEntriesFromDictionary:[self parseMember:child]];
	}
	
	return dict;
}

-(NSDictionary *)parseMember:(NSXMLElement *)memberElement {
	NSString *key = [self objectFromElement:[memberElement elementForName:@"name"]];
	id value = [self objectFromElement:[memberElement elementForName:@"value"]];
	
	return @{key : value};
}
	
#pragma mark -

- (NSDate *)parseDateString: (NSString *)dateString withFormat: (NSString *)format {
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]];
    [dateFormatter setDateFormat: format];
    
    NSDate *result = [dateFormatter dateFromString: dateString];
    
    return result;
}

#pragma mark -

- (NSNumber *)parseInteger: (NSString *)value {
    return @([value integerValue]);
}

- (NSNumber *)parseDouble: (NSString *)value {
    return @([value doubleValue]);
}

- (NSNumber *)parseBoolean: (NSString *)value {
    if ([value isEqualToString: @"1"]) {
        return @YES;
    }
    
    return @NO;
}

- (NSString *)parseString: (NSString *)value {
    return [value stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
}

- (NSDate *)parseDate: (NSString *)value {
    NSDate *result = nil;
    
    result = [self parseDateString: value withFormat: @"yyyyMMdd'T'HH:mm:ss"];
    
    if (!result) {
        result = [self parseDateString: value withFormat: @"yyyy'-'MM'-'dd'T'HH:mm:ss"];
    }
    
    return result;
}

- (NSData *)parseData: (NSString *)value {
	// Convert the base 64 encoded data into a string
	NSData *base64Data = [value dataUsingEncoding:NSASCIIStringEncoding];
	NSData *decodedData = [base64Data xmpp_base64Decoded];
	
    return decodedData;
}

	
@end
