/* tslint:disable */
import { onUnexpectedError } from '@vscode/monaco-editor/esm/vs/base/common/errors';
import { URI, UriComponents } from '@vscode/monaco-editor/esm/vs/base/common/uri';
import { IDisposable } from '@vscode/monaco-editor/esm/vs/base/common/lifecycle';
import { Disposable } from '@workbench-stack/extension-api/lib/core/types/ext-host-types';
import * as cdecode from 'cdecode/server';
import { MainContext, MainThreadDocumentContentProvidersShape, IMainContext } from '@workbench-stack/extension-api/lib/protocol/server.protocol';
import { ExtHostDocumentContentProvidersShape } from '@workbench-stack/extension-api/lib/protocol/common.protocol';
import { ExtHostDocumentsAndEditors } from './ext-host-documents-and-editors';
import { Schemas } from '@vscode/monaco-editor/esm/vs/base/common/network';
import { ILogService } from '@workbench-stack/server-core';
import { CancellationToken } from '@vscode/monaco-editor/esm/vs/base/common/cancellation';

export class ExtHostDocumentContentProvider implements ExtHostDocumentContentProvidersShape {

    private static _handlePool = 0;

    private readonly _documentContentProviders = new Map<number, cdecode.TextDocumentContentProvider>();
    private readonly _proxy: MainThreadDocumentContentProvidersShape;

    constructor(
        mainContext: IMainContext,
        private readonly _documentsAndEditors: ExtHostDocumentsAndEditors,
        private readonly _logService: ILogService,
    ) {
        this._proxy = mainContext.getProxy(MainContext.MainThreadDocumentContentProviders);
    }

    dispose(): void {
        // todo@joh
    }

    registerTextDocumentContentProvider(scheme: string, provider: cdecode.TextDocumentContentProvider): cdecode.Disposable {
        // todo@remote
        // check with scheme from fs-providers!
        if (scheme === Schemas.file || scheme === Schemas.untitled) {
            throw new Error(`scheme '${scheme}' already registered`);
        }

        const handle = ExtHostDocumentContentProvider._handlePool++;

        this._documentContentProviders.set(handle, provider);
        this._proxy.$registerTextContentProvider(handle, scheme);

        let subscription: IDisposable;
        if (typeof provider.onDidChange === 'function') {
            subscription = provider.onDidChange(uri => {
                if (uri.scheme !== scheme) {
                    this._logService.warn(`Provider for scheme '${scheme}' is firing event for schema '${uri.scheme}' which will be IGNORED`);
                    return;
                }
                if (this._documentsAndEditors.getDocument(uri.toString())) {
                    this.$provideTextDocumentContent(handle, uri).then(value => {

                        const document = this._documentsAndEditors.getDocument(uri.toString());
                        if (!document) {
                            // disposed in the meantime
                            return;
                        }

                        // create lines and compare
                        const lines = value.split(/\r\n|\r|\n/);

                        // broadcast event when content changed
                        if (!document.equalLines(lines)) {
                            return this._proxy.$onVirtualDocumentChange(uri, value);
                        }

                    }, onUnexpectedError);
                }
            });
        }
        return new Disposable(() => {
            if (this._documentContentProviders.delete(handle)) {
                this._proxy.$unregisterTextContentProvider(handle);
            }
            if (subscription) {
                subscription.dispose();
                subscription = undefined;
            }
        });
    }

    $provideTextDocumentContent(handle: number, uri: UriComponents): Promise<string> {
        const provider = this._documentContentProviders.get(handle);
        if (!provider) {
            return Promise.reject(new Error(`unsupported uri-scheme: ${uri.scheme}`));
        }
        return Promise.resolve(provider.provideTextDocumentContent(URI.revive(uri), CancellationToken.None));
    }
}
