/*
 * Copyright (c) 2008-2022 Jonathan Schleifer <js@nil.im>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
 * the packaging of this file.
 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either version 2 or 3, which can be found in the file
 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#include <signal.h>

#import "OFObject.h"
#import "OFNotification.h"

OF_ASSUME_NONNULL_BEGIN

/** @file */

@class OFArray OF_GENERIC(ObjectType);
@class OFDictionary OF_GENERIC(KeyType, ObjectType);
@class OFMutableArray OF_GENERIC(ObjectType);
@class OFMutableDictionary OF_GENERIC(KeyType, ObjectType);
@class OFSandbox;
@class OFString;

/**
 * @brief A notification that will be sent when the application will terminate.
 */
extern const OFNotificationName OFApplicationWillTerminateNotification;

/**
 * @brief Specify the class to be used as the application delegate.
 *
 * An instance of this class will be created and act as the application
 * delegate.
 *
 * For example, it can be used like this:
 *
 * @code
 * // In MyAppDelegate.h:
 * @interface MyAppDelegate: OFObject <OFApplicationDelegate>
 * @end
 *
 * // In MyAppDelegate.m:
 * OF_APPLICATION_DELEGATE(MyAppDelegate)
 *
 * @implementation MyAppDelegate
 * - (void)applicationDidFinishLaunching
 * {
 *         [OFApplication terminate];
 * }
 * @end
 * @endcode
 */
#define OF_APPLICATION_DELEGATE(class_)			\
	int						\
	main(int argc, char *argv[])			\
	{						\
		return OFApplicationMain(&argc, &argv,	\
		    (class_ *)[[class_ alloc] init]);	\
	}

#ifdef OF_HAVE_PLEDGE
# define OF_HAVE_SANDBOX
#endif

/**
 * @protocol OFApplicationDelegate OFApplication.h ObjFW/OFApplication.h
 *
 * @brief A protocol for delegates of OFApplication.
 *
 * @note Signals are not available on AmigaOS!
 */
@protocol OFApplicationDelegate <OFObject>
/**
 * @brief A method which is called when the application was initialized and is
 *	  running now.
 */
- (void)applicationDidFinishLaunching;

@optional
/**
 * @brief A method which is called when the application will terminate.
 */
- (void)applicationWillTerminate;

/**
 * @brief A method which is called when the application received a SIGINT.
 *
 * @warning You are not allowed to send any messages inside this method, as
 *	    message dispatching is not signal-safe! You are only allowed to do
 *	    signal-safe operations like setting a variable or calling a
 *	    signal-safe function!
 */
- (void)applicationDidReceiveSIGINT;

#ifdef SIGHUP
/**
 * @brief A method which is called when the application received a SIGHUP.
 *
 * This signal is not available on Windows.
 *
 * @warning You are not allowed to send any messages inside this method, as
 *	    message dispatching is not signal-safe! You are only allowed to do
 *	    signal-safe operations like setting a variable or calling a
 *	    signal-safe function!
 */
- (void)applicationDidReceiveSIGHUP;
#endif

#ifdef SIGUSR1
/**
 * @brief A method which is called when the application received a SIGUSR1.
 *
 * This signal is not available on Windows.
 *
 * @warning You are not allowed to send any messages inside this method, as
 *	    message dispatching is not signal-safe! You are only allowed to do
 *	    signal-safe operations like setting a variable or calling a
 *	    signal-safe function!
 */
- (void)applicationDidReceiveSIGUSR1;
#endif

#ifdef SIGUSR2
/**
 * @brief A method which is called when the application received a SIGUSR2.
 *
 * This signal is not available on Windows.
 *
 * @warning You are not allowed to send any messages inside this method, as
 *	    message dispatching is not signal-safe! You are only allowed to do
 *	    signal-safe operations like setting a variable or calling a
 *	    signal-safe function!
 */
- (void)applicationDidReceiveSIGUSR2;
#endif
@end

