﻿import { ApplicationCommon } from './application-common';
import type { AndroidAccessibilityEvent } from '../accessibility/accessibility-common';
import type { View } from '../ui/core/view';
import type { Page } from '../ui/page';
import type { AndroidActivityEventData, AndroidActivityBundleEventData, AndroidActivityResultEventData, AndroidActivityBackPressedEventData, AndroidActivityNewIntentEventData, AndroidActivityRequestPermissionsEventData, SceneEventData, SceneOpenURLContextsEventData, SceneContinueUserActivityEventData, ScenePerformActionForShortcutItemEventData } from './application-interfaces';
import type { NativeWindow, WindowOpenOptions } from '../native-window';

export * from './application-common';
export * from './application-interfaces';

export const Application: ApplicationCommon;

/**
 * The Application `on` overloads, widened with the Android activity bridges.
 *
 * An intersection rather than method overloads: overriding `on` as a method would replace
 * the inherited {@link ApplicationCommon} overloads instead of adding to them, hiding every
 * app-level event from `Application.android.on()`.
 */
type AndroidApplicationOn = ApplicationCommon['on'] & {
	(event: 'activityCreated', callback: (args: AndroidActivityBundleEventData) => void, thisArg?: any): void;
	(event: 'activityDestroyed', callback: (args: AndroidActivityEventData) => void, thisArg?: any): void;
	(event: 'activityStarted', callback: (args: AndroidActivityEventData) => void, thisArg?: any): void;
	(event: 'activityPaused', callback: (args: AndroidActivityEventData) => void, thisArg?: any): void;
	(event: 'activityResumed', callback: (args: AndroidActivityEventData) => void, thisArg?: any): void;
	(event: 'activityStopped', callback: (args: AndroidActivityEventData) => void, thisArg?: any): void;
	(event: 'saveActivityState', callback: (args: AndroidActivityBundleEventData) => void, thisArg?: any): void;
	(event: 'activityResult', callback: (args: AndroidActivityResultEventData) => void, thisArg?: any): void;
	(event: 'activityBackPressed', callback: (args: AndroidActivityBackPressedEventData) => void, thisArg?: any): void;
	(event: 'activityNewIntent', callback: (args: AndroidActivityNewIntentEventData) => void, thisArg?: any): void;
	(event: 'activityRequestPermissions', callback: (args: AndroidActivityRequestPermissionsEventData) => void, thisArg?: any): void;
};

/**
 * The Application `on` overloads, widened with the iOS scene bridges.
 *
 * An intersection rather than method overloads: overriding `on` as a method would replace
 * the inherited {@link ApplicationCommon} overloads instead of adding to them, hiding every
 * app-level event from `Application.ios.on()`.
 */
type IOSApplicationOn = ApplicationCommon['on'] & {
	(event: 'sceneWillConnect', callback: (args: SceneEventData) => void, thisArg?: any): void;
	(event: 'sceneDidActivate', callback: (args: SceneEventData) => void, thisArg?: any): void;
	(event: 'sceneWillResignActive', callback: (args: SceneEventData) => void, thisArg?: any): void;
	(event: 'sceneWillEnterForeground', callback: (args: SceneEventData) => void, thisArg?: any): void;
	(event: 'sceneDidEnterBackground', callback: (args: SceneEventData) => void, thisArg?: any): void;
	(event: 'sceneDidDisconnect', callback: (args: SceneEventData) => void, thisArg?: any): void;
	(event: 'sceneOpenURLContexts', callback: (args: SceneOpenURLContextsEventData) => void, thisArg?: any): void;
	(event: 'sceneContinueUserActivity', callback: (args: SceneContinueUserActivityEventData) => void, thisArg?: any): void;
	(event: 'scenePerformActionForShortcutItem', callback: (args: ScenePerformActionForShortcutItemEventData) => void, thisArg?: any): void;
};

export class AndroidApplication extends ApplicationCommon {
	static readonly activityCreatedEvent = 'activityCreated';
	static readonly activityDestroyedEvent = 'activityDestroyed';
	static readonly activityStartedEvent = 'activityStarted';
	static readonly activityPausedEvent = 'activityPaused';
	static readonly activityResumedEvent = 'activityResumed';
	static readonly activityStoppedEvent = 'activityStopped';
	static readonly saveActivityStateEvent = 'saveActivityState';
	static readonly activityResultEvent = 'activityResult';
	static readonly activityBackPressedEvent = 'activityBackPressed';
	static readonly activityNewIntentEvent = 'activityNewIntent';
	static readonly activityRequestPermissionsEvent = 'activityRequestPermissions';

