//
//  BSDownloadTask.m
//  BathyScaphe
//
//  Created by Hori,Masaki on 06/08/06.
//  Copyright 2006-2009 BathyScaphe Project. All rights reserved.
//  encoding="UTF-8"
//

#import "BSDownloadTask.h"

NSString *const BSDownloadTaskFinishDownloadNotification = @"BSDownloadTaskFinishDownloadNotification";
NSString *const BSDownloadTaskReceiveResponseNotification = @"BSDownloadTaskReceiveResponseNotification";
NSString *const BSDownloadTaskCanceledNotification = @"BSDownloadTaskCanceledNotification";
NSString *const BSDownloadTaskInternalErrorNotification = @"BSDownloadTaskInternalErrorNotification";
NSString *const BSDownloadTaskAbortDownloadNotification = @"BSDownloadTaskAbortDownloadNotification";
NSString *const BSDownloadTaskServerResponseKey = @"BSDownloadTaskServerResponseKey"; // NSURLResponse
NSString *const BSDownloadTaskStatusCodeKey = @"BSDownloadTaskStatusCodeKey"; // NSNumber (int)
NSString *const BSDownloadTaskFailDownloadNotification = @"BSDownloadTaskFailDownloadNotification";
NSString *const BSDownloadTaskErrorObjectKey = @"BSDownloadTaskErrorObjectKey"; // NSError


@implementation BSDownloadTask
+ (id)taskWithURL:(NSURL *)url
{
	return [[[self alloc] initWithURL:url] autorelease];
}

- (id) initWithURL:(NSURL *)url
{
	if(self = [super init]) {
		[self setURL:url];
		[self setIsInProgress:YES];
        m_contLengthIsUnknown = YES;
	}
	
	return self;
}

+ (id)taskWithURL:(NSURL *)url method:(NSString *)method
{
	return [[[self alloc] initWithURL:url method:method] autorelease];
}

- (id)initWithURL:(NSURL *)url method:(NSString *)inMethod
{
	if(self = [self initWithURL:url]) {
		method = [inMethod retain];
	}
	
	return self;
}

- (void)dealloc
{
	[self setURL:nil];
	[con release];
	[receivedData release];
	[method release];
	[_response release];
	
	[super dealloc];
}

#pragma mark Accessors
- (void)setURL:(NSURL *)url
{
	id temp = m_targetURL;
	m_targetURL = [url retain];
	[temp release];
}

- (NSURL *)url
{
	return m_targetURL;
}

- (void)setCurrentLength:(CGFloat)doubleValue
{
	m_currentLength = doubleValue;
}

- (CGFloat)currentLength
{
	return m_currentLength;
}

- (void)setContLength:(CGFloat)i
{
	m_contLength = i;
}

- (CGFloat)contLength
{
	return m_contLength;
}

- (NSData *)receivedData
{
	return receivedData;
}

- (void)setResponse:(id)response
{
	id temp = _response;
	_response = [response retain];
	[temp release];
}

- (id)response
{
	return _response;
}

