"use strict";
var MainThreadTreeView_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.TreeViewDragAndDropController = exports.TreeViewDataProvider = exports.MainThreadTreeView = void 0;
const tslib_1 = require("tslib");
const di_1 = require("@opensumi/di");
const ide_components_1 = require("@opensumi/ide-components");
const ide_connection_1 = require("@opensumi/ide-connection");
const ide_core_browser_1 = require("@opensumi/ide-core-browser");
const next_1 = require("@opensumi/ide-core-browser/lib/menu/next");
const progress_1 = require("@opensumi/ide-core-browser/lib/progress");
const ide_file_service_1 = require("@opensumi/ide-file-service");
const ide_main_layout_1 = require("@opensumi/ide-main-layout");
const ide_theme_1 = require("@opensumi/ide-theme");
const vscode_1 = require("../../../common/vscode");
const converter_1 = require("../../../common/vscode/converter");
const data_transfer_1 = require("../../../common/vscode/data-transfer");
const data_transfer_cache_1 = require("../../../common/vscode/data-transfer-cache");
const ext_types_1 = require("../../../common/vscode/ext-types");
const components_1 = require("../../components");
const tree_view_model_service_1 = require("./tree-view/tree-view.model.service");
const tree_view_node_defined_1 = require("./tree-view/tree-view.node.defined");
let MainThreadTreeView = MainThreadTreeView_1 = class MainThreadTreeView extends ide_core_browser_1.WithEventBus {
    constructor(rpcProtocol, extensionHostType) {
        super();
        this.rpcProtocol = rpcProtocol;
        this.extensionHostType = extensionHostType;
        this.treeModels = new Map();
        this.dndControllers = new Map();
        this.disposableCollection = new Map();
        this.disposable = new ide_core_browser_1.DisposableStore();
        this.proxy = this.rpcProtocol.getProxy(vscode_1.ExtHostAPIIdentifier.ExtHostTreeView);
        this.disposable.add((0, ide_core_browser_1.toDisposable)(() => this.treeModels.clear()));
        this.disposable.add((0, ide_core_browser_1.toDisposable)(() => this.dndControllers.clear()));
        this._registerInternalCommands();
        this.fileServiceClient.getCurrentUserHome().then((home) => {
            if (home) {
                this.userhome = new ide_core_browser_1.URI(home.uri);
            }
        });
        this.addDispose(this.eventBus.on(ide_main_layout_1.ViewCollapseChangedEvent, (e) => {
            if (e.payload.viewId && this.treeModels.has(e.payload.viewId) && !e.payload.collapsed) {
                const treeModel = this.treeModels.get(e.payload.viewId);
                if (treeModel) {
                    this.progressService.withProgress({ location: e.payload.viewId }, () => treeModel.refresh());
                }
            }
        }));
    }
    dispose() {
        this.disposable.dispose();
        this.disposableCollection.forEach((item) => {
            item.dispose();
        });
        this.disposableCollection.clear();
    }
    createTreeModel(treeViewId, dataProvider, dndController, options) {
        return tree_view_model_service_1.ExtensionTreeViewModel.createModel(this.injector, dataProvider, dndController, treeViewId, options || {});
    }
    async $registerTreeDataProvider(treeViewId, options) {
        if (this.treeModels.has(treeViewId)) {
            return;
        }
        const disposable = new ide_core_browser_1.DisposableStore();
        const dataProvider = new TreeViewDataProvider(treeViewId, this.proxy, this.iconService, this.themeService, this.labelService, this.contextKeyService, this.menuService, this.userhome);
        const dndController = new TreeViewDragAndDropController(treeViewId, options.dropMimeTypes, options.dragMimeTypes, options.hasHandleDrag, this.proxy);
        const model = this.createTreeModel(treeViewId, dataProvider, dndController, options);
        this.treeModels.set(treeViewId, model);
        this.dndControllers.set(treeViewId, dndController);
        disposable.add((0, ide_core_browser_1.toDisposable)(() => this.treeModels.delete(treeViewId)));
        this.mainLayoutService.replaceViewComponent({
            id: treeViewId,
            component: components_1.ExtensionTabBarTreeView,
        }, {
            model,
            dataProvider,
            treeViewId,
        });
        // const treeViewCollapseAllCommand = getTreeViewCollapseAllCommand(treeViewId);
        if (options === null || options === void 0 ? void 0 : options.showCollapseAll) {
            disposable.add(this.menuRegistry.registerMenuItem(next_1.MenuId.ViewTitle, {
                command: {
                    id: this.extensionHostType === 'worker'
                        ? MainThreadTreeView_1.TREE_VIEW_COLLAPSE_ALL_COMMAND_ID_WORKER
                        : MainThreadTreeView_1.TREE_VIEW_COLLAPSE_ALL_COMMAND_ID,
                    label: (0, ide_core_browser_1.localize)('treeview.command.action.collapse'),
                },
                extraTailArgs: [treeViewId],
                iconClass: (0, ide_core_browser_1.getIcon)('collapse-all'),
                when: `view == ${treeViewId}`,
                group: 'navigation',
                order: 10000, // keep the last position
            }));
        }
        disposable.add(model.onDidSelectedNodeChange((treeItemIds) => {
            dataProvider.setSelection(treeViewId, treeItemIds);
        }));
        disposable.add(model.onDidChangeExpansionState((state) => {
            const { treeItemId, expanded } = state;
            dataProvider.setExpanded(treeViewId, treeItemId, expanded);
        }));
        const handler = this.mainLayoutService.getTabbarHandler(treeViewId);
        if (handler) {
            disposable.add(handler.onActivate(() => {
                dataProvider.setVisible(treeViewId, true);
            }));
            disposable.add(handler.onInActivate(() => {
                dataProvider.setVisible(treeViewId, false);
            }));
            disposable.add(disposable.add((0, ide_core_browser_1.toDisposable)(() => handler.disposeView(treeViewId))));
        }
        this.disposableCollection.set(treeViewId, disposable);
    }
    async $unregisterTreeDataProvider(treeViewId) {
        const disposable = this.disposableCollection.get(treeViewId);
        if (disposable) {
            disposable.dispose();
        }
    }
    async $resolveDropFileData(treeViewId, requestId, dataItemId) {
        const controller = this.dndControllers.get(treeViewId);
        if (!controller) {
            throw new Error('Unknown tree');
        }
        return controller.resolveDropFileData(requestId, dataItemId);
    }
    async $refresh(treeViewId, itemsToRefresh) {
        const treeModel = this.treeModels.get(treeViewId);
        if (treeModel) {
            await treeModel.refresh(itemsToRefresh);
        }
    }
    async $setTitle(treeViewId, title) {
        const handler = this.mainLayoutService.getTabbarHandler(treeViewId);
        if (handler) {
            handler.updateViewTitle(treeViewId, title);
        }
    }
    async $setDescription(treeViewId, description) {
        const handler = this.mainLayoutService.getTabbarHandler(treeViewId);
        if (handler) {
            handler.updateViewDescription(treeViewId, description);
        }
    }
    async $setBadge(treeViewId, badge) {
        const handler = this.mainLayoutService.getTabbarHandler(treeViewId);
        if (handler) {
            handler.setBadge(badge ? badge : '');
            handler.accordionService.updateViewBadge(treeViewId, badge ? badge : '');
        }
    }
    async $setMessage(treeViewId, message) {
        const handler = this.mainLayoutService.getTabbarHandler(treeViewId);
        if (handler) {
            handler.updateViewMessage(treeViewId, message);
        }
    }
    async $reveal(treeViewId, treeItemId, options) {
        this.mainLayoutService.revealView(treeViewId);
        const treeModel = this.treeModels.get(treeViewId);
        if (treeModel && treeItemId) {
            treeModel.reveal(treeItemId, options);
        }
    }
    _registerInternalCommands() {
        this.disposable.add(this.commandRegistry.registerCommand({
            id: this.extensionHostType === 'worker'
                ? MainThreadTreeView_1.TREE_VIEW_COLLAPSE_ALL_COMMAND_ID_WORKER
                : MainThreadTreeView_1.TREE_VIEW_COLLAPSE_ALL_COMMAND_ID,
        }, {
            execute: (treeViewId) => {
                const treeModel = this.treeModels.get(treeViewId);
                if (treeModel) {
                    treeModel.collapseAll();
                }
            },
        }));
    }
};
exports.MainThreadTreeView = MainThreadTreeView;
MainThreadTreeView.TREE_VIEW_COLLAPSE_ALL_COMMAND_ID = 'TREE_VIEW_COLLAPSE_ALL';
MainThreadTreeView.TREE_VIEW_COLLAPSE_ALL_COMMAND_ID_WORKER = 'TREE_VIEW_COLLAPSE_ALL_WORKER';
tslib_1.__decorate([
    (0, di_1.Autowired)(ide_main_layout_1.IMainLayoutService),
    tslib_1.__metadata("design:type", Object)
], MainThreadTreeView.prototype, "mainLayoutService", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(ide_theme_1.IIconService),
    tslib_1.__metadata("design:type", Object)
], MainThreadTreeView.prototype, "iconService", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(next_1.IMenuRegistry),
    tslib_1.__metadata("design:type", next_1.IMenuRegistry)
], MainThreadTreeView.prototype, "menuRegistry", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(ide_core_browser_1.CommandRegistry),
    tslib_1.__metadata("design:type", Object)
], MainThreadTreeView.prototype, "commandRegistry", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(ide_theme_1.IThemeService),
    tslib_1.__metadata("design:type", Object)
], MainThreadTreeView.prototype, "themeService", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(progress_1.IProgressService),
    tslib_1.__metadata("design:type", Object)
], MainThreadTreeView.prototype, "progressService", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(ide_core_browser_1.LabelService),
    tslib_1.__metadata("design:type", ide_core_browser_1.LabelService)
], MainThreadTreeView.prototype, "labelService", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(ide_core_browser_1.IContextKeyService),
    tslib_1.__metadata("design:type", Object)
], MainThreadTreeView.prototype, "contextKeyService", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(next_1.AbstractMenuService),
    tslib_1.__metadata("design:type", next_1.AbstractMenuService)
], MainThreadTreeView.prototype, "menuService", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(ide_file_service_1.IFileServiceClient),
    tslib_1.__metadata("design:type", Object)
], MainThreadTreeView.prototype, "fileServiceClient", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(di_1.INJECTOR_TOKEN),
    tslib_1.__metadata("design:type", di_1.Injector)
], MainThreadTreeView.prototype, "injector", void 0);
exports.MainThreadTreeView = MainThreadTreeView = MainThreadTreeView_1 = tslib_1.__decorate([
    (0, di_1.Injectable)({ multiple: true }),
    tslib_1.__param(0, (0, di_1.Optional)(ide_connection_1.IRPCProtocol)),
    tslib_1.__metadata("design:paramtypes", [Object, String])
], MainThreadTreeView);
class TreeViewDataProvider extends ide_components_1.Tree {
    constructor(treeViewId, proxy, iconService, themeService, labelService, contextKeyService, menuService, userhome) {
        super();
        this.treeViewId = treeViewId;
        this.proxy = proxy;
        this.iconService = iconService;
        this.themeService = themeService;
        this.labelService = labelService;
        this.contextKeyService = contextKeyService;
        this.menuService = menuService;
        this.userhome = userhome;
        this.cachedMenu = new Map();
        this.onTreeDataChangedEmitter = new ide_core_browser_1.Emitter();
        this.onRevealChangedEmitter = new ide_core_browser_1.Emitter();
        this.isEmpty = true;
        this._onDidChangeEmpty = new ide_core_browser_1.Emitter();
        this.onDidChangeEmpty = this._onDidChangeEmpty.event;
        this.treeItemId2TreeNode = new Map();
    }
    get isTreeEmpty() {
        return this.isEmpty;
    }
    get onTreeDataChanged() {
        return this.onTreeDataChangedEmitter.event;
    }
    get onRevealChanged() {
        return this.onRevealChangedEmitter.event;
    }
    get root() {
        return this._root;
    }
    getTreeNodeIdByTreeItemId(treeItemId) {
        var _a;
        return (_a = this.treeItemId2TreeNode.get(treeItemId)) === null || _a === void 0 ? void 0 : _a.id;
    }
    getReadableDescription(item) {
        if (item.resourceUri) {
            if (this.userhome) {
                const path = ide_core_browser_1.URI.from(item.resourceUri);
                if (this.userhome.isEqualOrParent(path)) {
                    return decodeURIComponent(item.resourceUri.path.toString().replace(this.userhome.codeUri.fsPath, '~'));
                }
            }
            return item.resourceUri.path.toString();
        }
        return item.description;
    }
    getLabelAndDescription(item) {
        if (item.label) {
            return {
                label: item.label,
                description: item.description,
            };
        }
        else if (item.resourceUri) {
            let label = item.resourceUri.path.toString();
            label = decodeURIComponent(label);
            if (label.indexOf('/') >= 0) {
                label = label.substring(label.lastIndexOf('/') + 1);
            }
            const description = this.getReadableDescription(item);
            return {
                label,
                description,
            };
        }
        return {
            label: item.label,
            description: item.description,
        };
    }
    async createFoldNode(item, parent) {
        var _a;
        const expanded = ext_types_1.TreeItemCollapsibleState.Expanded === item.collapsibleState;
        const icon = await this.toIconClass(item);
        const actions = this.getInlineMenuNodes(item.contextValue || '');
        const { label, description } = this.getLabelAndDescription(item);
        const node = new tree_view_node_defined_1.ExtensionCompositeTreeNode(this, parent, label, description, icon, 
        // TODO: Support ThemeIcon on tooltip
        typeof item.tooltip === 'string' ? item.tooltip : (_a = item.tooltip) === null || _a === void 0 ? void 0 : _a.value, item.command, item.contextValue || '', item.id, actions, item.checkboxInfo, item.accessibilityInformation, expanded, item.resourceUri);
        return node;
    }
    async createNormalNode(item, parent) {
        var _a;
        const icon = await this.toIconClass(item);
        const actions = this.getInlineMenuNodes(item.contextValue || '');
        const { label, description } = this.getLabelAndDescription(item);
        const node = new tree_view_node_defined_1.ExtensionTreeNode(this, parent, label, description, icon, 
        // TODO: Support ThemeIcon on tooltip
        typeof item.tooltip === 'string' ? item.tooltip : (_a = item.tooltip) === null || _a === void 0 ? void 0 : _a.value, item.command, item.contextValue || '', item.id, actions, item.checkboxInfo, item.accessibilityInformation, item.resourceUri);
        return node;
    }
    isBase64Icon(iconPath) {
        const isBase64Regx = /^data:image\//;
        if (iconPath) {
            if ((0, ide_core_browser_1.isString)(iconPath)) {
                return isBase64Regx.test(iconPath);
            }
            else {
                return isBase64Regx.test(iconPath.dark);
            }
        }
        return false;
    }
    async toIconClass(item) {
        if (item.iconUrl || item.icon) {
            return this.iconService.fromIcon('', item.iconUrl || item.icon, this.isBase64Icon(item.iconUrl || item.icon) ? ide_theme_1.IconType.Base64 : ide_theme_1.IconType.Background, undefined, true);
        }
        else if (item.themeIcon) {
            let themeIconClass = (0, ide_core_browser_1.getExternalIcon)(item.themeIcon.id);
            if (item.resourceUri) {
                if (item.themeIcon.id === 'file') {
                    themeIconClass = this.labelService.getIcon(ide_core_browser_1.URI.from(item.resourceUri));
                }
                else if (item.themeIcon.id === 'folder') {
                    themeIconClass = this.labelService.getIcon(ide_core_browser_1.URI.from(item.resourceUri), {
                        isDirectory: true,
                    });
                }
            }
            const themeColorClass = this.themeService.getColorClassNameByColorToken(item.themeIcon.color);
            return `${themeIconClass} ${themeColorClass !== null && themeColorClass !== void 0 ? themeColorClass : ''}`;
        }
        else {
            return '';
        }
    }
    getInlineMenuNodes(viewItemValue) {
        if (this.cachedMenu.has(viewItemValue)) {
            return this.cachedMenu.get(viewItemValue);
        }
        const viewContextKey = this.contextKeyService.createScoped();
        viewContextKey.createKey('view', this.treeViewId);
        viewContextKey.createKey('viewItem', viewItemValue);
        // viewItem
        const menus = this.menuService.createMenu(next_1.MenuId.ViewItemContext, viewContextKey);
        const result = (0, next_1.generateCtxMenu)({ menus, separator: 'inline' });
        menus.dispose();
        viewContextKey.dispose();
        this.cachedMenu.set(viewItemValue, result[0]);
        return result[0];
    }
    /**
     * 创建节点
     *
     * @param item tree view item from the ext
     */
    async createTreeNode(item, parent) {
        if (!(0, ide_core_browser_1.isUndefined)(item.collapsibleState) && item.collapsibleState !== ext_types_1.TreeItemCollapsibleState.None) {
            return await this.createFoldNode(item, parent);
        }
        return await this.createNormalNode(item, parent);
    }
    async resolveChildren(parent) {
        let nodes = [];
        if (parent) {
            let children;
            if (tree_view_node_defined_1.ExtensionTreeRoot.is(parent)) {
                children = await this.proxy.$getChildren(this.treeViewId);
            }
            else {
                children = await this.proxy.$getChildren(this.treeViewId, parent.treeItemId);
            }
            if (children && Array.isArray(children)) {
                for (const child of children) {
                    const node = await this.createTreeNode(child, parent);
                    this.treeItemId2TreeNode.set(child.id, node);
                    nodes.push(node);
                }
            }
            if (tree_view_node_defined_1.ExtensionTreeRoot.is(parent)) {
                const oldEmpty = this.isEmpty;
                this.isEmpty = !children || children.length === 0;
                if (oldEmpty !== this.isEmpty) {
                    this._onDidChangeEmpty.fire();
                }
            }
        }
        else {
            nodes = [new tree_view_node_defined_1.ExtensionTreeRoot(this, this.treeViewId)];
        }
        return nodes;
    }
    async resolveTreeItem(treeViewId, treeItemId) {
        const token = new ide_core_browser_1.CancellationTokenSource().token;
        return this.proxy.$resolveTreeItem(treeViewId, treeItemId, token);
    }
    // 按照默认次序排序
    sortComparator(a, b) {
        if (!a) {
            return 1;
        }
        if (!b) {
            return -1;
        }
        return 0;
    }
    async refresh(itemsToRefresh) {
        await this.onTreeDataChangedEmitter.fire(itemsToRefresh);
    }
    async reveal(viewItemId) {
        await this.onRevealChangedEmitter.fire(viewItemId);
    }
    async setSelection(treeViewId, id) {
        this.proxy.$setSelection(treeViewId, id);
    }
    async setExpanded(treeViewId, id, expanded) {
        this.proxy.$setExpanded(treeViewId, id, expanded);
    }
    async setVisible(treeViewId, visible) {
        this.proxy.$setVisible(treeViewId, visible);
    }
    dispose() {
        super.dispose();
        this.treeItemId2TreeNode.clear();
    }
    markAsChecked(node, checked, manageCheckboxStateManually) {
        var _a;
        function findParentsToChange(child, nodes) {
            var _a, _b;
            if (((_a = child.parent) === null || _a === void 0 ? void 0 : _a.checkboxInfo) !== undefined &&
                child.parent.checkboxInfo.checked !== checked &&
                (!checked ||
                    !((_b = child.parent.children) === null || _b === void 0 ? void 0 : _b.some((candidate) => { var _a; return candidate !== child && ((_a = candidate.checkboxInfo) === null || _a === void 0 ? void 0 : _a.checked) === false; })))) {
                nodes.push(child.parent);
                findParentsToChange(child.parent, nodes);
            }
        }
        function findChildrenToChange(parent, nodes) {
            var _a;
            if (ide_components_1.CompositeTreeNode.is(parent)) {
                (_a = parent.children) === null || _a === void 0 ? void 0 : _a.forEach((child) => {
                    if (child.checkboxInfo !== undefined && child.checkboxInfo.checked !== checked) {
                        nodes.push(child);
                    }
                    findChildrenToChange(child, nodes);
                });
            }
        }
        const nodesToChange = [node];
        if (!manageCheckboxStateManually) {
            findParentsToChange(node, nodesToChange);
            findChildrenToChange(node, nodesToChange);
        }
        nodesToChange.forEach((n) => (n.checkboxInfo.checked = checked));
        (_a = this.proxy) === null || _a === void 0 ? void 0 : _a.$checkStateChanged(this.treeViewId, [{ treeItemId: node.treeItemId, checked }]);
    }
}
exports.TreeViewDataProvider = TreeViewDataProvider;
class TreeViewDragAndDropController extends ide_core_browser_1.Disposable {
    constructor(treeViewId, dropMimeTypes = [], dragMimeTypes = [], hasHandleDrag, _proxy) {
        super();
        this.treeViewId = treeViewId;
        this.dropMimeTypes = dropMimeTypes;
        this.dragMimeTypes = dragMimeTypes;
        this.hasHandleDrag = hasHandleDrag;
        this._proxy = _proxy;
        this.dataTransfersCache = new data_transfer_cache_1.DataTransferCache();
    }
    async handleDrop(dataTransfer, targetTreeItem, token, operationUuid, sourceTreeId, sourceTreeItemHandles) {
        const request = this.dataTransfersCache.add(dataTransfer);
        try {
            return await this._proxy.$handleDrop(this.treeViewId, request.id, await converter_1.DataTransfer.toDataTransferDTO(dataTransfer), targetTreeItem === null || targetTreeItem === void 0 ? void 0 : targetTreeItem.treeItemId, token, operationUuid, sourceTreeId, sourceTreeItemHandles);
        }
        finally {
            request.dispose();
        }
    }
    async handleDrag(sourceTreeItemHandles, operationUuid, token) {
        if (!this.hasHandleDrag) {
            return;
        }
        const additionalDataTransferDTO = await this._proxy.$handleDrag(this.treeViewId, sourceTreeItemHandles, operationUuid, token);
        if (!additionalDataTransferDTO) {
            return;
        }
        const additionalDataTransfer = new data_transfer_1.VSDataTransfer();
        additionalDataTransferDTO.items.forEach(([type, item]) => {
            additionalDataTransfer.replace(type, (0, data_transfer_1.createStringDataTransferItem)(item.asString));
        });
        return additionalDataTransfer;
    }
    resolveDropFileData(requestId, dataItemId) {
        return this.dataTransfersCache.resolveDropFileData(requestId, dataItemId);
    }
}
exports.TreeViewDragAndDropController = TreeViewDragAndDropController;
//# sourceMappingURL=main.thread.treeview.js.map