// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2016 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// BOINC is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC.  If not, see <http://www.gnu.org/licenses/>.

//  BOINCGUIApp.mm

#include "MacGUI.pch"
#include "BOINCGUIApp.h"
#include "BOINCBaseFrame.h"
#import <Cocoa/Cocoa.h>

#if !wxCHECK_VERSION(3,0,1)
// This should be fixed after wxCocoa 3.0.0:
// http://trac.wxwidgets.org/ticket/16156

#ifndef NSEventTypeApplicationDefined
#define NSEventTypeApplicationDefined NSApplicationDefined
#endif

// Cocoa routines which are part of CBOINCGUIApp
// Override standard wxCocoa wxApp::CallOnInit() to allow Manager
// to run properly when launched hidden on login via Login Item. 
bool CBOINCGUIApp::CallOnInit() {
        NSAutoreleasePool *mypool = [[NSAutoreleasePool alloc] init];

        NSEvent *event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined
                                        location:NSMakePoint(0.0, 0.0) 
                                   modifierFlags:0 
                                       timestamp:0 
                                    windowNumber:0 
                                         context:nil
                                         subtype:0 data1:0 data2:0]; 
        [NSApp postEvent:event atStart:FALSE];

    bool retVal = wxApp::CallOnInit();

    [mypool release];
    return retVal;
}
#endif


// Our application can get into a strange state 
// if our login item launched it hidden and the
// first time the user "opens" it he either
// double-clicks on our Finder icon or uses
// command-tab.  It becomes the frontmost
// application (with its menu in the menubar)
// but the windows remain hidden, and it does
// not receive an activate event, so we must 
// handle this case by polling.
//
// We can stop the polling after the windows
// have been shown once, since this state only
// occurs if the windows have never appeared.
//
// TODO: Can we avoid polling by using notification
// TODO: [NSApplicationDelegate applicationDidUnhide: ] ?
//
void CBOINCGUIApp::CheckPartialActivation() {
    // This code is not needed and has bad effects on OS 10.5.
    // Initializing wasHidden this way avoids the problem 
    // because we are briefly shown at login on OS 10.5.
    static bool wasHidden = [ NSApp isHidden ];
    
    if (wasHidden) {
        if (m_bAboutDialogIsOpen) return;
        
        if (! [ NSApp isHidden ]) {
            wasHidden = false;
            ShowInterface();
        }
    }
}


// Returns true if file was modified since system was booted, else false
//
bool CBOINCGUIApp::WasFileModifiedBeforeSystemBoot(char * filePath) {
    NSTimeInterval upTime = [[NSProcessInfo processInfo] systemUptime];
    NSString *path = [NSString stringWithUTF8String:filePath];
    NSError *error = nil;
    NSDictionary * attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:&error];
    if (attrs && !error) { // If file exists, then ...
        NSDate *fileLastModifiedDate = [attrs fileModificationDate];
        NSTimeInterval ageOfFile = -[fileLastModifiedDate timeIntervalSinceNow];
        return (ageOfFile > upTime);
    }
    
    return false;
}


// HideThisApp() is called from CBOINCGUIApp::ShowApplication(bool)
// and replaces a call of ShowHideProcess() which is deprecated
// under OS 10.9.
void CBOINCGUIApp::HideThisApp() {
    [ NSApp hide:NSApp ];
}


/// Determines if the current process is visible.
///
/// @return
///  true if the current process is visible, otherwise false.
/// 
bool CBOINCGUIApp::IsApplicationVisible() {
    return (! [ NSApp isHidden ]);
}


///
/// Shows or hides the current process.
///
/// @param bShow
///   true will show the process, false will hide the process.
///
void CBOINCGUIApp::ShowApplication(bool bShow) {
    if (bShow) {
        [ NSApp activateIgnoringOtherApps:YES ];
    } else {
        [ NSApp hide:NSApp ];
    }
}

CGFloat CBOINCGUIApp::GetMenuBarHeight() {
    CGFloat menuBarHeight = [[[NSApplication sharedApplication] mainMenu] menuBarHeight];
    return menuBarHeight;
}


extern bool s_bSkipExitConfirmation;

// Set s_bSkipExitConfirmation to true if cancelled because of logging out or shutting down
//
OSErr QuitAppleEventHandler( const AppleEvent *appleEvt, AppleEvent* reply, UInt32 refcon ) {
    DescType            senderType;
    Size                actualSize;
    pid_t               senderPid;
    OSStatus            anErr;

    // Refuse to quit if a modal dialog is open.  
    // Unfortunately, I know of no way to disable the Quit item in our Dock menu
    if (wxGetApp().IsModalDialogDisplayed()) {
        NSBeep();
        return userCanceledErr;
    }

    anErr = AEGetAttributePtr(appleEvt, keySenderPIDAttr, typeSInt32,
                                &senderType, &senderPid, sizeof(senderPid), &actualSize);
    if (anErr == noErr) {
        NSString * bundleID = [[NSRunningApplication runningApplicationWithProcessIdentifier:senderPid] bundleIdentifier];
        // Consider a Quit command from our Dock menu as coming from this application
        if (bundleID) {
            if (([bundleID compare:@"com.apple.dock"] != NSOrderedSame)
                    && ([bundleID compare:@"edu.berkeley.boinc"] != NSOrderedSame)) {
                s_bSkipExitConfirmation = true; // Not from our app, our dock icon or our taskbar icon
                // The following may no longer be needed under wxCocoa-3.0.0
                wxGetApp().ExitMainLoop();  // Prevents wxMac from issuing events to closed frames
            }
        }
    }
    
    wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED, wxID_EXIT);
    wxGetApp().GetFrame()->GetEventHandler()->AddPendingEvent(evt);
    return noErr;
}