#import "DownloadWindowController.h"
#import "sw_update.h"
#import <ns/ns.h>
#import <OakAppKit/OakAppKit.h>
#import <OakAppKit/NSImage Additions.h>
#import <OakAppKit/OakSound.h>
#import <OakAppKit/OakImage.h>
#import <OakFoundation/NSString Additions.h>
#import <oak/debug.h>

OAK_DEBUG_VAR(SoftwareUpdate_Download);

@interface DownloadWindowController ()
- (void)notifyUserAboutUpdate;

@property (nonatomic, retain) NSTimer* progressTimer;
@property (nonatomic, assign) BOOL isDownloading;
@property (nonatomic, assign) BOOL showUpdateBadge;
@property (retain) NSString* url;
@property (retain) NSString* archive;
@end

@implementation DownloadWindowController
@synthesize versionOfDownload, progressTimer, activityText, progress, statusText, isDownloading, canInstall, isInstalling, showUpdateBadge;
@synthesize url, archive;

- (id)initWithURL:(NSString*)aURL displayString:(NSString*)aDisplayString keyChain:(key_chain_t const&)aKeyChain
{
	D(DBF_SoftwareUpdate_Download, bug("%s\n", [aURL UTF8String]););
	if(self = [super initWithWindowNibName:@"DownloadProgress"])
	{
		sharedState.reset(new shared_state_t);
		downloadStartDate = [NSDate new];
		secondsLeft       = CGFLOAT_MAX;

		self.activityText  = aDisplayString;
		self.progress      = 0;
		self.statusText    = @"";
		self.isDownloading = YES;
		self.progressTimer = [NSTimer scheduledTimerWithTimeInterval:0.04 target:self selector:@selector(updateProgress:) userInfo:nil repeats:YES];

		self.url = aURL;
		keyChain = aKeyChain;
		[self performSelectorInBackground:@selector(performBackgroundDownload:) withObject:self];
	}
	return self;
}

- (void)performBackgroundDownload:(id)sender
{
	NSAutoreleasePool* pool = [NSAutoreleasePool new];

	shared_state_ptr state = sharedState;
	std::string error = NULL_STR;
	std::string path = sw_update::download_update(to_s(self.url), keyChain, &error, &state->progress, &state->stop);

	NSDictionary* arg = [NSDictionary dictionary];
	if(path != NULL_STR)
		arg = @{ @"path" : [NSString stringWithCxxString:path] };
	else if(error != NULL_STR)
		arg = @{ @"error" : [NSString stringWithCxxString:error] };
	[self performSelectorOnMainThread:@selector(didPerformBackgroundDownload:) withObject:arg waitUntilDone:NO];

	[pool drain];
}

- (void)updateProgress:(NSTimer*)aTimer
{
	self.progress = sharedState->progress;

	NSTimeInterval secondsElapsed = -[downloadStartDate timeIntervalSinceNow];
	if(secondsElapsed < 1.0 || self.progress < 0.01)
		return;

	NSTimeInterval left = secondsElapsed / self.progress - secondsElapsed;
	if(left < 2.6)
	{
		self.statusText = @"Time remaining: a few seconds";
	}
	else
	{
		NSTimeInterval roundedSecondsLeft = 5 * round(left / 5);
		if(roundedSecondsLeft < secondsLeft || roundedSecondsLeft - secondsLeft > 10)
		{
			NSTimeInterval const kMinute = 60;
			NSTimeInterval const kHour   = 60*60;

			if(roundedSecondsLeft < kMinute)
				self.statusText = [NSString stringWithFormat:@"Time remaining: about %.0f seconds", roundedSecondsLeft];
			else if(roundedSecondsLeft < 2*kMinute)
				self.statusText = [NSString stringWithFormat:@"Time remaining: a few minutes"];
			else if(roundedSecondsLeft < kHour)
				self.statusText = [NSString stringWithFormat:@"Time remaining: about %.0f minutes", roundedSecondsLeft / kMinute];
			else
				self.statusText = [NSString stringWithFormat:@"Time remaining: hours"];
			secondsLeft = roundedSecondsLeft;
		}
	}
}

