
import { __decorate, __param } from '../../../../../../../external/tslib/tslib.es6.js';
import { append, $, addDisposableListener, EventType, getWindow, isKeyboardEvent } from '../../../../base/browser/dom.js';
import { localize } from '../../../../nls.js';
import { Disposable, MutableDisposable, DisposableStore, isDisposable, toDisposable, dispose } from '../../../../base/common/lifecycle.js';
import { ActionRunner, Separator, Action } from '../../../../base/common/actions.js';
import { IExtensionsWorkbenchService } from '../common/extensions.service.js';
import { Event } from '../../../../base/common/event.js';
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
import { WorkbenchPagedList, WorkbenchAsyncDataTree } from '../../../../platform/list/browser/listService.js';
import { IListService } from '../../../../platform/list/browser/listService.service.js';
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.service.js';
import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.service.js';
import { registerThemingParticipant } from '../../../../platform/theme/common/themeService.js';
import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js';
import { isNonEmptyArray } from '../../../../base/common/arrays.js';
import { Delegate, Renderer } from './extensionsList.js';
import '../../../../platform/theme/common/colorUtils.js';
import { foreground } from '../../../../platform/theme/common/colors/baseColors.js';
import '../../../../platform/theme/common/colors/chartsColors.js';
import { editorBackground } from '../../../../platform/theme/common/colors/editorColors.js';
import '../../../../platform/theme/common/colors/inputColors.js';
import { listFocusBackground, listFocusForeground } from '../../../../platform/theme/common/colors/listColors.js';
import '../../../../platform/theme/common/colors/menuColors.js';
import '../../../../platform/theme/common/colors/minimapColors.js';
import '../../../../platform/theme/common/colors/miscColors.js';
import '../../../../platform/theme/common/colors/quickpickColors.js';
import '../../../../platform/theme/common/colors/searchColors.js';
import { StandardKeyboardEvent } from '../../../../base/browser/keyboardEvent.js';
import { StandardMouseEvent } from '../../../../base/browser/mouseEvent.js';
import { KeyCode } from '../../../../base/common/keyCodes.js';
import { HoverPosition } from '../../../../base/browser/ui/hover/hoverWidget.js';
import { ViewContainerLocation } from '../../../common/views.js';
import { IViewDescriptorService } from '../../../common/views.service.js';
import { Position } from '../../../services/layout/browser/layoutService.js';
import { IWorkbenchLayoutService } from '../../../services/layout/browser/layoutService.service.js';
import { areSameExtensions } from '../../../../platform/extensionManagement/common/extensionManagementUtil.js';
import { ManageExtensionAction, getContextMenuActions, ExtensionAction } from './extensionsActions.js';
import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.service.js';
import { INotificationService } from '../../../../platform/notification/common/notification.service.js';
import { getLocationBasedViewColors } from '../../../browser/parts/views/viewPane.js';
import { DelayedPagedModel } from '../../../../base/common/paging.js';
import { ExtensionIconWidget } from './extensionsWidgets.js';
import { ILogService } from '../../../../platform/log/common/log.service.js';
import { isCancellationError } from '../../../../base/common/errors.js';

