
#import "RPMHTTPConnection.h"

@interface RPMHTTPConnection ()
- (void)finished;
@property (readonly) NSHTTPURLResponse *response;
@property (readonly) NSMutableData     *data;
@property (readonly) NSError           *error;
@end


@implementation RPMHTTPConnection

@synthesize response                = _response;
@synthesize data                    = _data;
@synthesize error                   = _error;
@synthesize ignoreCertificateErrors = _ignoreCertificateErrors;
@synthesize userInfo                = _userInfo;

- (void)dealloc
{
    if (_connection)
    {
        [_connection release];
    }
    
    if (_response)
    {
        [_response release];
    }
    
    if (_data)
    {
        [_data release];
    }
    
    if (_error)
    {
        [_error release];
    }
    
    if (_completionBlock)
    {
        [_completionBlock release];
    }
	
	[super dealloc];
}

+ (RPMHTTPResponse *)sendSynchronousRequest:(NSURLRequest *)request
{
    RPMHTTPConnection *connection = [[RPMHTTPConnection alloc] initWithRequest:request delegate:nil];
    
    [connection waitUntilCompletion];
    
    RPMHTTPResponse *response = [RPMHTTPResponse responseWithResponse:connection.response
                                                                 data:connection.data
                                                                error:connection.error];
    
    [connection release];
    
    return response;
}

- (id)initWithRequest:(NSURLRequest *)request delegate:(id <RPMHTTPConnectionDelegate>)delegate
{
    self = [super init];
    
    if (self)
    {
        _delegate = delegate;
        _loadThread = [NSThread currentThread];
        _isLoading = YES;
        _connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    }
    
    return self;
}

#if NS_BLOCKS_AVAILABLE
- (id)initWithRequest:(NSURLRequest *)request andCompletionBlock:(void (^)(RPMHTTPResponse *response))completionBlock
{
    self = [super init];
    
    if (self)
    {
        if (completionBlock)
        {
            _completionBlock = [completionBlock copy];
        }
        
        _loadThread = [NSThread currentThread];
        _isLoading = YES;
        _connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
	}
	
	return self;
}
#endif

- (void)finished
{
    if (_isLoading)
    {
        _isLoading = NO;
        
        RPMHTTPResponse *response = nil;
        
        if (!_wasCancelled)
        {
            response = [RPMHTTPResponse responseWithResponse:_response
                                                        data:_data
                                                       error:_error];
            
            if ([_delegate respondsToSelector:@selector(connection:finishedWithResponse:)])
            {
                [_delegate connection:self finishedWithResponse:response];
            }
            else
            {
#if NS_BLOCKS_AVAILABLE
                if (_completionBlock)
                {
                    _completionBlock(response);
                }
#endif
            }
        }
        
#if NS_BLOCKS_AVAILABLE
        //--------------------------------------------------
        // Release this as soon as possible.
        //--------------------------------------------------
        if (_completionBlock)
        {
            [_completionBlock release];
            _completionBlock = nil;
        }
#endif
    }
}

- (void)waitUntilCompletion
{
	NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
	
	while (_isLoading)
    {
		[runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
	}
}

- (void)cancel
{
    if ([NSThread currentThread] == _loadThread)
    {
        if (_isLoading)
        {
            [_connection cancel];
            _wasCancelled = YES;
            [self finished];
        }
    }
    else if (_loadThread)
    {
        [self performSelector:_cmd onThread:_loadThread withObject:nil waitUntilDone:NO];
    }
}

#pragma mark -
#pragma mark NSURLConnection Delegate Methods

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    //--------------------------------------------------
    // According to the docs this callback may
    // potentially be called more than once so release
    // everything that has been received so far.
    //--------------------------------------------------
    if (_response)
    {
        [_response release];
        _response = nil;
    }
    
    if (_data)
    {
        [_data release];
        _data = nil;
    }
    
    _response = [(NSHTTPURLResponse *)response retain];
    _data     = [[NSMutableData alloc] init];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    [_data appendData:data];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    if (_data)
    {
        [_data release];
        _data = nil;
    }
    
    _error = [error retain];
    
    [self finished];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    [self finished];
}

#pragma mark - NSURLConnection authentication

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
    if (self.ignoreCertificateErrors)
    {
        [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
        [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
    }
    else
    {
        [challenge.sender performDefaultHandlingForAuthenticationChallenge:challenge];
    }
}

@end


@implementation RPMHTTPResponse

@synthesize response = _response;
@synthesize data     = _data;
@synthesize error    = _error;

- (void)dealloc
{
    [_response release];
    [_data release];
    [_error release];
	
	[super dealloc];
}

- (id)initWithResponse:(NSHTTPURLResponse *)response
                  data:(NSData *)data
                 error:(NSError *)error
{
    self = [super init];
    
    if (self)
    {
        _response = [response retain];
        _data     = [data retain];
        _error    = [error retain];
    }
    
    return self;
}

+ (id)responseWithResponse:(NSHTTPURLResponse *)response
                      data:(NSData *)data
                     error:(NSError *)error
{
    return [[[RPMHTTPResponse alloc] initWithResponse:response data:data error:error] autorelease];
}

- (NSInteger)statusCode
{
    return [_response statusCode];
}

- (NSString *)localizedStatusCode
{
	return [NSHTTPURLResponse localizedStringForStatusCode:[self statusCode]];
}

- (NSInteger)errorCode
{
    return [_error code];
}

- (NSString *)MIMEType
{
	return [_response MIMEType];
}

@end