	readonly activityCreatedEvent = 'activityCreated';
	readonly activityDestroyedEvent = 'activityDestroyed';
	readonly activityStartedEvent = 'activityStarted';
	readonly activityPausedEvent = 'activityPaused';
	readonly activityResumedEvent = 'activityResumed';
	readonly activityStoppedEvent = 'activityStopped';
	readonly saveActivityStateEvent = 'saveActivityState';
	readonly activityResultEvent = 'activityResult';
	readonly activityBackPressedEvent = 'activityBackPressed';
	readonly activityNewIntentEvent = 'activityNewIntent';
	readonly activityRequestPermissionsEvent = 'activityRequestPermissions';

	on: AndroidApplicationOn;

	getNativeApplication(): android.app.Application;

	/**
	 * @internal
	 */
	init(nativeApp: android.app.Application): void;

	/**
	 * The [android Application](http://developer.android.com/reference/android/app/Application.html) object instance provided to the init of the module.
	 */
	get nativeApp(): android.app.Application;

	/**
	 * @deprecated Use `Utils.android.getPackageName()` instead.
	 */
	get packageName(): string;

	/**
	 * The main (start) Activity for the application.
	 */
	get startActivity(): androidx.appcompat.app.AppCompatActivity;

	/**
	 * The currently active (loaded) [android Activity](http://developer.android.com/reference/android/app/Activity.html).
	 *
	 * This property is automatically updated upon Activity events.
	 */
	get foregroundActivity(): androidx.appcompat.app.AppCompatActivity;

	/**
	 * @deprecated Use `Utils.android.getApplicationContext()` instead.
	 */
	get context(): android.content.Context;

	/**
	 * @deprecated Use `Application.inBackground` instead.
	 */
	get backgrounded(): boolean;

	/**
	 * @deprecated Use `Application.suspended` instead.
	 */
	get paused(): boolean;

	/**
	 * Register a BroadcastReceiver to be run in the main activity thread. The receiver will be called with any broadcast Intent that matches filter, in the main application thread.
	 * For more information, please visit 'http://developer.android.com/reference/android/content/Context.html#registerReceiver%28android.content.BroadcastReceiver,%20android.content.IntentFilter%29'
	 * @param intentFilter A string containing the intent filter.
	 * @param onReceiveCallback A callback function that will be called each time the receiver receives a broadcast.
	 * @param flags Any combination of `RECEIVER_VISIBLE_TO_INSTANT_APPS` (1), `RECEIVER_EXPORTED` (2) and `RECEIVER_NOT_EXPORTED` (4). Defaults to `RECEIVER_EXPORTED`. Only honored from API 26 onwards.
	 * @return A function that can be called to unregister the receiver.
	 */
	registerBroadcastReceiver(intentFilter: string, onReceiveCallback: (context: android.content.Context, intent: android.content.Intent) => void, flags?: number): () => void;

	/**
	 * Unregister a previously registered BroadcastReceiver.
	 * For more information, please visit 'http://developer.android.com/reference/android/content/Context.html#unregisterReceiver(android.content.BroadcastReceiver)'
	 * @param intentFilter A string containing the intent filter with which the receiver was originally registered.
	 */
	unregisterBroadcastReceiver(intentFilter: string): void;

	/**
	 * Get a registered BroadcastReceiver, then you can get the result code of BroadcastReceiver in onReceiveCallback method.
	 * @param intentFilter A string containing the intent filter.
	 * @deprecated Use `getRegisteredBroadcastReceivers` instead.
	 */
	getRegisteredBroadcastReceiver(intentFilter: string): android.content.BroadcastReceiver | undefined;
	/**
	 * Get all registered BroadcastReceivers for a specific intent filter.
	 * @param intentFilter a string containing the intent filter
	 */
	getRegisteredBroadcastReceivers(intentFilter: string): android.content.BroadcastReceiver[];

	/**
	 * @internal - Get a NativeWindow by its activity.
	 */
	_getWindowForActivity(activity: androidx.appcompat.app.AppCompatActivity): NativeWindow | undefined;

	/**
	 * @internal - Feeds a window's active state into the application-level 'resume'/'suspend'
	 * events, raised when the first window becomes active and when the last active one resigns.
	 */
	_setWindowActive(nativeWindow: NativeWindow | undefined, active: boolean, activity?: androidx.appcompat.app.AppCompatActivity): void;

