// Copyright 1998-2000 Omni Development, Inc.  All rights reserved.
//
// This software may only be used and reproduced according to the
// terms in the file OmniSourceLicense.html, which should be
// distributed with this project and can also be found at
// http://www.omnigroup.com/DeveloperResources/OmniSourceLicense.html.

#import <OmniFoundation/OFController.h>

#import <Foundation/Foundation.h>
#import <OmniBase/OmniBase.h>

RCS_ID("$Header: /Network/Source/CVS/OmniGroup/Frameworks/OmniFoundation/OFController.m,v 1.1 2000/08/29 23:53:52 bungi Exp $")


static DEFINE_NSSTRING(OFControllerDidInitializeNotification);
static DEFINE_NSSTRING(OFControllerStartedRunningNotification);
static DEFINE_NSSTRING(OFControllerRequestsTerminateNotification);
static DEFINE_NSSTRING(OFControllerWillTerminateNotification);
static DEFINE_NSSTRING(OFControllerRequestsCancelTerminateException);



// The following exception can be raised during an OFControllerRequestsTerminateNotification.

static NSString *OFControllerRequestsCancelTerminateException;


static OFController *sharedController = nil;

@interface OFController (PrivateAPI)
- (id) _init;
@end

/*" OFController is used to represent the current state of the application and to receive notifications about changes in that state. "*/
@implementation OFController

+ (id) sharedController;
{
    if (!sharedController)
        sharedController = [[self alloc] _init];
    return sharedController;
}

// We currently don't support subclassing OFController and making that subclass the
// main controller (application delegate, for example) for your process.  We'd have
// to have some way to make sure the right class got allocated.
- (id) init;
{
    [self release];
    [NSException raise: NSInternalInconsistencyException
                format: @"Call +sharedController to get an OFController"];
    return nil;
}

- (OFControllerStatus) status;
{
    return status;
}


static inline void _addObserver(id observer, SEL selector, NSString *name)
{
    OBPRECONDITION(sharedController);
    
    if ([observer respondsToSelector: selector]) {
        [[NSNotificationCenter defaultCenter] addObserver: observer
                                                 selector: selector
                                                     name: name
                                                   object: sharedController];
    }
}

/*" Subscribes the observer to a set of notifications based on the methods that it implements in the OFControllerObserver informal protocol.  Classes can register for these notifications in their +didLoad methods (and those +didLoad methods probably shouldn't do much else, since defaults aren't yet registered during +didLoad). "*/
- (void) addObserver: (id) observer;
{
    _addObserver(observer,
                 @selector(controllerDidInitialize:),
                 OFControllerDidInitializeNotification);
    _addObserver(observer,
                 @selector(controllerStartedRunning:),
                 OFControllerStartedRunningNotification);
    _addObserver(observer,
                 @selector(controllerRequestsTerminate:),
                 OFControllerRequestsTerminateNotification);
    _addObserver(observer,
                 @selector(controllerWillTerminate:),
                 OFControllerWillTerminateNotification);
}


/*" Unsubscribes the observer to a set of notifications based on the methods that it implements in the OFControllerObserver informal protocol. "*/
- (void) removeObserver: (id) observer;
{
    NSNotificationCenter *center;
    
    center = [NSNotificationCenter defaultCenter];
    [center removeObserver: observer name: OFControllerDidInitializeNotification object: self];
    [center removeObserver: observer name: OFControllerStartedRunningNotification object: self];
    [center removeObserver: observer name: OFControllerRequestsTerminateNotification object: self];
    [center removeObserver: observer name: OFControllerWillTerminateNotification object: self];
}


/*" The application should call this once after it is initialized.  In AppKit applications, this should be called from -applicationWillFinishLaunching:. "*/
- (void) didInitialize;
{
    OBPRECONDITION(status == OFControllerNotInitializedStatus);
    
    status = OFControllerInitializedStatus;
    NS_DURING {
        [[NSNotificationCenter defaultCenter] postNotificationName: OFControllerDidInitializeNotification object: self];
    } NS_HANDLER {
        NSLog(@"Ignoring exception raised during -[OFController didInitialize]: %@", localException);
    } NS_ENDHANDLER;
}

/*" The application should call this once after calling -didInitialize.  In AppKit applications, this should be called from -applicationDidFinishLaunching:. "*/
- (void) startedRunning;
{
    OBPRECONDITION(status == OFControllerInitializedStatus);
    
    status = OFControllerRunningStatus;
    NS_DURING {
        [[NSNotificationCenter defaultCenter] postNotificationName: OFControllerStartedRunningNotification object: self];
    } NS_HANDLER {
        NSLog(@"Ignoring exception raised during -[OFController startedRunning]: %@", localException);
    } NS_ENDHANDLER;
}

/*" The application should call this when a termination request has been received.  If YES is returned, the termination can proceed (i.e., the caller should call -willTerminate) next. "*/
- (BOOL) requestTermination;
{
    BOOL shouldTerminate = YES;
    
    OBPRECONDITION(status == OFControllerRunningStatus);
    
    status = OFControllerRequestingTerminateStatus;
    NS_DURING {
        [[NSNotificationCenter defaultCenter] postNotificationName: OFControllerRequestsTerminateNotification object: self];
    } NS_HANDLER {
        // User requested that the terminate be cancelled
#warning One listener raising an exception does not stop a notification from being posted to the rest of the listeners
        // That means that the user can end up having to press "Cancel" several times to get out of the notification.  I guess we need a "user already cancelled this terminate operation" check (as we used to have before I cleverly eliminated it--doh).
        if ([[localException name] isEqualToString: OFControllerRequestsCancelTerminateException])
            shouldTerminate = NO;
        else
            NSLog(@"Ignoring exception raised during -[OFController requestTermination]: %@", localException);
    } NS_ENDHANDLER;
    
    if (shouldTerminate)
        status = OFControllerTerminatingStatus;
    else
        status = OFControllerRunningStatus;
    return shouldTerminate;
}


/*" This method can be called during a -controllerRequestsTerminate: method when an object wishes to cancel the termination (typically in response to a user pressing the "Cancel" button on a Save panel). "*/
- (void) cancelTermination;
{
    OBPRECONDITION(status == OFControllerRequestingTerminateStatus);
    
    [NSException raise: OFControllerRequestsCancelTerminateException
                format: @"User requested cancel of termination."];
}

/*" The application should call this method when it is going to terminate and there is no chance of cancelling it (i.e., after it has called -requestTermination and a YES has been returned). "*/
- (void) willTerminate;
{
    OBPRECONDITION(status == OFControllerTerminatingStatus);
    
    [[NSNotificationCenter defaultCenter] postNotificationName: OFControllerWillTerminateNotification object: self];
}

@end


@implementation OFController (PrivateAPI)

- (id) _init;
{
    status = OFControllerNotInitializedStatus;
    return self;
}

@end

