
import { registerCss } from '../../../../../../css.js';
import * as compositepart from './media/compositepart.css';
import { localize } from '../../../nls.js';
import { defaultGenerator } from '../../../base/common/idGenerator.js';
import { MutableDisposable, DisposableStore, dispose } from '../../../base/common/lifecycle.js';
import { Emitter } from '../../../base/common/event.js';
import { isCancellationError } from '../../../base/common/errors.js';
import { prepareActions, ActionsOrientation } from '../../../base/browser/ui/actionbar/actionbar.js';
import { ProgressBar } from '../../../base/browser/ui/progressbar/progressbar.js';
import { Part } from '../part.js';
import { StorageScope, StorageTarget } from '../../../platform/storage/common/storage.js';
import { ServiceCollection } from '../../../platform/instantiation/common/serviceCollection.js';
import { IEditorProgressService } from '../../../platform/progress/common/progress.service.js';
import { $, show, hide, append, Dimension } from '../../../base/browser/dom.js';
import '../../../base/browser/ui/contextview/contextview.js';
import { assertReturnsDefined } from '../../../base/common/types.js';
import { createActionViewItem } from '../../../platform/actions/browser/menuEntryActionViewItem.js';
import { ScopedProgressIndicator, AbstractProgressScope } from '../../services/progress/browser/progressIndicator.js';
import { WorkbenchToolBar } from '../../../platform/actions/browser/toolbar.js';
import { defaultProgressBarStyles } from '../../../platform/theme/browser/defaultStyles.js';
import { createInstantHoverDelegate, getDefaultHoverDelegate } from '../../../base/browser/ui/hover/hoverDelegateFactory.js';
import { AnchorAlignment } from '../../../base/common/layout.js';