- (void)didPerformBackgroundDownload:(NSDictionary*)info
{
	[self.progressTimer invalidate];
	self.progressTimer = nil;
	self.progress      = 1;
	self.statusText    = @"";
	self.isDownloading = NO;

	if(sharedState->stop)
		return;

	if(NSString* path = [info objectForKey:@"path"])
	{
		self.archive      = path;
		self.activityText = @"Download Completed";
		self.canInstall   = YES;

		if([NSApp isActive])
			OakPlayUISound(OakSoundDidCompleteSomethingUISound);

		[self notifyUserAboutUpdate];
	}
	else
	{
		NSString* error = [info objectForKey:@"error"];
		self.activityText = [NSString stringWithFormat:@"Failed: %@", error];
	}
}

- (void)dealloc
{
	D(DBF_SoftwareUpdate_Download, bug("\n"););
	ASSERT(!progressTimer);

	self.url     = nil;
	self.archive = nil;

	[activityText release];
	[statusText release];
	[downloadStartDate release];

	[super dealloc];
}

+ (NSSet*)keyPathsForValuesAffectingIsWorking { return [NSSet setWithObjects:@"isDownloading", @"isInstalling", nil]; }

- (BOOL)isWorking
{
	return isDownloading || isInstalling;
}

- (void)windowDidLoad
{
	self.window.hidesOnDeactivate = YES;
	self.window.level             = NSStatusWindowLevel;
}

- (BOOL)isVisible
{
	return [self isWindowLoaded] && [[self window] isVisible];
}

- (void)setShowUpdateBadge:(BOOL)flag
{
	D(DBF_SoftwareUpdate_Download, bug("%s\n", BSTR(flag)););
	if(self.showUpdateBadge == flag)
		return;

	if(showUpdateBadge = flag)
	{
		D(DBF_SoftwareUpdate_Download, bug("alter application icon\n"););
		NSImage* appIcon = [NSApp applicationIconImage];
		NSImage* dlBadge = [[[NSImage imageNamed:@"Update Badge" inSameBundleAsClass:[self class]] copy] autorelease];
		[dlBadge setSize:NSMakeSize(appIcon.size.width / 4, appIcon.size.height / 4)];
		[NSApp setApplicationIconImage:[OakImage imageWithBase:appIcon badge:dlBadge edge:CGRectMaxXEdge]];
	}
	else
	{
		[NSApp setApplicationIconImage:nil];
	}
}

- (void)notifyUserAboutUpdate
{
	self.showUpdateBadge = YES;
	[NSApp requestUserAttention:NSInformationalRequest];
}

- (IBAction)install:(id)sender
{
	D(DBF_SoftwareUpdate_Download, bug("\n"););

	self.activityText = [NSString stringWithFormat:@"Installing %@…", versionOfDownload ?: @"app"];
	self.isInstalling = YES;

	std::string err = sw_update::install_update(to_s(self.archive));
	if(err == NULL_STR)
	{
		self.activityText = @"Relaunching…";
		oak::application_t::relaunch();
	}
	else
	{
		self.activityText = [NSString stringWithCxxString:err];
		OakRunIOAlertPanel("%s", err.c_str());
	}
}

- (IBAction)cancel:(id)sender
{
	D(DBF_SoftwareUpdate_Download, bug("\n"););
	[[self window] performClose:self];
}

- (void)windowWillClose:(NSNotification*)aNotification
{
	D(DBF_SoftwareUpdate_Download, bug("\n"););
	[self.progressTimer invalidate];
	self.progressTimer = nil;
	self.isDownloading = NO;
	self.showUpdateBadge = NO;

	sharedState->stop = true;
	[self release];
}
@end