/**
 * @class OFApplication OFApplication.h ObjFW/OFApplication.h
 *
 * @brief A class which represents the application as an object.
 *
 * In order to create a new OFApplication, you should create a class conforming
 * to the optional @ref OFApplicationDelegate protocol and put
 * `OF_APPLICATION_DELEGATE(NameOfYourClass)` in the .m file of that class.
 *
 * When the application is about to be terminated,
 * @ref OFApplicationDelegate#applicationWillTerminate will be called on the
 * delegate and an @ref OFApplicationWillTerminateNotification will be sent.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFApplication: OFObject
{
	OFString *_programName;
	OFArray OF_GENERIC(OFString *) *_arguments;
	OFMutableDictionary OF_GENERIC(OFString *, OFString *) *_environment;
	int *_argc;
	char ***_argv;
	id <OFApplicationDelegate> _Nullable _delegate;
	void (*_Nullable _SIGINTHandler)(id, SEL);
#ifndef OF_WINDOWS
	void (*_Nullable _SIGHUPHandler)(id, SEL);
	void (*_Nullable _SIGUSR1Handler)(id, SEL);
	void (*_Nullable _SIGUSR2Handler)(id, SEL);
#endif
#ifdef OF_HAVE_SANDBOX
	OFSandbox *_Nullable _activeSandbox;
	OFSandbox *_Nullable _activeSandboxForChildProcesses;
#endif
}

#ifdef OF_HAVE_CLASS_PROPERTIES
@property (class, readonly, nullable, nonatomic)
    OFApplication *sharedApplication;
@property (class, readonly, nullable, nonatomic) OFString *programName;
@property (class, readonly, nullable, nonatomic)
    OFArray OF_GENERIC(OFString *) *arguments;
@property (class, readonly, nullable, nonatomic)
    OFDictionary OF_GENERIC(OFString *, OFString *) *environment;
#endif

/**
 * @brief The name of the program (argv[0]).
 */
@property (readonly, nonatomic) OFString *programName;

/**
 * @brief The arguments passed to the application.
 */
@property (readonly, nonatomic) OFArray OF_GENERIC(OFString *) *arguments;

/**
 * @brief The environment of the application.
 */
@property (readonly, nonatomic)
    OFDictionary OF_GENERIC(OFString *, OFString *) *environment;

/**
 * @brief The delegate of the application.
 */
@property OF_NULLABLE_PROPERTY (assign, nonatomic)
    id <OFApplicationDelegate> delegate;

#ifdef OF_HAVE_SANDBOX
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFSandbox *activeSandbox;
@property OF_NULLABLE_PROPERTY (readonly, nonatomic)
    OFSandbox *activeSandboxForChildProcesses;
#endif

/**
 * @brief Returns the only OFApplication instance in the application.
 *
 * @return The only OFApplication instance in the application
 */
+ (nullable OFApplication *)sharedApplication;

/**
 * @brief Returns the name of the program (argv[0]).
 *
 * @return The name of the program (argv[0])
 */
+ (nullable OFString *)programName;

/**
 * @brief Returns the arguments passed to the application.
 *
 * @return The arguments passed to the application
 */
+ (nullable OFArray OF_GENERIC(OFString *) *)arguments;

/**
 * @brief Returns the environment of the application.
 *
 * @return The environment of the application
 */
+ (nullable OFDictionary OF_GENERIC(OFString *, OFString *) *)environment;

/**
 * @brief Terminates the application with the EXIT_SUCCESS status.
 */
+ (void)terminate OF_NO_RETURN;

/**
 * @brief Terminates the application with the specified status.
 *
 * @param status The status with which the application will terminate
 */
+ (void)terminateWithStatus: (int)status OF_NO_RETURN;

#ifdef OF_HAVE_SANDBOX
+ (void)of_activateSandbox: (OFSandbox *)sandbox;
+ (void)of_activateSandboxForChildProcesses: (OFSandbox *)sandbox;
#endif

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Gets argc and argv.
 *
 * @param argc A pointer where a pointer to argc should be stored
 * @param argv A pointer where a pointer to argv should be stored
 */
- (void)getArgumentCount: (int *_Nonnull *_Nonnull)argc
       andArgumentValues: (char *_Nullable *_Nonnull *_Nonnull[_Nonnull])argv;

/**
 * @brief Terminates the application.
 */
- (void)terminate OF_NO_RETURN;

/**
 * @brief Terminates the application with the specified status.
 *
 * @param status The status with which the application will terminate
 */
- (void)terminateWithStatus: (int)status OF_NO_RETURN;

#ifdef OF_HAVE_SANDBOX
- (void)of_activateSandbox: (OFSandbox *)sandbox;
- (void)of_activateSandboxForChildProcesses: (OFSandbox *)sandbox;
#endif
@end

#ifdef __cplusplus
extern "C" {
#endif
extern int OFApplicationMain(int *_Nonnull, char *_Nullable *_Nonnull[_Nonnull],
    id <OFApplicationDelegate>);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END
