import { __decorate, __param } from '../../../../../external/tslib/tslib.es6.js';
import { setFullscreen, isSafari } from '../../base/browser/browser.js';
import { getWindowsCount, getWindows, getWindow, getActiveWindow, getWindowById, addDisposableListener, EventType, EventHelper, windowOpenWithSuccess, windowOpenPopup, windowOpenNoOpener } from '../../base/browser/dom.js';
import { DomEmitter } from '../../base/browser/event.js';
import { requestUsbDevice, requestSerialPort, requestHidDevice } from '../../base/browser/deviceAccess.js';
import { timeout } from '../../base/common/async.js';
import { Event } from '../../base/common/event.js';
import { Disposable, dispose, toDisposable } from '../../base/common/lifecycle.js';
import { matchesScheme, Schemas } from '../../base/common/network.js';
import { isMacintosh, isIOS } from '../../base/common/platform.js';
import Severity$1 from '../../base/common/severity.js';
import { URI } from '../../base/common/uri.js';
import { localizeWithPath } from '../../nls.js';
import { CommandsRegistry } from '../../platform/commands/common/commands.js';
import { IDialogService } from '../../platform/dialogs/common/dialogs.service.js';
import { IInstantiationService } from '../../platform/instantiation/common/instantiation.js';
import { ILabelService } from '../../platform/label/common/label.service.js';
import { IOpenerService } from '../../platform/opener/common/opener.service.js';
import { IProductService } from '../../platform/product/common/productService.service.js';
import { IBrowserWorkbenchEnvironmentService } from '../services/environment/browser/environmentService.service.js';
import { IWorkbenchLayoutService } from '../services/layout/browser/layoutService.service.js';
import { ShutdownReason } from '../services/lifecycle/common/lifecycle.js';
import { ILifecycleService } from '../services/lifecycle/common/lifecycle.service.js';
import { IHostService } from '../services/host/browser/host.service.js';
import { registerWindowDriver } from '../services/driver/browser/driver.js';
import { isAuxiliaryWindow, mainWindow } from '../../base/browser/window.js';
import { createSingleCallFunction } from '../../base/common/functional.js';
import { IConfigurationService } from '../../platform/configuration/common/configuration.service.js';
import { IWorkbenchEnvironmentService } from '../services/environment/common/environmentService.service.js';
var BaseWindow_1;
const _moduleId = "vs/workbench/browser/window";
let BaseWindow = class BaseWindow extends Disposable {
    static { BaseWindow_1 = this; }
    static { this.TIMEOUT_HANDLES = Number.MIN_SAFE_INTEGER; }
    static { this.TIMEOUT_DISPOSABLES = ( (new Map())); }
    constructor(targetWindow, dom = { getWindowsCount, getWindows }, hostService, environmentService) {
        super();
        this.hostService = hostService;
        this.environmentService = environmentService;
        this.enableWindowFocusOnElementFocus(targetWindow);
        this.enableMultiWindowAwareTimeout(targetWindow, dom);
        this.registerFullScreenListeners(targetWindow.vscodeWindowId);
    }
    enableWindowFocusOnElementFocus(targetWindow) {
        const originalFocus = targetWindow.HTMLElement.prototype.focus;
        const that = this;
        targetWindow.HTMLElement.prototype.focus = function (options) {
            that.onElementFocus(getWindow(this));
            originalFocus.apply(this, [options]);
        };
    }
    onElementFocus(targetWindow) {
        const activeWindow = getActiveWindow();
        if (activeWindow !== targetWindow && activeWindow.document.hasFocus()) {
            targetWindow.focus();
            if (!this.environmentService.extensionTestsLocationURI &&
                !targetWindow.document.hasFocus()) {
                this.hostService.focus(targetWindow);
            }
        }
    }
    enableMultiWindowAwareTimeout(targetWindow, dom = { getWindowsCount, getWindows }) {
        const originalSetTimeout = targetWindow.setTimeout;
        Object.defineProperty(targetWindow, 'vscodeOriginalSetTimeout', { get: () => originalSetTimeout });
        const originalClearTimeout = targetWindow.clearTimeout;
        Object.defineProperty(targetWindow, 'vscodeOriginalClearTimeout', { get: () => originalClearTimeout });
        targetWindow.setTimeout = function (handler, timeout = 0, ...args) {
            if (dom.getWindowsCount() === 1 || typeof handler === 'string' || timeout === 0 ) {
                return originalSetTimeout.apply(this, [handler, timeout, ...args]);
            }
            const timeoutDisposables = ( (new Set()));
            const timeoutHandle = BaseWindow_1.TIMEOUT_HANDLES++;
            BaseWindow_1.TIMEOUT_DISPOSABLES.set(timeoutHandle, timeoutDisposables);
            const handlerFn = createSingleCallFunction(handler, () => {
                dispose(timeoutDisposables);
                BaseWindow_1.TIMEOUT_DISPOSABLES.delete(timeoutHandle);
            });
            for (const { window, disposables } of dom.getWindows()) {
                if (isAuxiliaryWindow(window) && window.document.visibilityState === 'hidden') {
                    continue;
                }
                const handle = window.vscodeOriginalSetTimeout.apply(this, [handlerFn, timeout, ...args]);
                const timeoutDisposable = toDisposable(() => {
                    window.vscodeOriginalClearTimeout(handle);
                    timeoutDisposables.delete(timeoutDisposable);
                });
                disposables.add(timeoutDisposable);
                timeoutDisposables.add(timeoutDisposable);
            }
            return timeoutHandle;
        };
        targetWindow.clearTimeout = function (timeoutHandle) {
            const timeoutDisposables = typeof timeoutHandle === 'number' ? BaseWindow_1.TIMEOUT_DISPOSABLES.get(timeoutHandle) : undefined;
            if (timeoutDisposables) {
                dispose(timeoutDisposables);
                BaseWindow_1.TIMEOUT_DISPOSABLES.delete(timeoutHandle);
            }
            else {
                originalClearTimeout.apply(this, [timeoutHandle]);
            }
        };
    }
    registerFullScreenListeners(targetWindowId) {
        this._register(this.hostService.onDidChangeFullScreen(({ windowId, fullscreen }) => {
            if (windowId === targetWindowId) {
                const targetWindow = getWindowById(targetWindowId);
                if (targetWindow) {
                    setFullscreen(fullscreen, targetWindow.window);
                }
            }
        }));
    }
    static async confirmOnShutdown(accessor, reason) {
        const dialogService = accessor.get(IDialogService);
        const configurationService = accessor.get(IConfigurationService);
        const message = reason === ShutdownReason.QUIT ?
            (isMacintosh ? ( localizeWithPath(_moduleId, 0, "Are you sure you want to quit?")) : ( localizeWithPath(_moduleId, 1, "Are you sure you want to exit?"))) :
            ( localizeWithPath(_moduleId, 2, "Are you sure you want to close the window?"));
        const primaryButton = reason === ShutdownReason.QUIT ?
            (isMacintosh ? ( localizeWithPath(_moduleId, 3, "&&Quit")) : ( localizeWithPath(_moduleId, 4, "&&Exit"))) :
            ( localizeWithPath(_moduleId, 5, "&&Close Window"));
        const res = await dialogService.confirm({
            message,
            primaryButton,
            checkbox: {
                label: ( localizeWithPath(_moduleId, 6, "Do not ask me again"))
            }
        });
        if (res.confirmed && res.checkboxChecked) {
            await configurationService.updateValue('window.confirmBeforeClose', 'never');
        }
        return res.confirmed;
    }
};
BaseWindow = BaseWindow_1 = ( (__decorate([
    ( (__param(2, IHostService))),
    ( (__param(3, IWorkbenchEnvironmentService)))
], BaseWindow)));
let BrowserWindow = class BrowserWindow extends BaseWindow {
    constructor(openerService, lifecycleService, dialogService, labelService, productService, browserEnvironmentService, layoutService, instantiationService, hostService) {
        super(mainWindow, undefined, hostService, browserEnvironmentService);
        this.openerService = openerService;
        this.lifecycleService = lifecycleService;
        this.dialogService = dialogService;
        this.labelService = labelService;
        this.productService = productService;
        this.browserEnvironmentService = browserEnvironmentService;
        this.layoutService = layoutService;
        this.instantiationService = instantiationService;
        this.registerListeners();
        this.create();
    }
    registerListeners() {
        this._register(this.lifecycleService.onWillShutdown(() => this.onWillShutdown()));
        const viewport = isIOS && mainWindow.visualViewport ? mainWindow.visualViewport  : mainWindow ;
        this._register(addDisposableListener(viewport, EventType.RESIZE, () => {
            this.layoutService.layout();
            if (isIOS) {
                mainWindow.scrollTo(0, 0);
            }
        }));
        this._register(addDisposableListener(this.layoutService.mainContainer, EventType.WHEEL, e => e.preventDefault(), { passive: false }));
        this._register(addDisposableListener(this.layoutService.mainContainer, EventType.CONTEXT_MENU, e => EventHelper.stop(e, true)));
        this._register(addDisposableListener(this.layoutService.mainContainer, EventType.DROP, e => EventHelper.stop(e, true)));
    }
    onWillShutdown() {
        Event.toPromise(Event.any(Event.once(( (new DomEmitter(mainWindow.document.body, EventType.KEY_DOWN, true))).event), Event.once(( (new DomEmitter(mainWindow.document.body, EventType.MOUSE_DOWN, true))).event))).then(async () => {
            await timeout(3000);
            await this.dialogService.prompt({
                type: Severity$1.Error,
                message: ( localizeWithPath(
                    _moduleId,
                    7,
                    "An unexpected error occurred that requires a reload of this page."
                )),
                detail: ( localizeWithPath(_moduleId, 8, "The workbench was unexpectedly disposed while running.")),
                buttons: [
                    {
                        label: ( localizeWithPath(_moduleId, 9, "&&Reload")),
                        run: () => mainWindow.location.reload()
                    }
                ]
            });
        });
    }
    create() {
        this.setupOpenHandlers();
        this.registerLabelFormatters();
        this.registerCommands();
        this.setupDriver();
    }
    setupDriver() {
        if (this.environmentService.enableSmokeTestDriver) {
            registerWindowDriver(this.instantiationService);
        }
    }
    setupOpenHandlers() {
        this.openerService.setDefaultExternalOpener({
            openExternal: async (href) => {
                let isAllowedOpener = false;
                if (this.browserEnvironmentService.options?.openerAllowedExternalUrlPrefixes) {
                    for (const trustedPopupPrefix of this.browserEnvironmentService.options.openerAllowedExternalUrlPrefixes) {
                        if (href.startsWith(trustedPopupPrefix)) {
                            isAllowedOpener = true;
                            break;
                        }
                    }
                }
                if (matchesScheme(href, Schemas.http) || matchesScheme(href, Schemas.https)) {
                    if (isSafari) {
                        const opened = windowOpenWithSuccess(href, !isAllowedOpener);
                        if (!opened) {
                            await this.dialogService.prompt({
                                type: Severity$1.Warning,
                                message: ( localizeWithPath(
                                    _moduleId,
                                    10,
                                    "The browser interrupted the opening of a new tab or window. Press 'Open' to open it anyway."
                                )),
                                detail: href,
                                buttons: [
                                    {
                                        label: ( localizeWithPath(_moduleId, 11, "&&Open")),
                                        run: () => isAllowedOpener ? windowOpenPopup(href) : windowOpenNoOpener(href)
                                    },
                                    {
                                        label: ( localizeWithPath(_moduleId, 12, "&&Learn More")),
                                        run: () => this.openerService.open(( (URI.parse('https://aka.ms/allow-vscode-popup'))))
                                    }
                                ],
                                cancelButton: true
                            });
                        }
                    }
                    else {
                        isAllowedOpener
                            ? windowOpenPopup(href)
                            : windowOpenNoOpener(href);
                    }
                }
                else {
                    const invokeProtocolHandler = () => {
                        this.lifecycleService.withExpectedShutdown({ disableShutdownHandling: true }, () => mainWindow.location.href = href);
                    };
                    invokeProtocolHandler();
                    const showProtocolUrlOpenedDialog = async () => {
                        const { downloadUrl } = this.productService;
                        let detail;
                        const buttons = [
                            {
                                label: ( localizeWithPath(_moduleId, 13, "&&Try Again")),
                                run: () => invokeProtocolHandler()
                            }
                        ];
                        if (downloadUrl !== undefined) {
                            detail = ( localizeWithPath(
                                _moduleId,
                                14,
                                "We launched {0} on your computer.\n\nIf {1} did not launch, try again or install it below.",
                                this.productService.nameLong,
                                this.productService.nameLong
                            ));
                            buttons.push({
                                label: ( localizeWithPath(_moduleId, 15, "&&Install")),
                                run: async () => {
                                    await this.openerService.open(( (URI.parse(downloadUrl))));
                                    showProtocolUrlOpenedDialog();
                                }
                            });
                        }
                        else {
                            detail = ( localizeWithPath(
                                _moduleId,
                                16,
                                "We launched {0} on your computer.\n\nIf {1} did not launch, try again below.",
                                this.productService.nameLong,
                                this.productService.nameLong
                            ));
                        }
                        await this.hostService.withExpectedShutdown(() => this.dialogService.prompt({
                            type: Severity$1.Info,
                            message: ( localizeWithPath(_moduleId, 17, "All done. You can close this tab now.")),
                            detail,
                            buttons,
                            cancelButton: true
                        }));
                    };
                    if (matchesScheme(href, this.productService.urlProtocol)) {
                        await showProtocolUrlOpenedDialog();
                    }
                }
                return true;
            }
        });
    }
    registerLabelFormatters() {
        this._register(this.labelService.registerFormatter({
            scheme: Schemas.vscodeUserData,
            priority: true,
            formatting: {
                label: '(Settings) ${path}',
                separator: '/',
            }
        }));
    }
    registerCommands() {
        CommandsRegistry.registerCommand('workbench.experimental.requestUsbDevice', async (_accessor, options) => {
            return requestUsbDevice(options);
        });
        CommandsRegistry.registerCommand('workbench.experimental.requestSerialPort', async (_accessor, options) => {
            return requestSerialPort(options);
        });
        CommandsRegistry.registerCommand('workbench.experimental.requestHidDevice', async (_accessor, options) => {
            return requestHidDevice(options);
        });
    }
};
BrowserWindow = ( (__decorate([
    ( (__param(0, IOpenerService))),
    ( (__param(1, ILifecycleService))),
    ( (__param(2, IDialogService))),
    ( (__param(3, ILabelService))),
    ( (__param(4, IProductService))),
    ( (__param(5, IBrowserWorkbenchEnvironmentService))),
    ( (__param(6, IWorkbenchLayoutService))),
    ( (__param(7, IInstantiationService))),
    ( (__param(8, IHostService)))
], BrowserWindow)));
export { BaseWindow, BrowserWindow };
