//
//  PlaybackEventController.m
//  Cog
//
//  Created by Vincent Spader on 3/5/09.
//  Copyright 2009 __MyCompanyName__. All rights reserved.

#import "PlaybackEventController.h"

#import "AudioScrobbler.h"

#import "PlaylistEntry.h"

NSString *TrackNotification = @"com.apple.iTunes.playerInfo";

NSString *TrackArtist = @"Artist";
NSString *TrackAlbum = @"Album";
NSString *TrackTitle = @"Name";
NSString *TrackGenre = @"Genre";
NSString *TrackNumber = @"Track Number";
NSString *TrackLength = @"Total Time";
NSString *TrackPath = @"Location";
NSString *TrackState = @"Player State";

typedef NS_ENUM(NSInteger, TrackStatus) { TrackPlaying,
	                                      TrackPaused,
	                                      TrackStopped };

@implementation PlaybackEventController {
	AudioScrobbler *scrobbler;

	NSOperationQueue *queue;

	PlaylistEntry *entry;

	Boolean didGainUN API_AVAILABLE(macosx(10.14));
}

- (void)initDefaults {
	NSDictionary *defaultsDictionary = @{
		@"enableAudioScrobbler": @YES,
		@"automaticallyLaunchLastFM": @NO,
		@"notifications.enable": @YES,
		@"notifications.itunes-style": @YES,
		@"notifications.show-album-art": @YES
	};

	[[NSUserDefaults standardUserDefaults] registerDefaults:defaultsDictionary];
}