registerCss(compositepart);
class CompositePart extends Part {
    constructor(
        notificationService,
        storageService,
        contextMenuService,
        layoutService,
        keybindingService,
        hoverService,
        instantiationService,
        themeService,
        registry,
        activeCompositeSettingsKey,
        defaultCompositeId,
        nameForTelemetry,
        compositeCSSClass,
        titleForegroundColor,
        titleBorderColor,
        id,
        options
    ) {
        super(id, options, themeService, storageService, layoutService);
        this.notificationService = notificationService;
        this.storageService = storageService;
        this.contextMenuService = contextMenuService;
        this.keybindingService = keybindingService;
        this.hoverService = hoverService;
        this.instantiationService = instantiationService;
        this.registry = registry;
        this.activeCompositeSettingsKey = activeCompositeSettingsKey;
        this.defaultCompositeId = defaultCompositeId;
        this.nameForTelemetry = nameForTelemetry;
        this.compositeCSSClass = compositeCSSClass;
        this.titleForegroundColor = titleForegroundColor;
        this.titleBorderColor = titleBorderColor;
        this.onDidCompositeOpen = this._register(( new Emitter()));
        this.onDidCompositeClose = this._register(( new Emitter()));
        this.mapCompositeToCompositeContainer = ( new Map());
        this.mapActionsBindingToComposite = ( new Map());
        this.instantiatedCompositeItems = ( new Map());
        this.actionsListener = this._register(( new MutableDisposable()));
        this.lastActiveCompositeId = storageService.get(
            activeCompositeSettingsKey,
            StorageScope.WORKSPACE,
            this.defaultCompositeId
        );
        this.toolbarHoverDelegate = this._register(createInstantHoverDelegate());
        this.trailingSeparator = options.trailingSeparator ?? false;
    }
    openComposite(id, focus) {
        if (this.activeComposite?.getId() === id) {
            if (focus) {
                this.activeComposite.focus();
            }
            return this.activeComposite;
        }
        if (!this.element) {
            return;
        }
        return this.doOpenComposite(id, focus);
    }
    doOpenComposite(id, focus = false) {
        const currentCompositeOpenToken = defaultGenerator.nextId();
        this.currentCompositeOpenToken = currentCompositeOpenToken;
        if (this.activeComposite) {
            this.hideActiveComposite();
        }
        this.updateTitle(id);
        const composite = this.createComposite(id, true);
        if ((this.currentCompositeOpenToken !== currentCompositeOpenToken) || (this.activeComposite && this.activeComposite.getId() !== composite.getId())) {
            return undefined;
        }
        if (this.activeComposite?.getId() === composite.getId()) {
            if (focus) {
                composite.focus();
            }
            this.onDidCompositeOpen.fire({
                composite,
                focus
            });
            return composite;
        }
        this.showComposite(composite);
        if (focus) {
            composite.focus();
        }
        if (composite) {
            this.onDidCompositeOpen.fire({
                composite,
                focus
            });
        }
        return composite;
    }
    createComposite(id, isActive) {
        const compositeItem = this.instantiatedCompositeItems.get(id);
        if (compositeItem) {
            return compositeItem.composite;
        }
        const compositeDescriptor = this.registry.getComposite(id);
        if (compositeDescriptor) {
            const that = this;
            const compositeProgressIndicator = ( new ScopedProgressIndicator(
                assertReturnsDefined(this.progressBar),
                this._register(new (class extends AbstractProgressScope {
                    constructor() {
                        super(compositeDescriptor.id, !!isActive);
                        this._register(
                            that.onDidCompositeOpen.event(e => this.onScopeOpened(e.composite.getId()))
                        );
                        this._register(that.onDidCompositeClose.event(e => this.onScopeClosed(e.getId())));
                    }
                })())
            ));
            const compositeInstantiationService = this._register(this.instantiationService.createChild(( new ServiceCollection(
                [IEditorProgressService, compositeProgressIndicator]
            ))));
            const composite = compositeDescriptor.instantiate(compositeInstantiationService);
            const disposable = ( new DisposableStore());
            this.instantiatedCompositeItems.set(id, {
                composite,
                disposable,
                progress: compositeProgressIndicator
            });
            disposable.add(
                composite.onTitleAreaUpdate(() => this.onTitleAreaUpdate(composite.getId()), this)
            );
            disposable.add(compositeInstantiationService);
            return composite;
        }
        throw ( new Error(`Unable to find composite with id ${id}`));
    }
    showComposite(composite) {
        this.activeComposite = composite;
        const id = this.activeComposite.getId();
        if (id !== this.defaultCompositeId) {
            this.storageService.store(
                this.activeCompositeSettingsKey,
                id,
                StorageScope.WORKSPACE,
                StorageTarget.MACHINE
            );
        } else {
            this.storageService.remove(this.activeCompositeSettingsKey, StorageScope.WORKSPACE);
        }
        this.lastActiveCompositeId = this.activeComposite.getId();
        let compositeContainer = this.mapCompositeToCompositeContainer.get(composite.getId());
        if (!compositeContainer) {
            compositeContainer = $(".composite");
            compositeContainer.classList.add(...this.compositeCSSClass.split(" "));
            compositeContainer.id = composite.getId();
            composite.create(compositeContainer);
            composite.updateStyles();
            this.mapCompositeToCompositeContainer.set(composite.getId(), compositeContainer);
        }
        if (!this.activeComposite || composite.getId() !== this.activeComposite.getId()) {
            return undefined;
        }
        this.contentArea?.appendChild(compositeContainer);
        show(compositeContainer);
        if (this.toolBar) {
            this.toolBar.actionRunner = composite.getActionRunner();
        }
        const descriptor = this.registry.getComposite(composite.getId());
        if (descriptor && descriptor.name !== composite.getTitle()) {
            this.updateTitle(composite.getId(), composite.getTitle());
        }
        let actionsBinding = this.mapActionsBindingToComposite.get(composite.getId());
        if (!actionsBinding) {
            actionsBinding = this.collectCompositeActions(composite);
            this.mapActionsBindingToComposite.set(composite.getId(), actionsBinding);
        }
        actionsBinding();
        if (this.toolBar) {
            this.actionsListener.value = this.toolBar.actionRunner.onDidRun(e => {
                if (e.error && !isCancellationError(e.error)) {
                    this.notificationService.error(e.error);
                }
            });
        }
        composite.setVisible(true);
        if (!this.activeComposite || composite.getId() !== this.activeComposite.getId()) {
            return;
        }
        if (this.contentAreaSize) {
            composite.layout(this.contentAreaSize);
        }
        if (this.boundarySashes) {
            composite.setBoundarySashes(this.boundarySashes);
        }
    }
    onTitleAreaUpdate(compositeId) {
        const composite = this.instantiatedCompositeItems.get(compositeId);
        if (composite) {
            this.updateTitle(compositeId, composite.composite.getTitle());
        }
        if (this.activeComposite?.getId() === compositeId) {
            const actionsBinding = this.collectCompositeActions(this.activeComposite);
            this.mapActionsBindingToComposite.set(this.activeComposite.getId(), actionsBinding);
            actionsBinding();
        }
        else {
            this.mapActionsBindingToComposite.delete(compositeId);
        }
    }
    updateTitle(compositeId, compositeTitle) {
        const compositeDescriptor = this.registry.getComposite(compositeId);
        if (!compositeDescriptor || !this.titleLabel) {
            return;
        }
        if (!compositeTitle) {
            compositeTitle = compositeDescriptor.name;
        }
        const keybinding = this.keybindingService.lookupKeybinding(compositeId);
        this.titleLabel.updateTitle(compositeId, compositeTitle, keybinding?.getLabel() ?? undefined);
        this.toolBar?.setAriaLabel(( localize(3103, "{0} actions", compositeTitle)));
    }
    collectCompositeActions(composite) {
        const menuIds = composite?.getMenuIds();
        const primaryActions = composite?.getActions().slice(0) || [];
        const secondaryActions = composite?.getSecondaryActions().slice(0) || [];
        if (this.toolBar) {
            this.toolBar.context = this.actionsContextProvider();
        }
        return () => {
            this.toolBar?.setActions(prepareActions(primaryActions), prepareActions(secondaryActions), menuIds);
            this.titleArea?.classList.toggle("has-actions", primaryActions.length > 0 || secondaryActions.length > 0);
        };
    }
    getActiveComposite() {
        return this.activeComposite;
    }
    getLastActiveCompositeId() {
        return this.lastActiveCompositeId;
    }
    hideActiveComposite() {
        if (!this.activeComposite) {
            return undefined;
        }
        const composite = this.activeComposite;
        this.activeComposite = undefined;
        const compositeContainer = this.mapCompositeToCompositeContainer.get(composite.getId());
        composite.setVisible(false);
        if (compositeContainer) {
            compositeContainer.remove();
            hide(compositeContainer);
        }
        this.progressBar?.stop().hide();
        if (this.toolBar) {
            this.collectCompositeActions()();
        }
        this.onDidCompositeClose.fire(composite);
        return composite;
    }
    createTitleArea(parent) {
        if (!this.options.hasTitle) {
            return undefined;
        }
        const titleArea = append(parent, $(".composite"));
        titleArea.classList.add("title");
        this.titleLabel = this.createTitleLabel(titleArea);
        const titleActionsContainer = append(titleArea, $(".title-actions"));
        this.toolBar = this._register(
            this.instantiationService.createInstance(WorkbenchToolBar, titleActionsContainer, {
                actionViewItemProvider: (action, options) => this.actionViewItemProvider(action, options),
                orientation: ActionsOrientation.HORIZONTAL,
                getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id),
                anchorAlignmentProvider: () => this.getTitleAreaDropDownAnchorAlignment(),
                toggleMenuTitle: ( localize(3104, "Views and More Actions...")),
                telemetrySource: this.nameForTelemetry,
                hoverDelegate: this.toolbarHoverDelegate,
                trailingSeparator: this.trailingSeparator
            })
        );
        this.collectCompositeActions()();
        return titleArea;
    }
    createTitleLabel(parent) {
        const titleContainer = append(parent, $(".title-label"));
        const titleLabel = append(titleContainer, $("h2"));
        this.titleLabelElement = titleLabel;
        const hover = this._register(
            this.hoverService.setupManagedHover(getDefaultHoverDelegate("mouse"), titleLabel, "")
        );
        const $this = this;
        return {
            updateTitle: (id, title, keybinding) => {
                if (!this.activeComposite || this.activeComposite.getId() === id) {
                    titleLabel.textContent = title;
                    hover.update(keybinding ? ( localize(3105, "{0} ({1})", title, keybinding)) : title);
                }
            },
            updateStyles: () => {
                titleLabel.style.color = $this.titleForegroundColor ? $this.getColor($this.titleForegroundColor) || "" : "";
                const borderColor = $this.titleBorderColor ? $this.getColor($this.titleBorderColor) : undefined;
                parent.style.borderBottom = borderColor ? `1px solid ${borderColor}` : "";
            }
        };
    }
    createHeaderArea() {
        return $(".composite");
    }
    createFooterArea() {
        return $(".composite");
    }
    updateStyles() {
        super.updateStyles();
        this.titleLabel?.updateStyles();
    }
    actionViewItemProvider(action, options) {
        if (this.activeComposite) {
            return this.activeComposite.getActionViewItem(action, options);
        }
        return createActionViewItem(this.instantiationService, action, options);
    }
    actionsContextProvider() {
        if (this.activeComposite) {
            return this.activeComposite.getActionsContext();
        }
        return null;
    }
    createContentArea(parent) {
        const contentContainer = append(parent, $(".content"));
        this.progressBar = this._register(( new ProgressBar(contentContainer, defaultProgressBarStyles)));
        this.progressBar.hide();
        return contentContainer;
    }
    getProgressIndicator(id) {
        const compositeItem = this.instantiatedCompositeItems.get(id);
        return compositeItem ? compositeItem.progress : undefined;
    }
    getTitleAreaDropDownAnchorAlignment() {
        return AnchorAlignment.RIGHT;
    }
    layout(width, height, top, left) {
        super.layout(width, height, top, left);
        this.contentAreaSize = Dimension.lift(super.layoutContents(width, height).contentSize);
        this.activeComposite?.layout(this.contentAreaSize);
    }
    setBoundarySashes(sashes) {
        this.boundarySashes = sashes;
        this.activeComposite?.setBoundarySashes(sashes);
    }
    removeComposite(compositeId) {
        if (this.activeComposite?.getId() === compositeId) {
            return false;
        }
        this.mapCompositeToCompositeContainer.delete(compositeId);
        this.mapActionsBindingToComposite.delete(compositeId);
        const compositeItem = this.instantiatedCompositeItems.get(compositeId);
        if (compositeItem) {
            compositeItem.composite.dispose();
            dispose(compositeItem.disposable);
            this.instantiatedCompositeItems.delete(compositeId);
        }
        return true;
    }
    dispose() {
        this.mapCompositeToCompositeContainer.clear();
        this.mapActionsBindingToComposite.clear();
        this.instantiatedCompositeItems.forEach(compositeItem => {
            compositeItem.composite.dispose();
            dispose(compositeItem.disposable);
        });
        this.instantiatedCompositeItems.clear();
        super.dispose();
    }
}

export { CompositePart };
