/*  HBAppDelegate.m $

 This file is part of the HandBrake source code.
 Homepage: <http://handbrake.fr/>.
 It may be used under the terms of the GNU General Public License. */

#import "HBAppDelegate.h"

#import "HBUtilities.h"
#import "HBPresetsManager.h"
#import "HBPreset.h"

#import "HBPreferencesController.h"
#import "HBQueueController.h"
#import "HBOutputPanelController.h"
#import "HBController.h"

@import HandBrakeKit;

#define PRESET_FILE @"UserPresets.json"
#define QUEUE_FILE @"Queue.hbqueue"

@interface HBAppDelegate ()

@property (nonatomic, strong) HBPresetsManager *presetsManager;
@property (unsafe_unretained) IBOutlet NSMenu *presetsMenu;

@property (nonatomic, strong) HBPreferencesController *preferencesController;
@property (nonatomic, strong) HBQueueController *queueController;

@property (nonatomic, strong) HBOutputPanelController *outputPanel;

@property (nonatomic, strong) HBController *mainController;

@end

@implementation HBAppDelegate

- (instancetype)init
{
    self = [super init];
    if (self)
    {
        // Register the default preferences
        [HBPreferencesController registerUserDefaults];

        [HBCore initGlobal];
        [HBCore registerErrorHandler:^(NSString *error) {
            fprintf(stderr, "error: %s\n", error.UTF8String);
        }];
        [HBCore setDVDNav:[[[NSUserDefaults standardUserDefaults] objectForKey:@"UseDvdNav"] boolValue]];

        _outputPanel = [[HBOutputPanelController alloc] init];

        // we init the HBPresetsManager
        NSURL *appSupportURL = [HBUtilities appSupportURL];
        _presetsManager = [[HBPresetsManager alloc] initWithURL:[appSupportURL URLByAppendingPathComponent:PRESET_FILE]];

        // Queue
        _queueController = [[HBQueueController alloc] initWithURL:[appSupportURL URLByAppendingPathComponent:QUEUE_FILE]];
        _queueController.delegate = self;
        _mainController = [[HBController alloc] initWithQueue:_queueController presetsManager:_presetsManager];

        // Set the Growl Delegate
        [GrowlApplicationBridge setGrowlDelegate:_queueController];
    }
    return self;
}

#pragma mark - NSApplicationDelegate

- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag
{
    if (!flag)
    {
        [self.mainController showWindow:nil];
    }
    return YES;
}

- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
    [self buildPresetsMenu];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(buildPresetsMenu) name:HBPresetsChangedNotification object:nil];

    // Get the number of HandBrake instances currently running
    NSUInteger instances = [NSRunningApplication runningApplicationsWithBundleIdentifier:[[NSBundle mainBundle] bundleIdentifier]].count;

    // Open debug output window now if it was visible when HB was closed
    if ([[NSUserDefaults standardUserDefaults] boolForKey:@"OutputPanelIsOpen"])
        [self showOutputPanel:nil];

    // On Screen Notification
    // We check to see if there is already another instance of hb running.
    if (instances > 1)
    {
        NSAlert *alert = [[NSAlert alloc] init];
        [alert setMessageText:NSLocalizedString(@"There is already an instance of HandBrake running.", nil)];
        [alert setInformativeText:NSLocalizedString(@"The queue will be shared between the instances.", nil)];
        [alert runModal];
    }
    else
    {
        [self.queueController setEncodingJobsAsPending];
        [self.queueController removeCompletedJobs];
    }

    // Now we re-check the queue array to see if there are
    // any remaining encodes to be done
    if (self.queueController.count)
    {
        [self showMainWindow:self];
        [self showQueueWindow:self];
    }
    else
    {
        // Open queue window now if it was visible when HB was closed
        if ([[NSUserDefaults standardUserDefaults] boolForKey:@"QueueWindowIsOpen"])
            [self showQueueWindow:nil];

        [self showMainWindow:self];
        [self.mainController launchAction];
    }

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
        // Remove encodes logs older than a month
        if ([[NSUserDefaults standardUserDefaults] boolForKey:@"HBClearOldLogs"])
        {
            [self cleanEncodeLogs];
        }

        // If we are a single instance it is safe to clean up the previews if there are any
        // left over. This is a bit of a kludge but will prevent a build up of old instance
        // live preview cruft. No danger of removing an active preview directory since they
        // are created later in HBPreviewController if they don't exist at the moment a live
        // preview encode is initiated.
        if (instances == 1)
        {
            [self cleanPreviews];
        }
    });
}

- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)app
{
    if (self.queueController.core.state != HBStateIdle)
    {
        NSAlert *alert = [[NSAlert alloc] init];
        [alert setMessageText:NSLocalizedString(@"Are you sure you want to quit HandBrake?", nil)];
        [alert setInformativeText:NSLocalizedString(@"If you quit HandBrake your current encode will be reloaded into your queue at next launch. Do you want to quit anyway?", nil)];
        [alert addButtonWithTitle:NSLocalizedString(@"Quit", nil)];
        [alert addButtonWithTitle:NSLocalizedString(@"Don't Quit", nil)];
        [alert.buttons[1] setKeyEquivalent:@"\E"];
        [alert setAlertStyle:NSCriticalAlertStyle];

        NSInteger result = [alert runModal];

        if (result == NSAlertFirstButtonReturn)
        {
            return NSTerminateNow;
        }
        else
        {
            return NSTerminateCancel;
        }
    }

    return NSTerminateNow;
}

- (void)applicationWillTerminate:(NSNotification *)notification
{
    [self.presetsManager savePresets];

    [[NSUserDefaults standardUserDefaults] setBool:_queueController.window.isVisible forKey:@"QueueWindowIsOpen"];
    [[NSUserDefaults standardUserDefaults] setBool:_outputPanel.window.isVisible forKey:@"OutputPanelIsOpen"];

    _mainController = nil;
    _queueController = nil;

    [HBCore closeGlobal];
}

- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames
{
    [self.mainController openURL:[NSURL fileURLWithPath:filenames.firstObject]];
    [NSApp replyToOpenOrPrint:NSApplicationDelegateReplySuccess];
}

- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
{
    SEL action = menuItem.action;

    if (action == @selector(rip:) || action == @selector(pause:))
    {
        // Delegate the validation to the queue controller
        return [self.queueController validateMenuItem:menuItem];
    }
    else if (action == @selector(showAddPresetPanel:) ||
             action == @selector(showPreviewWindow:) ||
             action == @selector(browseSources:))
    {
        // Delegate the validation to the main controller
        return [self.mainController validateMenuItem:menuItem];
    }

    return YES;
}

#pragma mark - Clean ups

/**
 *  Clears the EncodeLogs folder, removes the logs
 *  older than a month.
 */
- (void)cleanEncodeLogs
{
    NSURL *directoryUrl = [[HBUtilities appSupportURL] URLByAppendingPathComponent:@"EncodeLogs"];

    if (directoryUrl)
    {
        NSArray *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:directoryUrl
                                                          includingPropertiesForKeys:nil
                                                                             options:NSDirectoryEnumerationSkipsSubdirectoryDescendants |
                                                                                     NSDirectoryEnumerationSkipsHiddenFiles |
                                                                                     NSDirectoryEnumerationSkipsPackageDescendants
                                                                               error:NULL];

        NSDate *limit = [NSDate dateWithTimeIntervalSinceNow: -(60 * 60 * 24 * 30)];
        NSFileManager *manager = [[NSFileManager alloc] init];

        for (NSURL *fileURL in contents)
        {
            NSDate *creationDate = nil;
            [fileURL getResourceValue:&creationDate forKey:NSURLCreationDateKey error:NULL];
            if ([creationDate isLessThan:limit])
            {
                [manager removeItemAtURL:fileURL error:NULL];
            }
        }
    }
}

- (void)cleanPreviews
{
    NSURL *previewDirectory = [[HBUtilities appSupportURL] URLByAppendingPathComponent:@"Previews"];

    if (previewDirectory)
    {
        NSArray *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:previewDirectory
                                                          includingPropertiesForKeys:nil
                                                                             options:NSDirectoryEnumerationSkipsSubdirectoryDescendants |
                                                                                     NSDirectoryEnumerationSkipsPackageDescendants
                                                                               error:NULL];

        NSFileManager *manager = [[NSFileManager alloc] init];
        for (NSURL *url in contents)
        {
            NSError *error = nil;
            BOOL result = [manager removeItemAtURL:url error:&error];
            if (result == NO && error)
            {
                [HBUtilities writeToActivityLog: "Could not remove existing preview at : %s", url.lastPathComponent.UTF8String];
            }
        }
    }
}