	/**
	 * Opens a new window by launching the start activity into its own task.
	 *
	 * @param options Options for the new window. `options.data` is put on the launch
	 * intent as extras and surfaces as the window's `data`.
	 *
	 * @experimental The start activity's `launchMode` in AndroidManifest.xml decides whether a
	 * second instance can exist at all: `singleTask` (the app template default) and
	 * `singleInstance` route the intent to the existing activity's `onNewIntent` instead of
	 * creating one. Use `singleInstancePerTask` (API 31+) to keep single-task behavior for
	 * launcher and deep-link starts while allowing additional windows, or `standard`.
	 * When the app is already in split-screen, the new window opens in the adjacent pane;
	 * otherwise it covers the current one and both show in recents.
	 */
	openWindow(options?: WindowOpenOptions): void;
}

export class iOSApplication extends ApplicationCommon {
	on: IOSApplicationOn;

	/**
	 * The root view controller for the application.
	 */
	get rootController(): UIViewController;

	/**
	 * The [UIApplication](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplication_Class/index.html).
	 */
	get nativeApp(): UIApplication;

	/**
	 * The key window.
	 */
	get window(): UIWindow;

	/**
	 * The [UIApplicationDelegate](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/index.html) class.
	 */
	get delegate(): UIApplicationDelegate & { prototype: UIApplicationDelegate };

	/**
	 * Sets a custom [UIApplicationDelegate](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/index.html) class.
	 */
	set delegate(value: UIApplicationDelegate | unknown);

	/**
	 * NativeScript's default implementation of the `UIApplicationDelegate`
	 * `applicationConfigurationForConnectingSceneSessionOptions` method.
	 *
	 * It is installed automatically on the application delegate class unless that class
	 * already implements the method. A delegate that does implement it can handle the
	 * scenes it cares about and forward the rest here:
	 *
	 * ```ts
	 * applicationConfigurationForConnectingSceneSessionOptions(app, session, options) {
	 *   if (session.role === myCustomRole) {
	 *     return myConfig;
	 *   }
	 *   return Application.ios.defaultSceneConfiguration(app, session, options);
	 * }
	 * ```
	 *
	 * `onSceneConfiguration` is consulted first. Scenes with the
	 * `UIWindowSceneSessionRoleApplication` role then get a configuration backed by
	 * NativeScript's SceneDelegate; every other role gets a bare configuration that
	 * NativeScript does not manage.
	 */
	defaultSceneConfiguration(application: UIApplication, connectingSceneSession: UISceneSession, options: UISceneConnectionOptions): UISceneConfiguration;

	/**
	 * NativeScript's default implementation of the `UIApplicationDelegate`
	 * `applicationDidDiscardSceneSessions` method, which retires the windows belonging
	 * to the discarded sessions.
	 *
	 * It is installed automatically on the application delegate class unless that class
	 * already implements the method, in which case forward to it from there so window
	 * bookkeeping stays correct.
	 */
	defaultDiscardSceneSessions(application: UIApplication, sceneSessions: NSSet<UISceneSession>): void;

	/**
	 * Adds a delegate handler for the specified delegate method name. This method does not replace an existing handler,
	 * but rather adds the new handler to the existing chain of handlers.
	 * @param methodName The name of the delegate method to add a handler for.
	 * @param handler A function that will be called when the specified delegate method is called.
	 */
	addDelegateHandler<T extends keyof UIApplicationDelegate>(methodName: T, handler: (typeof UIApplicationDelegate.prototype)[T]): void;

	/**
	 * Adds an observer to the default notification center for the specified notification.
	 * For more information, please visit 'https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSNotificationCenter_Class/#//apple_ref/occ/instm/NSNotificationCenter/addObserver:selector:name:object:'
	 * @param notificationName A string containing the name of the notification.
	 * @param onReceiveCallback A callback function that will be called each time the observer receives a notification.
	 */
	addNotificationObserver(notificationName: string, onReceiveCallback: (notification: NSNotification) => void): NotificationObserver;

	/**
	 * Removes the observer for the specified notification from the default notification center.
	 * For more information, please visit 'https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSNotificationCenter_Class/#//apple_ref/occ/instm/NSNotificationCenter/addObserver:selector:name:object:'
	 * @param observer The observer that was returned from the addNotificationObserver method.
	 * @param notificationName A string containing the name of the notification.
	 * @param onReceiveCallback A callback function that will be called each time the observer receives a notification.
	 */
	removeNotificationObserver(observer: any, notificationName: string);

