
import { registerCss } from '../../../../../../../css.js';
import { __decorate, __param } from '../../../../../../../external/tslib/tslib.es6.js';
import * as panel from './media/panel.css';
import { localize } from '../../../../nls.js';
import { append, $, isActiveElement } from '../../../../base/browser/dom.js';
import { basename } from '../../../../base/common/resources.js';
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
import { IThemeService } from '../../../../platform/theme/common/themeService.service.js';
import { CommentNode, ResourceWithCommentThreads } from '../common/commentModel.js';
import { ICommentService } from './commentService.service.js';
import { IEditorService } from '../../../services/editor/common/editorService.service.js';
import { ResourceLabels } from '../../../browser/labels.js';
import { Filter, CommentsList, COMMENTS_VIEW_TITLE } from './commentsTreeViewer.js';
import { FilterViewPane } from '../../../browser/parts/views/viewPane.js';
import { IViewDescriptorService } from '../../../common/views.service.js';
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.service.js';
import { RawContextKey } from '../../../../platform/contextkey/common/contextkey.js';
import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.service.js';
import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.service.js';
import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.service.js';
import { IOpenerService } from '../../../../platform/opener/common/opener.service.js';
import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.service.js';
import { CommentsViewFilterFocusContextKey } from './comments.js';
import { CommentsFilters, CommentsSortOrder } from './commentsViewActions.js';
import { Memento } from '../../../common/memento.js';
import { StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
import { IStorageService } from '../../../../platform/storage/common/storage.service.js';
import { FilterOptions } from './commentsFilterOptions.js';
import { CommentThreadApplicability } from '../../../../editor/common/languages.js';
import { revealCommentThread } from './commentsController.js';
import { registerNavigableContainer } from '../../../browser/actions/widgetNavigationCommands.js';
import { threadHasMeaningfulComments, CommentsModel } from './commentsModel.js';
import { IHoverService } from '../../../../platform/hover/browser/hover.service.js';
import { AccessibilityVerbositySettingId } from '../../accessibility/browser/accessibilityConfiguration.js';
import { AccessibleViewAction } from '../../accessibility/browser/accessibleViewActions.js';
import { IPathService } from '../../../services/path/common/pathService.service.js';
import { isCodeEditor } from '../../../../editor/browser/editorBrowser.js';

registerCss(panel);
const CONTEXT_KEY_HAS_COMMENTS = ( new RawContextKey("commentsView.hasComments", false));
const CONTEXT_KEY_SOME_COMMENTS_EXPANDED = ( new RawContextKey("commentsView.someCommentsExpanded", false));
const CONTEXT_KEY_COMMENT_FOCUSED = ( new RawContextKey("commentsView.commentFocused", false));
const VIEW_STORAGE_ID = "commentsViewState";
function createResourceCommentsIterator(model) {
    const result = [];
    for (const m of model.resourceCommentThreads) {
        const children = [];
        for (const r of m.commentThreads) {
            if (threadHasMeaningfulComments(r.thread)) {
                children.push({
                    element: r
                });
            }
        }
        if (children.length > 0) {
            result.push({
                element: m,
                children
            });
        }
    }
    return result;
}
let CommentsPanel = class CommentsPanel extends FilterViewPane {
    get focusedCommentNode() {
        const focused = this.tree?.getFocus();
        if (focused?.length === 1 && focused[0] instanceof CommentNode) {
            return focused[0];
        }
        return undefined;
    }
    get focusedCommentInfo() {
        if (!this.focusedCommentNode) {
            return;
        }
        return this.getScreenReaderInfoForNode(this.focusedCommentNode);
    }
    focusNextNode() {
        if (!this.tree) {
            return;
        }
        const focused = this.tree.getFocus()?.[0];
        if (!focused) {
            return;
        }
        let next = this.tree.navigate(focused).next();
        while (next && !(next instanceof CommentNode)) {
            next = this.tree.navigate(next).next();
        }
        if (!next) {
            return;
        }
        this.tree.setFocus([next]);
    }
    focusPreviousNode() {
        if (!this.tree) {
            return;
        }
        const focused = this.tree.getFocus()?.[0];
        if (!focused) {
            return;
        }
        let previous = this.tree.navigate(focused).previous();
        while (previous && !(previous instanceof CommentNode)) {
            previous = this.tree.navigate(previous).previous();
        }
        if (!previous) {
            return;
        }
        this.tree.setFocus([previous]);
    }
    constructor(
        options,
        instantiationService,
        viewDescriptorService,
        editorService,
        configurationService,
        contextKeyService,
        contextMenuService,
        keybindingService,
        openerService,
        themeService,
        commentService,
        hoverService,
        uriIdentityService,
        storageService,
        pathService
    ) {
        const stateMemento = ( new Memento(VIEW_STORAGE_ID, storageService));
        const viewState = stateMemento.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE);
        super({
            ...options,
            filterOptions: {
                placeholder: ( localize(8677, "Filter (e.g. text, author)")),
                ariaLabel: ( localize(8678, "Filter comments")),
                history: viewState.filterHistory || [],
                text: viewState.filter || "",
                focusContextKey: CommentsViewFilterFocusContextKey.key
            }
        }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, hoverService);
        this.editorService = editorService;
        this.commentService = commentService;
        this.uriIdentityService = uriIdentityService;
        this.pathService = pathService;
        this.totalComments = 0;
        this.currentHeight = 0;
        this.currentWidth = 0;
        this.cachedFilterStats = undefined;
        this.onDidChangeVisibility = this.onDidChangeBodyVisibility;
        this.hasCommentsContextKey = CONTEXT_KEY_HAS_COMMENTS.bindTo(contextKeyService);
        this.someCommentsExpandedContextKey = CONTEXT_KEY_SOME_COMMENTS_EXPANDED.bindTo(contextKeyService);
        this.commentsFocusedContextKey = CONTEXT_KEY_COMMENT_FOCUSED.bindTo(contextKeyService);
        this.stateMemento = stateMemento;
        this.viewState = viewState;
        this.filters = this._register(( new CommentsFilters({
            showResolved: this.viewState.showResolved !== false,
            showUnresolved: this.viewState.showUnresolved !== false,
            sortBy: this.viewState.sortBy ?? CommentsSortOrder.ResourceAscending
        }, this.contextKeyService)));
        this.filter = ( new Filter(( new FilterOptions(
            this.filterWidget.getFilterText(),
            this.filters.showResolved,
            this.filters.showUnresolved
        ))));
        this._register(this.filters.onDidChange(event => {
            if (event.showResolved || event.showUnresolved) {
                this.updateFilter();
            }
            if (event.sortBy) {
                this.refresh();
            }
        }));
        this._register(this.filterWidget.onDidChangeFilterText(() => this.updateFilter()));
    }
    saveState() {
        this.viewState.filter = this.filterWidget.getFilterText();
        this.viewState.filterHistory = this.filterWidget.getHistory();
        this.viewState.showResolved = this.filters.showResolved;
        this.viewState.showUnresolved = this.filters.showUnresolved;
        this.viewState.sortBy = this.filters.sortBy;
        this.stateMemento.saveMemento();
        super.saveState();
    }
    render() {
        super.render();
        this._register(registerNavigableContainer({
            name: "commentsView",
            focusNotifiers: [this, this.filterWidget],
            focusNextWidget: () => {
                if (this.filterWidget.hasFocus()) {
                    this.focus();
                }
            },
            focusPreviousWidget: () => {
                if (!this.filterWidget.hasFocus()) {
                    this.focusFilter();
                }
            }
        }));
    }
    focusFilter() {
        this.filterWidget.focus();
    }
    clearFilterText() {
        this.filterWidget.setFilterText("");
    }
    getFilterStats() {
        if (!this.cachedFilterStats) {
            this.cachedFilterStats = {
                total: this.totalComments,
                filtered: this.tree?.getVisibleItemCount() ?? 0
            };
        }
        return this.cachedFilterStats;
    }
    updateFilter() {
        this.filter.options = ( new FilterOptions(
            this.filterWidget.getFilterText(),
            this.filters.showResolved,
            this.filters.showUnresolved
        ));
        this.tree?.filterComments();
        this.cachedFilterStats = undefined;
        const {
            total,
            filtered
        } = this.getFilterStats();
        this.filterWidget.updateBadge(total === filtered || total === 0 ? undefined : ( localize(8679, "Showing {0} of {1}", filtered, total)));
        this.filterWidget.checkMoreFilters(!this.filters.showResolved || !this.filters.showUnresolved);
    }
    renderBody(container) {
        super.renderBody(container);
        container.classList.add("comments-panel");
        const domContainer = append(container, $(".comments-panel-container"));
        this.treeContainer = append(domContainer, $(".tree-container"));
        this.treeContainer.classList.add("file-icon-themable-tree", "show-file-icons");
        this.cachedFilterStats = undefined;
        this.createTree();
        this.createMessageBox(domContainer);
        this._register(
            this.commentService.onDidSetAllCommentThreads(this.onAllCommentsChanged, this)
        );
        this._register(
            this.commentService.onDidUpdateCommentThreads(this.onCommentsUpdated, this)
        );
        this._register(
            this.commentService.onDidDeleteDataProvider(this.onDataProviderDeleted, this)
        );
        this._register(this.onDidChangeBodyVisibility(visible => {
            if (visible) {
                this.refresh();
            }
        }));
        this.renderComments();
    }
    focus() {
        super.focus();
        const element = this.tree?.getHTMLElement();
        if (element && isActiveElement(element)) {
            return;
        }
        if (!this.commentService.commentsModel.hasCommentThreads() && this.messageBoxContainer) {
            this.messageBoxContainer.focus();
        } else if (this.tree) {
            this.tree.domFocus();
        }
    }
    renderComments() {
        this.treeContainer.classList.toggle("hidden", !this.commentService.commentsModel.hasCommentThreads());
        this.renderMessage();
        this.tree?.setChildren(null, createResourceCommentsIterator(this.commentService.commentsModel));
    }
    collapseAll() {
        if (this.tree) {
            this.tree.collapseAll();
            this.tree.setSelection([]);
            this.tree.setFocus([]);
            this.tree.domFocus();
            this.tree.focusFirst();
        }
    }
    expandAll() {
        if (this.tree) {
            this.tree.expandAll();
            this.tree.setSelection([]);
            this.tree.setFocus([]);
            this.tree.domFocus();
            this.tree.focusFirst();
        }
    }
    get hasRendered() {
        return !!this.tree;
    }
    layoutBodyContent(height = this.currentHeight, width = this.currentWidth) {
        if (this.messageBoxContainer) {
            this.messageBoxContainer.style.height = `${height}px`;
        }
        this.tree?.layout(height, width);
        this.currentHeight = height;
        this.currentWidth = width;
    }
    createMessageBox(parent) {
        this.messageBoxContainer = append(parent, $(".message-box-container"));
        this.messageBoxContainer.setAttribute("tabIndex", "0");
    }
    renderMessage() {
        this.messageBoxContainer.textContent = this.commentService.commentsModel.getMessage();
        this.messageBoxContainer.classList.toggle("hidden", this.commentService.commentsModel.hasCommentThreads());
    }
    makeCommentLocationLabel(file, range) {
        const fileLabel = basename(file);
        if (!range) {
            return localize(8680, "in {0}", fileLabel);
        }
        if (range.startLineNumber === range.endLineNumber) {
            return localize(
                8681,
                "at line {0} column {1} in {2}",
                range.startLineNumber,
                range.startColumn,
                fileLabel
            );
        } else {
            return localize(
                8682,
                "from line {0} to line {1} in {2}",
                range.startLineNumber,
                range.endLineNumber,
                fileLabel
            );
        }
    }
    makeScreenReaderLabelInfo(element, forAriaLabel) {
        const userName = element.comment.userName;
        const locationLabel = this.makeCommentLocationLabel(element.resource, element.range);
        const replyCountLabel = this.getReplyCountAsString(element, forAriaLabel);
        const bodyLabel = (typeof element.comment.body === "string") ? element.comment.body : element.comment.body.value;
        return {
            userName,
            locationLabel,
            replyCountLabel,
            bodyLabel
        };
    }
    getScreenReaderInfoForNode(element, forAriaLabel) {
        let accessibleViewHint = "";
        if (forAriaLabel && this.configurationService.getValue(AccessibilityVerbositySettingId.Comments)) {
            const kbLabel = this.keybindingService.lookupKeybinding(AccessibleViewAction.id)?.getAriaLabel();
            accessibleViewHint = kbLabel ? ( localize(8683, "\nInspect this in the accessible view ({0}).", kbLabel)) : ( localize(
                8684,
                "\nInspect this in the accessible view via the command Open Accessible View which is currently not triggerable via keybinding."
            ));
        }
        const replies = this.getRepliesAsString(element, forAriaLabel);
        const editor = this.editorService.findEditors(element.resource);
        const codeEditor = this.editorService.activeEditorPane?.getControl();
        let relevantLines;
        if (element.range && editor?.length && isCodeEditor(codeEditor)) {
            relevantLines = codeEditor.getModel()?.getValueInRange(element.range);
            if (relevantLines) {
                relevantLines = "\nCorresponding code: \n" + relevantLines;
            }
        }
        if (!relevantLines) {
            relevantLines = "";
        }
        const labelInfo = this.makeScreenReaderLabelInfo(element, forAriaLabel);
        if (element.threadRelevance === CommentThreadApplicability.Outdated) {
            return ( localize(
                8685,
                "Outdated from {0}: {1}\n{2}\n{3}\n{4}",
                labelInfo.userName,
                labelInfo.bodyLabel,
                labelInfo.locationLabel,
                labelInfo.replyCountLabel,
                relevantLines
            )) + replies + accessibleViewHint;
        } else {
            return ( localize(
                8686,
                "{0}: {1}\n{2}\n{3}\n{4}",
                labelInfo.userName,
                labelInfo.bodyLabel,
                labelInfo.locationLabel,
                labelInfo.replyCountLabel,
                relevantLines
            )) + replies + accessibleViewHint;
        }
    }
    getRepliesAsString(node, forAriaLabel) {
        if (!node.replies.length || forAriaLabel) {
            return "";
        }
        return "\n" + ( node.replies.map(reply => ( localize(
            8687,
            "{0} {1}",
            reply.comment.userName,
            (typeof reply.comment.body === "string") ? reply.comment.body : reply.comment.body.value
        )))).join("\n");
    }
    getReplyCountAsString(node, forAriaLabel) {
        return node.replies.length && !forAriaLabel ? ( localize(8688, " {0} replies,", node.replies.length)) : "";
    }
    createTree() {
        this.treeLabels = this._register(this.instantiationService.createInstance(ResourceLabels, this));
        this.tree = this._register(
            this.instantiationService.createInstance(CommentsList, this.treeLabels, this.treeContainer, {
                overrideStyles: this.getLocationBasedColors().listOverrideStyles,
                selectionNavigation: true,
                filter: this.filter,
                sorter: {
                    compare: (a, b) => {
                        if (a instanceof CommentsModel || b instanceof CommentsModel) {
                            return 0;
                        }
                        if (this.filters.sortBy === CommentsSortOrder.UpdatedAtDescending) {
                            return a.lastUpdatedAt > b.lastUpdatedAt ? -1 : 1;
                        } else if (this.filters.sortBy === CommentsSortOrder.ResourceAscending) {
                            if (a instanceof ResourceWithCommentThreads && b instanceof ResourceWithCommentThreads) {
                                const workspaceScheme = this.pathService.defaultUriScheme;
                                if ((a.resource.scheme !== b.resource.scheme) && (a.resource.scheme === workspaceScheme || b.resource.scheme === workspaceScheme)) {
                                    return b.resource.scheme === workspaceScheme ? 1 : -1;
                                }
                                return ( a.resource.toString()) > ( b.resource.toString()) ? 1 : -1;
                            } else if (a instanceof CommentNode && b instanceof CommentNode && a.thread.range && b.thread.range) {
                                return a.thread.range?.startLineNumber > b.thread.range?.startLineNumber ? 1 : -1;
                            }
                        }
                        return 0;
                    }
                },
                keyboardNavigationLabelProvider: {
                    getKeyboardNavigationLabel: item => {
                        return undefined;
                    }
                },
                accessibilityProvider: {
                    getAriaLabel: element => {
                        if (element instanceof CommentsModel) {
                            return localize(8689, "Comments for current workspace");
                        }
                        if (element instanceof ResourceWithCommentThreads) {
                            return localize(
                                8690,
                                "Comments in {0}, full path {1}",
                                basename(element.resource),
                                element.resource.fsPath
                            );
                        }
                        if (element instanceof CommentNode) {
                            return this.getScreenReaderInfoForNode(element, true);
                        }
                        return "";
                    },
                    getWidgetAriaLabel() {
                        return COMMENTS_VIEW_TITLE.value;
                    }
                }
            })
        );
        this._register(this.tree.onDidOpen(e => {
            this.openFile(
                e.element,
                e.editorOptions.pinned,
                e.editorOptions.preserveFocus,
                e.sideBySide
            );
        }));
        this._register(this.tree.onDidChangeModel(() => {
            this.updateSomeCommentsExpanded();
        }));
        this._register(this.tree.onDidChangeCollapseState(() => {
            this.updateSomeCommentsExpanded();
        }));
        this._register(this.tree.onDidFocus(() => this.commentsFocusedContextKey.set(true)));
        this._register(this.tree.onDidBlur(() => this.commentsFocusedContextKey.set(false)));
    }
    openFile(element, pinned, preserveFocus, sideBySide) {
        if (!element) {
            return;
        }
        if (!(element instanceof ResourceWithCommentThreads || element instanceof CommentNode)) {
            return;
        }
        const threadToReveal = element instanceof ResourceWithCommentThreads ? element.commentThreads[0].thread : element.thread;
        const commentToReveal = element instanceof ResourceWithCommentThreads ? element.commentThreads[0].comment : undefined;
        return revealCommentThread(
            this.commentService,
            this.editorService,
            this.uriIdentityService,
            threadToReveal,
            commentToReveal,
            false,
            pinned,
            preserveFocus,
            sideBySide
        );
    }
    async refresh() {
        if (!this.tree) {
            return;
        }
        if (this.isVisible()) {
            this.hasCommentsContextKey.set(this.commentService.commentsModel.hasCommentThreads());
            this.cachedFilterStats = undefined;
            this.renderComments();
            if (this.tree.getSelection().length === 0 && this.commentService.commentsModel.hasCommentThreads()) {
                const firstComment = this.commentService.commentsModel.resourceCommentThreads[0].commentThreads[0];
                if (firstComment) {
                    this.tree.setFocus([firstComment]);
                    this.tree.setSelection([firstComment]);
                }
            }
        }
    }
    onAllCommentsChanged(e) {
        this.cachedFilterStats = undefined;
        this.totalComments += e.commentThreads.length;
        for (const thread of e.commentThreads) {
        }
        this.refresh();
    }
    onCommentsUpdated(e) {
        this.cachedFilterStats = undefined;
        this.totalComments += e.added.length;
        this.totalComments -= e.removed.length;
        for (const resource of this.commentService.commentsModel.resourceCommentThreads) {
            for (const thread of resource.commentThreads) {
            }
        }
        this.refresh();
    }
    onDataProviderDeleted(owner) {
        this.cachedFilterStats = undefined;
        this.totalComments = 0;
        this.refresh();
    }
    updateSomeCommentsExpanded() {
        this.someCommentsExpandedContextKey.set(this.isSomeCommentsExpanded());
    }
    areAllCommentsExpanded() {
        if (!this.tree) {
            return false;
        }
        const navigator = this.tree.navigate();
        while (navigator.next()) {
            if (this.tree.isCollapsed(navigator.current())) {
                return false;
            }
        }
        return true;
    }
    isSomeCommentsExpanded() {
        if (!this.tree) {
            return false;
        }
        const navigator = this.tree.navigate();
        while (navigator.next()) {
            if (!this.tree.isCollapsed(navigator.current())) {
                return true;
            }
        }
        return false;
    }
};
CommentsPanel = ( __decorate([( __param(1, IInstantiationService)), ( __param(2, IViewDescriptorService)), ( __param(3, IEditorService)), ( __param(4, IConfigurationService)), ( __param(5, IContextKeyService)), ( __param(6, IContextMenuService)), ( __param(7, IKeybindingService)), ( __param(8, IOpenerService)), ( __param(9, IThemeService)), ( __param(10, ICommentService)), ( __param(11, IHoverService)), ( __param(12, IUriIdentityService)), ( __param(13, IStorageService)), ( __param(14, IPathService))], CommentsPanel));

export { CONTEXT_KEY_COMMENT_FOCUSED, CONTEXT_KEY_HAS_COMMENTS, CONTEXT_KEY_SOME_COMMENTS_EXPANDED, CommentsPanel };
