
#import "MultipartMessageHeaderField.h"
#import "HTTPLogging.h"

//-----------------------------------------------------------------
#pragma mark log level

#ifdef DEBUG
static const int httpLogLevel = HTTP_LOG_LEVEL_WARN;
#else
static const int httpLogLevel = HTTP_LOG_LEVEL_WARN;
#endif


// helpers
int findChar(const char* str,NSUInteger length, char c);
NSString* extractParamValue(const char* bytes, NSUInteger length, NSStringEncoding encoding);

//-----------------------------------------------------------------
// interface MultipartMessageHeaderField (private)
//-----------------------------------------------------------------


@interface MultipartMessageHeaderField (private)
-(BOOL) parseHeaderValueBytes:(char*) bytes length:(NSUInteger) length encoding:(NSStringEncoding) encoding;
@end


//-----------------------------------------------------------------
// implementation MultipartMessageHeaderField
//-----------------------------------------------------------------

@implementation MultipartMessageHeaderField
@synthesize name,value,params;

- (id) initWithData:(NSData *)data contentEncoding:(NSStringEncoding)encoding {
    params = [[NSMutableDictionary alloc] initWithCapacity:1];

    char* bytes = (char*)data.bytes;
    NSUInteger length = data.length;

    int separatorOffset = findChar(bytes, length, ':');
    if( (-1 == separatorOffset) || (separatorOffset >= length-2) ) {
        HTTPLogError(@"MultipartFormDataParser: Bad format.No colon in field header.");
        // tear down
        return nil;
    }
    
    // header name is always ascii encoded;
    name = [[NSString alloc] initWithBytes: bytes length: separatorOffset encoding: NSASCIIStringEncoding];
    if( nil == name ) {
        HTTPLogError(@"MultipartFormDataParser: Bad MIME header name.");
        // tear down
        return nil;     
    }
    
    // skip the separator and the next ' ' symbol
    bytes += separatorOffset + 2;
    length -= separatorOffset + 2;

    separatorOffset = findChar(bytes, length, ';');
    if( separatorOffset == -1 ) {
        // couldn't find ';', means we don't have extra params here. 
        value = [[NSString alloc] initWithBytes:bytes length: length encoding:encoding];

        if( nil == value ) {
            HTTPLogError(@"MultipartFormDataParser: Bad MIME header value for header name: '%@'",name);
            // tear down
            return nil;     
        }
        return self;
    }
    
    value = [[NSString alloc] initWithBytes:bytes length: separatorOffset encoding:encoding];
    HTTPLogVerbose(@"MultipartFormDataParser: Processing  header field '%@' : '%@'",name,value);
    // skipe the separator and the next ' ' symbol
    bytes += separatorOffset + 2;
    length -= separatorOffset + 2;

    // parse the "params" part of the header
    if( ![self parseHeaderValueBytes:bytes length:length encoding:encoding] ) {
        NSString* paramsStr = [[NSString alloc] initWithBytes:bytes length:length encoding:NSASCIIStringEncoding];
        HTTPLogError(@"MultipartFormDataParser: Bad params for header with name '%@' and value '%@'",name,value);
        HTTPLogError(@"MultipartFormDataParser: Params str: %@",paramsStr);

        return nil;     
    }
    return self;
}

-(BOOL) parseHeaderValueBytes:(char*) bytes length:(NSUInteger) length encoding:(NSStringEncoding) encoding {
    int offset = 0;
    NSString* currentParam = nil;
    BOOL insideQuote = NO;
    while( offset < length ) {
        if( bytes[offset] == '\"' ) {
            if( !offset || bytes[offset-1] != '\\' ) {
               insideQuote = !insideQuote;
            }
        }

        // skip quoted symbols
        if( insideQuote ) {
            ++ offset;
            continue; 
        }
        if( bytes[offset] == '=' ) {
            if( currentParam ) {
                // found '=' before terminating previous param.
                return NO;
            }
            currentParam = [[NSString alloc] initWithBytes:bytes length:offset encoding:NSASCIIStringEncoding];

            bytes+=offset + 1;
            length -= offset + 1;
            offset = 0;
            continue;
        }
        if( bytes[offset] == ';' ) {
            if( !currentParam ) {
                // found ; before stating '='.
                HTTPLogError(@"MultipartFormDataParser: Unexpected ';' when parsing header");
                return NO;
            }
            NSString* paramValue = extractParamValue(bytes, offset,encoding);
             if( nil == paramValue ) {
                HTTPLogWarn(@"MultipartFormDataParser: Failed to exctract paramValue for key %@ in header %@",currentParam,name);
            }
            else {
#ifdef DEBUG
                if( [params objectForKey:currentParam] ) {
                    HTTPLogWarn(@"MultipartFormDataParser: param %@ mentioned more then once in header %@",currentParam,name);
                }
#endif
                [params setObject:paramValue forKey:currentParam];
                HTTPLogVerbose(@"MultipartFormDataParser: header param: %@ = %@",currentParam,paramValue);
            }

            currentParam = nil;

            // ';' separator has ' ' following, skip them.
            bytes+=offset + 2;
            length -= offset + 2;
            offset = 0;
        }
        ++ offset;
    }

    // add last param
    if( insideQuote ) {
        HTTPLogWarn(@"MultipartFormDataParser: unterminated quote in header %@",name);
//      return YES;
    }
    if( currentParam ) {
        NSString* paramValue = extractParamValue(bytes, length, encoding);

        if( nil == paramValue ) {
            HTTPLogError(@"MultipartFormDataParser: Failed to exctract paramValue for key %@ in header %@",currentParam,name);
        }

#ifdef DEBUG
        if( [params objectForKey:currentParam] ) {
            HTTPLogWarn(@"MultipartFormDataParser: param %@ mentioned more then once in one header",currentParam);
        }
#endif
        [params setObject:paramValue forKey:currentParam];
        HTTPLogVerbose(@"MultipartFormDataParser: header param: %@ = %@",currentParam,paramValue);
        currentParam = nil;
    }
    
    return YES;
}

- (NSString *)description {
    return [NSString stringWithFormat:@"%@:%@\n params: %@",name,value,params];
}

@end

int findChar(const char* str, NSUInteger length, char c) {
    int offset = 0;
    while( offset < length ) {
        if( str[offset] == c )
            return offset;
        ++ offset;
    }
    return -1;
}

NSString* extractParamValue(const char* bytes, NSUInteger length, NSStringEncoding encoding) {
    if( !length ) 
        return nil;
    NSMutableString* value = nil;
    
    if( bytes[0] == '"' ) {
        // values may be quoted. Strip the quotes to get what we need.
        value = [[NSMutableString alloc] initWithBytes:bytes + 1 length: length - 2 encoding:encoding]; 
    }
    else {
        value = [[NSMutableString alloc] initWithBytes:bytes length: length encoding:encoding];
    }
    // restore escaped symbols
    NSRange range= [value rangeOfString:@"\\"];
    while ( range.length ) {
        [value deleteCharactersInRange:range];
        range.location ++;
        range = [value rangeOfString:@"\\" options:NSLiteralSearch range: range];
    }
    return value;
}