	/**
	 * Checks if the application supports scenes.
	 */
	supportsScenes(): boolean;

	/**
	 * Checks if the application supports multiple scenes.
	 */
	supportsMultipleScenes(): boolean;

	/**
	 * Checks if the application is using the scene lifecycle.
	 */
	isUsingSceneLifecycle(): boolean;

	/**
	 * Opens a new window (scene).
	 *
	 * @param options Options for the new window. `options.data` is serialized into the
	 * activating scene's `NSUserActivity.userInfo`.
	 */
	openWindow(options?: WindowOpenOptions): void;

	/**
	 * Closes a secondary window/scene.
	 * If no target is provided, attempts to close a non-primary active scene.
	 * @param target Optional target to resolve the window to close. Can be a NativeWindow, a View, a UIWindow, a UIWindowScene, or a string scene identifier.
	 */
	closeWindow(target?: NativeWindow | View | UIWindow | UIWindowScene | string): void;

	/**
	 * Gets all windows for the application.
	 * @deprecated Use `getWindows()` instead.
	 */
	getAllWindows(): UIWindow[];

	/**
	 * Gets all scenes for the application.
	 * @deprecated Use `getWindows()` instead.
	 */
	getAllScenes(): UIScene[];

	/**
	 * Gets all window scenes for the application.
	 * @deprecated Use `getWindows()` instead.
	 */
	getWindowScenes(): UIWindowScene[];

	/**
	 * Gets the primary window for the application.
	 * @deprecated Use `primaryWindow?.ios?.uiWindow` instead.
	 */
	getPrimaryWindow(): UIWindow;

	/**
	 * Gets the primary scene for the application.
	 * @deprecated Use `primaryWindow?.ios?.scene` instead.
	 */
	getPrimaryScene(): UIWindowScene | null;

	/**
	 * Sets the root view for a specific window.
	 * @param window The window to set the root view for.
	 * @param view The view to set as the root view.
	 */
	setWindowRootView(window: UIWindow, view: View): void;

	/**
	 * The scene delegate for the application.
	 * Get the current one or set a custom one.
	 */
	sceneDelegate: UIWindowSceneDelegate;

	/**
	 * Register a callback to intercept scene configuration.
	 *
	 * Called for every new scene session. Return a `UISceneConfiguration` to handle
	 * the scene yourself (e.g. CarPlay, external display), or return `null`/`undefined`
	 * to let NativeScript handle it with the default SceneDelegate.
	 *
	 * NativeScript only auto-manages `UIWindowSceneSessionRoleApplication` scenes.
	 * All other scene roles are ignored unless you provide a configuration here.
	 *
	 * @example
	 * ```ts
	 * Application.ios.onSceneConfiguration = (app, session, options) => {
	 *   if (session.role === CPTemplateApplicationSceneSessionRoleApplication) {
	 *     const config = UISceneConfiguration.configurationWithNameSessionRole('CarPlay', session.role);
	 *     config.delegateClass = MyCarPlaySceneDelegate;
	 *     return config;
	 *   }
	 *   return null;
	 * };
	 * ```
	 */
	onSceneConfiguration: ((application: UIApplication, connectingSceneSession: UISceneSession, options: UISceneConnectionOptions) => UISceneConfiguration | null | undefined) | null;

	/**
	 * Delays the 'launch' event, and with it the creation of the first window's content, until the
	 * app first becomes active, instead of raising it while the app finishes launching.
	 *
	 * Applies to non-scene apps only. It has no effect in a scene-based app, where each window's
	 * content is resolved as its scene connects.
	 *
	 * @deprecated Use the 'ready' event for application initialization, and
	 * Application.setWindowContentResolver() to provide window UI.
	 */
	shouldDelayLaunchEvent: boolean;
}

export const VALID_FONT_SCALES: number[];
export function getCurrentFontScale(): number;
export function getAndroidAccessibilityManager(): android.view.accessibility.AccessibilityManager | null;

/**
 * Update accessibility properties on nativeView
 */
export function updateAccessibilityProperties(view: View): void;

/**
 * Android: helper function for triggering accessibility events
 */
export function sendAccessibilityEvent(View: View, eventName: AndroidAccessibilityEvent, text?: string): void;

/**
 * Is Android TalkBack or iOS VoiceOver enabled?
 */
export function isAccessibilityServiceEnabled(): boolean;

/**
 * Find the last view focused on a page.
 */
export function getLastFocusedViewOnPage(page: Page): View | null;