var ExtensionRenderer_1;
function getAriaLabelForExtension(extension) {
    if (!extension) {
        return "";
    }
    const publisher = extension.publisherDomain?.verified ? ( localize(9248, "Verified Publisher {0}", extension.publisherDisplayName)) : ( localize(9249, "Publisher {0}", extension.publisherDisplayName));
    const deprecated = extension?.deprecationInfo ? ( localize(9250, "Deprecated")) : "";
    const rating = extension?.rating ? ( localize(
        9251,
        "Rated {0} out of 5 stars by {1} users",
        extension.rating.toFixed(2),
        extension.ratingCount
    )) : "";
    return `${extension.displayName}, ${deprecated ? `${deprecated}, ` : ""}${extension.version}, ${publisher}, ${extension.description} ${rating ? `, ${rating}` : ""}`;
}
let ExtensionsList = class ExtensionsList extends Disposable {
    constructor(
        parent,
        viewId,
        options,
        extensionsViewState,
        extensionsWorkbenchService,
        viewDescriptorService,
        layoutService,
        notificationService,
        contextMenuService,
        contextKeyService,
        instantiationService,
        logService
    ) {
        super();
        this.extensionsWorkbenchService = extensionsWorkbenchService;
        this.contextMenuService = contextMenuService;
        this.contextKeyService = contextKeyService;
        this.instantiationService = instantiationService;
        this.logService = logService;
        this.contextMenuActionRunner = this._register(( new ActionRunner()));
        this.modalNavigationDisposable = this._register(( new MutableDisposable()));
        this._register(this.contextMenuActionRunner.onDidRun((
            {
                error
            }
        ) => error && notificationService.error(error)));
        const delegate = ( new Delegate());
        const renderer = instantiationService.createInstance(Renderer, extensionsViewState, {
            hoverOptions: {
                position: () => {
                    const viewLocation = viewDescriptorService.getViewLocationById(viewId);
                    if (viewLocation === ViewContainerLocation.Sidebar) {
                        return layoutService.getSideBarPosition() === Position.LEFT ? HoverPosition.RIGHT : HoverPosition.LEFT;
                    }
                    if (viewLocation === ViewContainerLocation.AuxiliaryBar) {
                        return layoutService.getSideBarPosition() === Position.LEFT ? HoverPosition.LEFT : HoverPosition.RIGHT;
                    }
                    return HoverPosition.RIGHT;
                }
            }
        });
        this.list = instantiationService.createInstance(
            WorkbenchPagedList,
            `${viewId}-Extensions`,
            parent,
            delegate,
            [renderer],
            {
                multipleSelectionSupport: false,
                setRowLineHeight: false,
                horizontalScrolling: false,
                accessibilityProvider: {
                    getAriaLabel(extension) {
                        return getAriaLabelForExtension(extension);
                    },
                    getWidgetAriaLabel() {
                        return localize(9252, "Extensions");
                    }
                },
                overrideStyles: getLocationBasedViewColors(viewDescriptorService.getViewLocationById(viewId)).listOverrideStyles,
                openOnSingleClick: true,
                ...options
            }
        );
        this._register(this.list.onContextMenu(e => this.onContextMenu(e), this));
        this._register(this.list);
        this._register(Event.debounce(
            Event.filter(this.list.onDidOpen, e => e.element !== null),
            (_, event) => event,
            75,
            true
        )(options => {
            this.openExtension(options.element, {
                sideByside: options.sideBySide,
                ...options.editorOptions
            });
        }));
    }
    setModel(model) {
        this.list.model = ( new DelayedPagedModel(model));
    }
    layout(height, width) {
        this.list.layout(height, width);
    }
    openExtension(extension, options) {
        extension = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, extension.identifier))[0] || extension;
        this.extensionsWorkbenchService.open(extension, {
            ...options,
            modal: options.sideByside ? undefined : buildModalNavigationForPagedList(
                extension,
                () => this.list.model,
                (extA, extB) => areSameExtensions(extA.identifier, extB.identifier),
                (ext, modal) => this.extensionsWorkbenchService.open(ext, {
                    pinned: false,
                    modal
                }),
                this.modalNavigationDisposable,
                this.logService
            )
        });
    }
    async onContextMenu(e) {
        if (e.element) {
            const disposables = ( new DisposableStore());
            const manageExtensionAction = disposables.add(this.instantiationService.createInstance(ManageExtensionAction));
            const extension = e.element ? this.extensionsWorkbenchService.local.find(
                local => areSameExtensions(local.identifier, e.element.identifier) && (!e.element.server || e.element.server === local.server)
            ) || e.element : e.element;
            manageExtensionAction.extension = extension;
            let groups = [];
            if (manageExtensionAction.enabled) {
                groups = await manageExtensionAction.getActionGroups();
            } else if (extension) {
                groups = await getContextMenuActions(extension, this.contextKeyService, this.instantiationService);
                groups.forEach(group => group.forEach(extensionAction => {
                    if (extensionAction instanceof ExtensionAction) {
                        extensionAction.extension = extension;
                    }
                }));
            }
            const actions = [];
            for (const menuActions of groups) {
                for (const menuAction of menuActions) {
                    actions.push(menuAction);
                    if (isDisposable(menuAction)) {
                        disposables.add(menuAction);
                    }
                }
                actions.push(( new Separator()));
            }
            actions.pop();
            this.contextMenuService.showContextMenu({
                getAnchor: () => e.anchor,
                getActions: () => actions,
                actionRunner: this.contextMenuActionRunner,
                onHide: () => disposables.dispose()
            });
        }
    }
};
ExtensionsList = ( __decorate([( __param(4, IExtensionsWorkbenchService)), ( __param(5, IViewDescriptorService)), ( __param(6, IWorkbenchLayoutService)), ( __param(7, INotificationService)), ( __param(8, IContextMenuService)), ( __param(9, IContextKeyService)), ( __param(10, IInstantiationService)), ( __param(11, ILogService))], ExtensionsList));
let ExtensionsGridView = class ExtensionsGridView extends Disposable {
    constructor(parent, delegate, instantiationService) {
        super();
        this.instantiationService = instantiationService;
        this.element = append(parent, $(".extensions-grid-view"));
        this.renderer = this.instantiationService.createInstance(Renderer, {
            onFocus: Event.None,
            onBlur: Event.None,
            filters: {}
        }, {
            hoverOptions: {
                position() {
                    return HoverPosition.BELOW;
                }
            }
        });
        this.delegate = delegate;
        this.disposableStore = this._register(( new DisposableStore()));
    }
    setExtensions(extensions) {
        this.disposableStore.clear();
        extensions.forEach((e, index) => this.renderExtension(e, index));
    }
    renderExtension(extension, index) {
        const extensionContainer = append(this.element, $(".extension-container"));
        extensionContainer.style.height = `${this.delegate.getHeight()}px`;
        extensionContainer.setAttribute("tabindex", "0");
        const template = this.renderer.renderTemplate(extensionContainer);
        this.disposableStore.add(toDisposable(() => this.renderer.disposeTemplate(template)));
        const openExtensionAction = this.instantiationService.createInstance(OpenExtensionAction);
        openExtensionAction.extension = extension;
        template.name.setAttribute("tabindex", "0");
        const handleEvent = e => {
            if (e instanceof StandardKeyboardEvent && e.keyCode !== KeyCode.Enter) {
                return;
            }
            openExtensionAction.run(e.ctrlKey || e.metaKey);
            e.stopPropagation();
            e.preventDefault();
        };
        this.disposableStore.add(
            addDisposableListener(template.name, EventType.CLICK, e => handleEvent(( new StandardMouseEvent(getWindow(template.name), e))))
        );
        this.disposableStore.add(
            addDisposableListener(template.name, EventType.KEY_DOWN, e => handleEvent(( new StandardKeyboardEvent(e))))
        );
        this.disposableStore.add(addDisposableListener(
            extensionContainer,
            EventType.KEY_DOWN,
            e => handleEvent(( new StandardKeyboardEvent(e)))
        ));
        this.renderer.renderElement(extension, index, template);
    }
};
ExtensionsGridView = ( __decorate([( __param(2, IInstantiationService))], ExtensionsGridView));
class AsyncDataSource {
    hasChildren(
        {
            hasChildren
        }
    ) {
        return hasChildren;
    }
    getChildren(extensionData) {
        return extensionData.getChildren();
    }
}
class VirualDelegate {
    getHeight(element) {
        return 62;
    }
    getTemplateId(
        {
            extension
        }
    ) {
        return extension ? ExtensionRenderer.TEMPLATE_ID : UnknownExtensionRenderer.TEMPLATE_ID;
    }
}
let ExtensionRenderer = class ExtensionRenderer {
    static {
        ExtensionRenderer_1 = this;
    }
    static {
        this.TEMPLATE_ID = "extension-template";
    }
    constructor(instantiationService) {
        this.instantiationService = instantiationService;
    }
    get templateId() {
        return ExtensionRenderer_1.TEMPLATE_ID;
    }
    renderTemplate(container) {
        container.classList.add("extension");
        const iconWidget = this.instantiationService.createInstance(ExtensionIconWidget, container);
        const details = append(container, $(".details"));
        const header = append(details, $(".header"));
        const name = append(header, $("span.name"));
        const openExtensionAction = this.instantiationService.createInstance(OpenExtensionAction);
        const extensionDisposables = [addDisposableListener(name, "click", e => {
            openExtensionAction.run(e.ctrlKey || e.metaKey);
            e.stopPropagation();
            e.preventDefault();
        }), iconWidget, openExtensionAction];
        const identifier = append(header, $("span.identifier"));
        const footer = append(details, $(".footer"));
        const author = append(footer, $(".author"));
        return {
            name,
            identifier,
            author,
            extensionDisposables,
            set extensionData(extensionData) {
                iconWidget.extension = extensionData.extension;
                openExtensionAction.extension = extensionData.extension;
            }
        };
    }
    renderElement(node, index, data) {
        const extension = node.element.extension;
        data.name.textContent = extension.displayName;
        data.identifier.textContent = extension.identifier.id;
        data.author.textContent = extension.publisherDisplayName;
        data.extensionData = node.element;
    }
    disposeTemplate(templateData) {
        templateData.extensionDisposables = dispose(templateData.extensionDisposables);
    }
};
ExtensionRenderer = ExtensionRenderer_1 = ( __decorate([( __param(0, IInstantiationService))], ExtensionRenderer));
class UnknownExtensionRenderer {
    static {
        this.TEMPLATE_ID = "unknown-extension-template";
    }
    get templateId() {
        return UnknownExtensionRenderer.TEMPLATE_ID;
    }
    renderTemplate(container) {
        const messageContainer = append(container, $("div.unknown-extension"));
        append(messageContainer, $("span.error-marker")).textContent = ( localize(9253, "Error"));
        append(messageContainer, $("span.message")).textContent = ( localize(9254, "Unknown Extension:"));
        const identifier = append(messageContainer, $("span.message"));
        return {
            identifier
        };
    }
    renderElement(node, index, data) {
        data.identifier.textContent = node.element.extension.identifier.id;
    }
    disposeTemplate(data) {}
}
let OpenExtensionAction = class OpenExtensionAction extends Action {
    constructor(extensionsWorkdbenchService) {
        super("extensions.action.openExtension", "");
        this.extensionsWorkdbenchService = extensionsWorkdbenchService;
    }
    set extension(extension) {
        this._extension = extension;
    }
    run(sideByside) {
        if (this._extension) {
            return this.extensionsWorkdbenchService.open(this._extension, {
                sideByside
            });
        }
        return Promise.resolve();
    }
};
OpenExtensionAction = ( __decorate([( __param(0, IExtensionsWorkbenchService))], OpenExtensionAction));
let ExtensionsTree = class ExtensionsTree extends WorkbenchAsyncDataTree {
    constructor(
        input,
        container,
        overrideStyles,
        contextKeyService,
        listService,
        instantiationService,
        configurationService,
        extensionsWorkdbenchService
    ) {
        const delegate = ( new VirualDelegate());
        const dataSource = ( new AsyncDataSource());
        const renderers = [
            instantiationService.createInstance(ExtensionRenderer),
            instantiationService.createInstance(UnknownExtensionRenderer)
        ];
        const identityProvider = {
            getId(
                {
                    extension,
                    parent
                }
            ) {
                return parent ? this.getId(parent) + "/" + extension.identifier.id : extension.identifier.id;
            }
        };
        super("ExtensionsTree", container, delegate, renderers, dataSource, {
            indent: 40,
            identityProvider,
            multipleSelectionSupport: false,
            overrideStyles,
            accessibilityProvider: {
                getAriaLabel(extensionData) {
                    return getAriaLabelForExtension(extensionData.extension);
                },
                getWidgetAriaLabel() {
                    return localize(9252, "Extensions");
                }
            }
        }, instantiationService, contextKeyService, listService, configurationService);
        this.setInput(input);
        this.disposables.add(this.onDidChangeSelection(event => {
            if (isKeyboardEvent(event.browserEvent)) {
                extensionsWorkdbenchService.open(event.elements[0].extension, {
                    sideByside: false
                });
            }
        }));
    }
};
ExtensionsTree = ( __decorate([( __param(3, IContextKeyService)), ( __param(4, IListService)), ( __param(5, IInstantiationService)), ( __param(6, IConfigurationService)), ( __param(7, IExtensionsWorkbenchService))], ExtensionsTree));
class ExtensionData {
    constructor(extension, parent, getChildrenExtensionIds, extensionsWorkbenchService) {
        this.extension = extension;
        this.parent = parent;
        this.getChildrenExtensionIds = getChildrenExtensionIds;
        this.extensionsWorkbenchService = extensionsWorkbenchService;
        this.childrenExtensionIds = this.getChildrenExtensionIds(extension);
    }
    get hasChildren() {
        return isNonEmptyArray(this.childrenExtensionIds);
    }
    async getChildren() {
        if (this.hasChildren) {
            const result = await getExtensions(this.childrenExtensionIds, this.extensionsWorkbenchService);
            return ( result.map(extension => ( new ExtensionData(
                extension,
                this,
                this.getChildrenExtensionIds,
                this.extensionsWorkbenchService
            ))));
        }
        return null;
    }
}
async function getExtensions(extensions, extensionsWorkbenchService) {
    const localById = extensionsWorkbenchService.local.reduce((result, e) => {
        result.set(e.identifier.id.toLowerCase(), e);
        return result;
    }, ( new Map()));
    const result = [];
    const toQuery = [];
    for (const extensionId of extensions) {
        const id = extensionId.toLowerCase();
        const local = localById.get(id);
        if (local) {
            result.push(local);
        } else {
            toQuery.push(id);
        }
    }
    if (toQuery.length) {
        const galleryResult = await extensionsWorkbenchService.getExtensions(( toQuery.map(id => ({
            id
        }))), CancellationToken.None);
        result.push(...galleryResult);
    }
    return result;
}
function buildModalNavigationForPagedList(openedItem, getModel, isSame, openItem, cancellationStore, logService) {
    const model = getModel();
    if (!model) {
        return undefined;
    }
    const total = model.length;
    if (total <= 1) {
        return undefined;
    }
    let current = -1;
    for (let i = 0; i < total; i++) {
        if (model.isResolved(i) && isSame(model.get(i), openedItem)) {
            current = i;
            break;
        }
    }
    if (current === -1) {
        return undefined;
    }
    const openAtIndex = (index, item) => {
        const currentTotal = getModel()?.length ?? 0;
        openItem(item, {
            navigation: {
                total: currentTotal,
                current: index,
                navigate
            }
        });
    };
    let cts;
    const navigate = index => {
        cts?.cancel();
        cts = cancellationStore.value = ( new CancellationTokenSource());
        const token = cts.token;
        const currentModel = getModel();
        if (!currentModel || index < 0 || index >= currentModel.length) {
            return;
        }
        if (currentModel.isResolved(index)) {
            openAtIndex(index, currentModel.get(index));
        }
        else {
            currentModel.resolve(index, token).then(item => {
                if (token.isCancellationRequested) {
                    return;
                }
                openAtIndex(index, item);
            }, error => {
                if (!isCancellationError(error)) {
                    logService.error(`Error while resolving item at index ${index} for modal navigation`, error);
                }
            });
        }
    };
    return {
        navigation: {
            total,
            current,
            navigate
        }
    };
}
registerThemingParticipant((theme, collector) => {
    const focusBackground = theme.getColor(listFocusBackground);
    if (focusBackground) {
        collector.addRule(
            `.extensions-grid-view .extension-container:focus { background-color: ${focusBackground}; outline: none; }`
        );
    }
    const focusForeground = theme.getColor(listFocusForeground);
    if (focusForeground) {
        collector.addRule(
            `.extensions-grid-view .extension-container:focus { color: ${focusForeground}; }`
        );
    }
    const foregroundColor = theme.getColor(foreground);
    const editorBackgroundColor = theme.getColor(editorBackground);
    if (foregroundColor && editorBackgroundColor) {
        const authorForeground = ( foregroundColor.transparent(.9)).makeOpaque(editorBackgroundColor);
        collector.addRule(
            `.extensions-grid-view .extension-container:not(.disabled) .author { color: ${authorForeground}; }`
        );
        const disabledExtensionForeground = ( foregroundColor.transparent(.5)).makeOpaque(editorBackgroundColor);
        collector.addRule(
            `.extensions-grid-view .extension-container.disabled { color: ${disabledExtensionForeground}; }`
        );
    }
});

export { ExtensionData, ExtensionsGridView, ExtensionsList, ExtensionsTree, buildModalNavigationForPagedList, getExtensions };