#pragma mark - Menu actions

- (IBAction)rip:(id)sender
{
    [self.queueController rip:self];
}

- (IBAction)pause:(id)sender
{
    [self.queueController togglePauseResume:self];
}

- (IBAction)browseSources:(id)sender
{
    [self.mainController browseSources:self];
}

#pragma mark - Presets Menu actions

/**
 *  Adds the presets list to the menu.
 */
- (void)buildPresetsMenu
{
    // First we remove all the preset menu items
    // inserted previously
    NSArray *menuItems = [self.presetsMenu.itemArray copy];
    for (NSMenuItem *item in menuItems)
    {
        if (item.tag != -1)
        {
            [self.presetsMenu removeItem:item];
        }
    }

    BOOL builtInSeparatorInserted = NO;
    for (HBPreset *preset in self.presetsManager.root.children)
    {
        if (preset.isBuiltIn == NO && builtInSeparatorInserted == NO)
        {
            [self.presetsMenu addItem:[NSMenuItem separatorItem]];
            builtInSeparatorInserted = YES;
        }
        [self.presetsMenu addItem:[self buildMenuItemWithPreset:preset]];
    }
}

- (NSMenuItem *)buildMenuItemWithPreset:(HBPreset *)preset
{
    NSMenuItem *item = [[NSMenuItem alloc] init];
    item.title = preset.name;
    item.toolTip = preset.presetDescription;
    item.tag = 2;

    if (preset.isLeaf)
    {
        item.action = @selector(selectPresetFromMenu:);
        item.representedObject = preset;

        // Make the default preset font bold.
        if ([preset isEqualTo:self.presetsManager.defaultPreset])
        {
            NSAttributedString *newTitle = [[NSAttributedString alloc] initWithString:preset.name
                                                                           attributes:@{NSFontAttributeName: [NSFont boldSystemFontOfSize:14]}];
            [item setAttributedTitle:newTitle];
        }
    }
    else
    {
        NSMenu *menu = [[NSMenu alloc] init];
        for (HBPreset *childPreset in preset.children)
        {
            [menu addItem:[self buildMenuItemWithPreset:childPreset]];
        }

        item.submenu = menu;
    }

    return item;
}

/**
 * We use this method to recreate new, updated factory presets
 */
- (IBAction)addFactoryPresets:(id)sender
{
    [self.presetsManager generateBuiltInPresets];
}

#pragma mark - Show Window Menu Items

/**
 * Shows preferences window.
 */
- (IBAction)showPreferencesWindow:(id)sender
{
    if (_preferencesController == nil)
    {
        _preferencesController = [[HBPreferencesController alloc] init];
    }

    [self.preferencesController showWindow:self];
}

/**
 * Shows queue window.
 */
- (IBAction)showQueueWindow:(id)sender
{
    [self.queueController showWindow:sender];
}

/**
 * Shows debug output window.
 */
- (IBAction)showOutputPanel:(id)sender
{
    [self.outputPanel showWindow:sender];
}

- (IBAction)showPreviewWindow:(id)sender
{
    [self.mainController showPreviewWindow:self];
}

/**
 * Shows main window.
 */
- (IBAction)showMainWindow:(id)sender
{
    [self.mainController showWindow:sender];
}

- (IBAction)openHomepage:(id)sender
{
    [[NSWorkspace sharedWorkspace] openURL:[NSURL
                                            URLWithString:@"https://handbrake.fr/"]];
}

- (IBAction)openForums:(id)sender
{
    [[NSWorkspace sharedWorkspace] openURL:[NSURL
                                            URLWithString:@"https://forum.handbrake.fr/"]];
}
- (IBAction)openUserGuide:(id)sender
{
    [[NSWorkspace sharedWorkspace] openURL:[NSURL
                                            URLWithString:@"https://handbrake.fr/docs/en/1.0.0/"]];
}

@end