- (id)init {
	self = [super init];
	if(self) {
		[self initDefaults];

		didGainUN = NO;

		if(@available(macOS 10.14, *)) {
			UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
			[center
			requestAuthorizationWithOptions:UNAuthorizationOptionAlert
			              completionHandler:^(BOOL granted, NSError *_Nullable error) {
				              self->didGainUN = granted;

				              if(granted) {
					              UNNotificationAction *skipAction = [UNNotificationAction
					              actionWithIdentifier:@"skip"
					                             title:@"Skip"
					                           options:UNNotificationActionOptionNone];

					              UNNotificationCategory *playCategory = [UNNotificationCategory
					              categoryWithIdentifier:@"play"
					                             actions:@[skipAction]
					                   intentIdentifiers:@[]
					                             options:UNNotificationCategoryOptionNone];

					              [center setNotificationCategories:
					                      [NSSet setWithObject:playCategory]];
				              }
			              }];

			[center setDelegate:self];
		}

		queue = [[NSOperationQueue alloc] init];
		[queue setMaxConcurrentOperationCount:1];

		scrobbler = [[AudioScrobbler alloc] init];
		[[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self];

		entry = nil;
	}

	return self;
}

- (void)userNotificationCenter:(UNUserNotificationCenter *)center
       willPresentNotification:(UNNotification *)notification
         withCompletionHandler:
         (void (^)(UNNotificationPresentationOptions options))completionHandler
API_AVAILABLE(macos(10.14)) {
	UNNotificationPresentationOptions presentationOptions = UNNotificationPresentationOptionAlert;

	completionHandler(presentationOptions);
}

- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
         withCompletionHandler:(void (^)(void))completionHandler API_AVAILABLE(macos(10.14)) {
	if([[response actionIdentifier] isEqualToString:@"skip"]) {
		[playbackController next:self];
	}
}

- (NSDictionary *)fillNotificationDictionary:(PlaylistEntry *)pe status:(TrackStatus)status {
	NSMutableDictionary *dict = [NSMutableDictionary dictionary];
	if(pe == nil || pe.deLeted || pe.url == nil) return dict;

	[dict setObject:[pe.url absoluteString] forKey:TrackPath];
	if(pe.title) [dict setObject:pe.title forKey:TrackTitle];
	if(pe.artist) [dict setObject:pe.artist forKey:TrackArtist];
	if(pe.album) [dict setObject:pe.album forKey:TrackAlbum];
	if(pe.genre) [dict setObject:pe.genre forKey:TrackGenre];
	if(pe.track)
		[dict setObject:pe.trackText forKey:TrackNumber];
	if(pe.length)
		[dict setObject:@((NSInteger)([pe.length doubleValue] * 1000.0))
		         forKey:TrackLength];

	NSString *state = nil;

	switch(status) {
		case TrackPlaying:
			state = @"Playing";
			break;
		case TrackPaused:
			state = @"Paused";
			break;
		case TrackStopped:
			state = @"Stopped";
			break;
		default:
			break;
	}

	[dict setObject:state forKey:TrackState];

	return dict;
}

- (void)performPlaybackDidBeginActions:(PlaylistEntry *)pe {
	if(NO == [pe error]) {
		entry = pe;

		[[NSDistributedNotificationCenter defaultCenter]
		postNotificationName:TrackNotification
		              object:nil
		            userInfo:[self fillNotificationDictionary:pe status:TrackPlaying]
		  deliverImmediately:YES];

		NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

		if([defaults boolForKey:@"notifications.enable"]) {
			if([defaults boolForKey:@"enableAudioScrobbler"]) {
				[scrobbler start:pe];
				if([AudioScrobbler isRunning]) return;
			}

			if(@available(macOS 10.14, *)) {
				if(didGainUN) {
					UNUserNotificationCenter *center =
					[UNUserNotificationCenter currentNotificationCenter];

					UNMutableNotificationContent *content =
					[[UNMutableNotificationContent alloc] init];

					content.title = @"Now Playing";

					NSString *subtitle;
					NSString *artist = (pe.artist && [pe.artist length]) ? pe.artist : nil;
					NSString *album = (pe.album && [pe.album length]) ? pe.album : nil;
					if(artist && album) {
						subtitle = [NSString stringWithFormat:@"%@ - %@", artist, album];
					} else if(artist) {
						subtitle = artist;
					} else if(album) {
						subtitle = album;
					} else {
						subtitle = @"";
					}

					NSString *body = [NSString stringWithFormat:@"%@\n%@", [pe title], subtitle];
					content.body = body;
					content.sound = nil;
					content.categoryIdentifier = @"play";

					if([defaults boolForKey:@"notifications.show-album-art"] &&
					   [pe albumArt]) {
						NSError *error = nil;
						NSFileManager *fileManager = [NSFileManager defaultManager];
						NSURL *tmpSubFolderURL = [[NSURL fileURLWithPath:NSTemporaryDirectory()]
						URLByAppendingPathComponent:@"cog-artworks-cache"
						                isDirectory:true];
						if([fileManager createDirectoryAtPath:[tmpSubFolderURL path]
						          withIntermediateDirectories:true
						                           attributes:nil
						                                error:&error]) {
							NSString *tmpFileName =
							[[NSProcessInfo.processInfo globallyUniqueString]
							stringByAppendingString:@".jpg"];
							NSURL *fileURL =
							[tmpSubFolderURL URLByAppendingPathComponent:tmpFileName];
							NSImage *image = [pe albumArt];
							CGImageRef cgRef = [image CGImageForProposedRect:NULL
							                                         context:nil
							                                           hints:nil];

							if(cgRef) {
								NSBitmapImageRep *newRep =
								[[NSBitmapImageRep alloc] initWithCGImage:cgRef];
								NSData *jpgData = [newRep
								representationUsingType:NSBitmapImageFileTypeJPEG
								             properties:@{ NSImageCompressionFactor: @0.5f }];
								[jpgData writeToURL:fileURL atomically:YES];

								UNNotificationAttachment *icon =
								[UNNotificationAttachment attachmentWithIdentifier:@"art"
								                                               URL:fileURL
								                                           options:nil
								                                             error:&error];
								if(error) {
									// We have size limit of 10MB per image attachment.
									NSLog(@"%@", error.localizedDescription);
								} else {
									content.attachments = @[icon];
								}
							}
						}
					}

					UNNotificationRequest *request =
					[UNNotificationRequest requestWithIdentifier:@"PlayTrack"
					                                     content:content
					                                     trigger:nil];

					[center addNotificationRequest:request
					         withCompletionHandler:^(NSError *_Nullable error) {
						         NSLog(@"%@", error.localizedDescription);
					         }];
				}
			} else {
				NSUserNotification *notif = [[NSUserNotification alloc] init];
				notif.title = [pe title];

				NSString *subtitle;
				NSString *artist = (pe.artist && [pe.artist length]) ? pe.artist : nil;
				NSString *album = (pe.album && [pe.album length]) ? pe.album : nil;
				if(artist && album) {
					subtitle = [NSString stringWithFormat:@"%@ - %@", artist, album];
				} else if(artist) {
					subtitle = artist;
				} else if(album) {
					subtitle = album;
				} else {
					subtitle = @"";
				}

				if([defaults boolForKey:@"notifications.itunes-style"]) {
					notif.subtitle = subtitle;
					[notif setValue:@YES forKey:@"_showsButtons"];
				} else {
					notif.informativeText = subtitle;
				}

				if([notif respondsToSelector:@selector(setContentImage:)]) {
					if([defaults boolForKey:@"notifications.show-album-art"] &&
					   [pe albumArtInternal]) {
						NSImage *image = [pe albumArt];

						if([defaults boolForKey:@"notifications.itunes-style"]) {
							[notif setValue:image forKey:@"_identityImage"];
						} else {
							notif.contentImage = image;
						}
					}
				}

				notif.actionButtonTitle = NSLocalizedString(@"SkipAction", @"");

				[[NSUserNotificationCenter defaultUserNotificationCenter]
				scheduleNotification:notif];
			}
		}
	}
}

- (void)performPlaybackDidPauseActions {
	[[NSDistributedNotificationCenter defaultCenter]
	postNotificationName:TrackNotification
	              object:nil
	            userInfo:[self fillNotificationDictionary:entry status:TrackPaused]
	  deliverImmediately:YES];
	if([[NSUserDefaults standardUserDefaults] boolForKey:@"enableAudioScrobbler"]) {
		[scrobbler pause];
	}
}

- (void)performPlaybackDidResumeActions {
	[[NSDistributedNotificationCenter defaultCenter]
	postNotificationName:TrackNotification
	              object:nil
	            userInfo:[self fillNotificationDictionary:entry status:TrackPlaying]
	  deliverImmediately:YES];
	if([[NSUserDefaults standardUserDefaults] boolForKey:@"enableAudioScrobbler"]) {
		[scrobbler resume];
	}
}

- (void)performPlaybackDidStopActions {
	[[NSDistributedNotificationCenter defaultCenter]
	postNotificationName:TrackNotification
	              object:nil
	            userInfo:[self fillNotificationDictionary:entry status:TrackStopped]
	  deliverImmediately:YES];
	entry = nil;
	if([[NSUserDefaults standardUserDefaults] boolForKey:@"enableAudioScrobbler"]) {
		[scrobbler stop];
	}
}

- (void)awakeFromNib {
	[[NSNotificationCenter defaultCenter] addObserver:self
	                                         selector:@selector(playbackDidBegin:)
	                                             name:CogPlaybackDidBeginNotficiation
	                                           object:nil];
	[[NSNotificationCenter defaultCenter] addObserver:self
	                                         selector:@selector(playbackDidPause:)
	                                             name:CogPlaybackDidPauseNotficiation
	                                           object:nil];
	[[NSNotificationCenter defaultCenter] addObserver:self
	                                         selector:@selector(playbackDidResume:)
	                                             name:CogPlaybackDidResumeNotficiation
	                                           object:nil];
	[[NSNotificationCenter defaultCenter] addObserver:self
	                                         selector:@selector(playbackDidStop:)
	                                             name:CogPlaybackDidStopNotficiation
	                                           object:nil];
}

- (void)playbackDidBegin:(NSNotification *)notification {
	NSOperation *op = [NSBlockOperation blockOperationWithBlock:^{
		[self performPlaybackDidBeginActions:(PlaylistEntry *)[notification object]];
	}];
	[queue addOperation:op];
}

- (void)playbackDidPause:(NSNotification *)notification {
	NSOperation *op = [NSBlockOperation blockOperationWithBlock:^{
		[self performPlaybackDidPauseActions];
	}];
	[queue addOperation:op];
}

- (void)playbackDidResume:(NSNotification *)notification {
	NSOperation *op = [NSBlockOperation blockOperationWithBlock:^{
		[self performPlaybackDidResumeActions];
	}];
	[queue addOperation:op];
}

- (void)playbackDidStop:(NSNotification *)notification {
	NSOperation *op = [NSBlockOperation blockOperationWithBlock:^{
		[self performPlaybackDidStopActions];
	}];
	[queue addOperation:op];
}

- (void)userNotificationCenter:(NSUserNotificationCenter *)center
       didActivateNotification:(NSUserNotification *)notification {
	switch(notification.activationType) {
		case NSUserNotificationActivationTypeActionButtonClicked:
			[playbackController next:self];
			break;

		case NSUserNotificationActivationTypeContentsClicked: {
			NSWindow *window = [[NSUserDefaults standardUserDefaults] boolForKey:@"miniMode"] ? miniWindow : mainWindow;

			[NSApp activateIgnoringOtherApps:YES];
			[window makeKeyAndOrderFront:self];
		}; break;

		default:
			break;
	}
}

@end