#pragma mark Overrides
- (void)doExecuteWithLayout:(CMRThreadLayout *)layout
{
	NSRunLoop *loop = [NSRunLoop currentRunLoop];
		
	[receivedData release];
	receivedData = nil;
	[self setCurrentLength:0];
	[self setContLength:0];
	[self setAmount:-1];
    [self setMessage:[NSString stringWithFormat:NSLocalizedStringFromTable(@"Download url(%@)", @"Downloader", @""), [[self url] absoluteString]]];

	NSMutableURLRequest *request;
	
	request = [NSMutableURLRequest requestWithURL:[self url]];
	if (!request) {
		[self postNotificationWithName:BSDownloadTaskInternalErrorNotification userInfo:nil];
		return;
	}
	[request setValue:[NSBundle monazillaUserAgent] forHTTPHeaderField:@"User-Agent"];
	if (method) {
		[request setHTTPMethod:method];
	}

	con = [[NSURLConnection alloc] initWithRequest:request delegate:self];
	if (!con) {
		[self postNotificationWithName:BSDownloadTaskInternalErrorNotification userInfo:nil];
		return;
	}
	
	while ([self isInProgress]) {
		id pool = [[NSAutoreleasePool alloc] init];
		@try {
			[loop runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
		}
		@catch(id ex) {
			// do nothing.
			@throw;
		}
		@finally {
			[pool release];
		}
	}
}

#pragma mark CMRTask
- (IBAction)cancel:(id)sender
{
	[con cancel];
	[self postNotificationWithName:BSDownloadTaskCanceledNotification userInfo:nil];

	[super cancel:sender];
}

- (id)identifier
{
	return [NSString stringWithFormat:@"%@-%p", self, self];
}

- (NSString *)title
{
	return NSLocalizedStringFromTable(@"Download.", @"Downloader", @"");
}

- (double)amount
{
	return m_taskAmount;
}

- (void)setAmount:(double)doubleValue
{
	m_taskAmount = doubleValue;
}
@end


@implementation BSDownloadTask(NSURLConnectionDelegate)
- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response;
{
	// Leopard
	if (!response) {
		return request;
	}
	[self setResponse:response];
	[self postNotificaionWithResponse:response];
	[connection cancel];
	return nil;
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
	BOOL disconnect = NO;
	
	[self setResponse:response];
    if ([[NSUserDefaults standardUserDefaults] boolForKey:BSUserDebugEnabledKey]) {
        NSLog(@"** USER DEBUG **\n%@", [(NSHTTPURLResponse *)response allHeaderFields]);
	}
	switch ([(NSHTTPURLResponse *)response statusCode]) {
		case 200:
		case 206:
			break;
		case 304:
			NSLog(@"Content is not modified.");
			disconnect = YES;
			break;
		case 404:
			NSLog(@"Content has not found.");
			disconnect = YES;
			break;
		case 416:
			NSLog(@"Range is mismatch.");
			disconnect = YES;
			break;
		default:
			NSLog(@"Unknown error.");
			disconnect = YES;
			break;
	}
	if (disconnect) {
		[connection cancel];
		[self postNotificaionWithResponse:response];
		
		return;
	}
	
//	[self postNotificaionWithResponseDontFinish:response]; // 2009-03-24 今は誰も受け取っていないので発行を休止中。
    CGFloat length = [response expectedContentLength];
    if (length <= 0) {
        CGFloat assumedLength = [[[(NSHTTPURLResponse *)response allHeaderFields] objectForKey:@"Content-Length"] doubleValue];
        if (assumedLength > 0) {
            [self setContLength:assumedLength*1.7]; // gzip 圧縮を考慮、適当
            m_contLengthIsUnknown = NO;
        }
    } else {
        m_contLengthIsUnknown = NO;
        [self setContLength:length];
    }
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
	if (!receivedData) {
		receivedData = [[NSMutableData alloc] init];
	}

	if (!receivedData) {
		// abort
		[connection cancel];
		[self postNotificationWithName:BSDownloadTaskInternalErrorNotification userInfo:nil];
		
		return;
	}
	
	[receivedData appendData:data];
	[self setCurrentLength:[receivedData length]];

	if (!m_contLengthIsUnknown && ([self contLength] > 0)) {
		CGFloat bar = [self currentLength]/[self contLength]*100.0;
		[self setAmount:bar];
		[self setMessage:[NSString stringWithFormat:NSLocalizedStringFromTable(@"Download url(%@) (%.0fk of %.0fk)", @"Downloader", @""),
									  [[self url] absoluteString], (CGFloat)[self currentLength]/1024, (CGFloat)[self contLength]/1024]];
    } else {
        [self setMessage:[NSString stringWithFormat:NSLocalizedStringFromTable(@"Download url(%@) (%.0fk)", @"Downloader", @""),
                                    [[self url] absoluteString], (CGFloat)[self currentLength]/1024]];
    }
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
	[self postNotificationWithName:BSDownloadTaskFinishDownloadNotification userInfo:nil];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
	// abort
    NSDictionary *userInfo = [NSDictionary dictionaryWithObject:error forKey:BSDownloadTaskErrorObjectKey];
    [self postNotificationWithName:BSDownloadTaskFailDownloadNotification userInfo:userInfo];
	[self setIsInProgress:NO];
}
@end


@implementation BSDownloadTask(TaskNotification)
- (void)postNotificationWithName:(NSString *)name userInfo:(NSDictionary *)info
{
	[[NSNotificationCenter defaultCenter] postNotificationName:name object:self userInfo:info];
	[self setIsInProgress:NO];
}

- (void)postNotificaionWithResponse:(NSURLResponse *)response
{
	NSDictionary			*info;
	
	info = [NSDictionary dictionaryWithObjectsAndKeys:response, BSDownloadTaskServerResponseKey,
					[NSNumber numberWithInteger:[(NSHTTPURLResponse *)response statusCode]], BSDownloadTaskStatusCodeKey,
					NULL];
    [self postNotificationWithName:BSDownloadTaskAbortDownloadNotification userInfo:info];
}

- (void)postNotificaionWithResponseDontFinish:(NSURLResponse *)response
{
	NSDictionary			*info;
	
	info = [NSDictionary dictionaryWithObjectsAndKeys:response, BSDownloadTaskServerResponseKey,
					[NSNumber numberWithInteger:[(NSHTTPURLResponse *)response statusCode]], BSDownloadTaskStatusCodeKey,
					NULL];
    [self postNotificationWithName:BSDownloadTaskReceiveResponseNotification userInfo:info];
}
@end
