"use strict";
/** ******************************************************************************
 * Copyright (C) 2018 Red Hat, Inc. and others.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the Eclipse
 * Public License v. 2.0 are satisfied: GNU General Public License, version 2
 * with the GNU Classpath Exception which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 ********************************************************************************/
// Some code copied and modified from https://github.com/eclipse-theia/theia/tree/v1.14.0/packages/plugin-ext/src/plugin/file-system-ext-impl.ts
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExtHostFileSystem = void 0;
exports.convertToVSCFileStat = convertToVSCFileStat;
const tslib_1 = require("tslib");
const ide_core_common_1 = require("@opensumi/ide-core-common");
const ide_file_service_1 = require("@opensumi/ide-file-service");
const vscode_1 = require("../../../common/vscode");
const files = tslib_1.__importStar(require("../../../common/vscode/file-system"));
function convertToVSCFileStat(stat) {
    return {
        type: (stat === null || stat === void 0 ? void 0 : stat.type) || 0,
        ctime: (stat === null || stat === void 0 ? void 0 : stat.createTime) || -1,
        mtime: stat === null || stat === void 0 ? void 0 : stat.lastModification,
        size: (stat === null || stat === void 0 ? void 0 : stat.size) || 0,
    };
}
class ConsumerFileSystem {
    constructor(_proxy, _fileSystemInfo) {
        this._proxy = _proxy;
        this._fileSystemInfo = _fileSystemInfo;
    }
    stat(uri) {
        return this._proxy.$stat(uri).catch(ConsumerFileSystem._handleError);
    }
    readDirectory(uri) {
        return this._proxy.$readdir(uri).catch(ConsumerFileSystem._handleError);
    }
    createDirectory(uri) {
        return this._proxy.$mkdir(uri).catch(ConsumerFileSystem._handleError);
    }
    async readFile(uri) {
        return this._proxy
            .$readFile(uri)
            .then((content) => Buffer.from(content))
            .catch(ConsumerFileSystem._handleError);
    }
    writeFile(uri, content) {
        return this._proxy.$writeFile(uri, content).catch(ConsumerFileSystem._handleError);
    }
    delete(uri, options) {
        return this._proxy
            .$delete(uri, { ...{ recursive: false, useTrash: false }, ...options })
            .catch(ConsumerFileSystem._handleError);
    }
    rename(oldUri, newUri, options) {
        return this._proxy
            .$rename(oldUri, newUri, { ...{ overwrite: false }, ...options })
            .catch(ConsumerFileSystem._handleError);
    }
    copy(source, destination, options) {
        return this._proxy
            .$copy(source, destination, { ...{ overwrite: false }, ...options })
            .catch(ConsumerFileSystem._handleError);
    }
    isWritableFileSystem(scheme) {
        const capabilities = this._fileSystemInfo.getCapabilities(scheme);
        if (typeof capabilities === 'number') {
            return !(capabilities & 2048 /* FileSystemProviderCapabilities.Readonly */);
        }
        return undefined;
    }
    static _handleError(err) {
        // generic error
        if (!(err instanceof Error)) {
            throw new files.FileSystemError(String(err));
        }
        // no provider (unknown scheme) error
        if (err.name === 'ENOPRO') {
            throw files.FileSystemError.Unavailable(err.message);
        }
        // file system error
        switch (err.name) {
            case files.FileSystemProviderErrorCode.FileExists:
                throw files.FileSystemError.FileExists(err.message);
            case files.FileSystemProviderErrorCode.FileNotFound:
                throw files.FileSystemError.FileNotFound(err.message);
            case files.FileSystemProviderErrorCode.FileNotADirectory:
                throw files.FileSystemError.FileNotADirectory(err.message);
            case files.FileSystemProviderErrorCode.FileIsADirectory:
                throw files.FileSystemError.FileIsADirectory(err.message);
            case files.FileSystemProviderErrorCode.NoPermissions:
                throw files.FileSystemError.NoPermissions(err.message);
            case files.FileSystemProviderErrorCode.Unavailable:
                throw files.FileSystemError.Unavailable(err.message);
            default:
                throw new files.FileSystemError(err.message, err.name);
        }
    }
}
class ExtHostFileSystem {
    constructor(rpcProtocol, _fileSystemInfo) {
        this.rpcProtocol = rpcProtocol;
        this._fileSystemInfo = _fileSystemInfo;
        this._fsProvider = new Map();
        this._usedSchemes = new Set();
        this._watches = new Map();
        this._handlePool = 0;
        this._proxy = this.rpcProtocol.getProxy(vscode_1.MainThreadAPIIdentifier.MainThreadFileSystem);
        this.fileSystem = new ConsumerFileSystem(this._proxy, this._fileSystemInfo);
        // register used schemes
        Object.keys(ide_core_common_1.Schemes).forEach((scheme) => this._usedSchemes.add(scheme));
    }
    registerFileSystemProvider(scheme, provider, options = {}) {
        if (this._usedSchemes.has(scheme)) {
            throw new Error(`a provider for the scheme '${scheme}' is already registered`);
        }
        const handle = this._handlePool++;
        this._usedSchemes.add(scheme);
        this._fsProvider.set(handle, provider);
        let capabilities = 2 /* FileSystemProviderCapabilities.FileReadWrite */;
        if (options.isCaseSensitive) {
            capabilities += 1024 /* FileSystemProviderCapabilities.PathCaseSensitive */;
        }
        if (options.isReadonly) {
            capabilities += 2048 /* FileSystemProviderCapabilities.Readonly */;
        }
        if (typeof provider.copy === 'function') {
            capabilities += 8 /* FileSystemProviderCapabilities.FileFolderCopy */;
        }
        this._proxy.$registerFileSystemProvider(handle, scheme, capabilities);
        const subscription = provider.onDidChangeFile((event) => {
            const mapped = [];
            for (const e of event) {
                const { uri, type } = e;
                if (uri.scheme !== scheme) {
                    // dropping events for wrong scheme
                    continue;
                }
                let newType;
                switch (type) {
                    case files.FileChangeType.Changed:
                        newType = ide_file_service_1.FileChangeType.UPDATED;
                        break;
                    case files.FileChangeType.Created:
                        newType = ide_file_service_1.FileChangeType.ADDED;
                        break;
                    case files.FileChangeType.Deleted:
                        newType = ide_file_service_1.FileChangeType.DELETED;
                        break;
                    default:
                        throw new Error('Unknown FileChangeType');
                }
                mapped.push({ uri: uri.toString(), type: newType });
            }
            this._proxy.$onFileSystemChange(handle, mapped);
        });
        return (0, ide_core_common_1.toDisposable)(() => {
            subscription.dispose();
            this._usedSchemes.delete(scheme);
            this._fsProvider.delete(handle);
            this._proxy.$unregisterProvider(handle);
        });
    }
    static _asIStat(stat) {
        // permissions is proposed api
        const { type, ctime, mtime, size, permissions } = stat;
        return { type, ctime, mtime, size, permissions };
    }
    $stat(handle, resource) {
        return Promise.resolve(this._getFsProvider(handle).stat(ide_core_common_1.URI.revive(resource))).then(ExtHostFileSystem._asIStat);
    }
    $readdir(handle, resource) {
        return Promise.resolve(this._getFsProvider(handle).readDirectory(ide_core_common_1.URI.revive(resource)));
    }
    $readFile(handle, resource) {
        return Promise.resolve(this._getFsProvider(handle).readFile(ide_core_common_1.URI.revive(resource))).then((data) => data);
    }
    $writeFile(handle, resource, content, opts) {
        return Promise.resolve(this._getFsProvider(handle).writeFile(ide_core_common_1.URI.revive(resource), content, opts));
    }
    $delete(handle, resource, opts) {
        return Promise.resolve(this._getFsProvider(handle).delete(ide_core_common_1.URI.revive(resource), opts));
    }
    $rename(handle, oldUri, newUri, opts) {
        return Promise.resolve(this._getFsProvider(handle).rename(ide_core_common_1.URI.revive(oldUri), ide_core_common_1.URI.revive(newUri), opts));
    }
    $copy(handle, oldUri, newUri, opts) {
        const provider = this._getFsProvider(handle);
        if (!provider.copy) {
            throw new Error('FileSystemProvider does not implement "copy"');
        }
        return Promise.resolve(provider.copy(ide_core_common_1.URI.revive(oldUri), ide_core_common_1.URI.revive(newUri), opts));
    }
    $mkdir(handle, resource) {
        return Promise.resolve(this._getFsProvider(handle).createDirectory(ide_core_common_1.URI.revive(resource)));
    }
    $watch(handle, session, resource, opts) {
        const subscription = this._getFsProvider(handle).watch(ide_core_common_1.URI.revive(resource), opts);
        this._watches.set(session, subscription);
    }
    $unwatch(_handle, session) {
        const subscription = this._watches.get(session);
        if (subscription) {
            subscription.dispose();
            this._watches.delete(session);
        }
    }
    _getFsProvider(handle) {
        const provider = this._fsProvider.get(handle);
        if (!provider) {
            const err = new Error();
            err.name = 'ENOPRO';
            err.message = 'no provider';
            throw err;
        }
        return provider;
    }
}
exports.ExtHostFileSystem = ExtHostFileSystem;
//# sourceMappingURL=ext.host.file-system.js.map