
/* tslint:disable */
import * as assert from 'assert';
import { Subject, Observable } from 'rxjs';
import { filter, map } from 'rxjs/operators';
import { Emitter, Event } from '@vscode/monaco-editor/esm/vs/base/common/event';
import { dispose } from '@vscode/monaco-editor/esm/vs/base/common/lifecycle';
import { URI } from '@vscode/monaco-editor/esm/vs/base/common/uri';
import { IMainContext, MainContext } from '@workbench-stack/extension-api/lib/protocol/server.protocol';
import { ExtHostDocumentsAndEditorsShape, IDocumentsAndEditorsDelta  } from '@workbench-stack/extension-api/lib/protocol/common.protocol';
import { ExtHostDocumentData } from './ext-host-document-data';
import { ExtHostTextEditor } from './ext-host-text-editor';
import * as typeConverters from '@workbench-stack/extension-api/lib/core/converters/ext-host-type-converters';
import { Disposable } from '@workbench-stack/extension-api/lib/core/types/ext-host-types';
import { getEvents, getEventPayload } from '../../util/event-pipes';

import { createProxy, handleRequests, handleServerProxyRequests } from '@workbench-stack/extension-api';
import { Connection, createConnection, Logger, MessageTransports } from '@workbench-stack/extension-api';

export enum DocumentsAndEditorsUpdates {
    AddDocuments = 'AddDocuments',
    RemoveDocuments = 'RemoveDocuments',
    ChangeActiveTextEditor = 'ChangeActiveTextEditor',
    ChangeVisibleTextEditors = 'ChangeVisibleTextEditors',
}

export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsShape {
    private updates$ = new Subject();
    private _disposables: Disposable[] = [];

    private _activeEditorId: string;

    private readonly _editors = new Map<string, ExtHostTextEditor>();
    private readonly _documents = new Map<string, ExtHostDocumentData>();

    readonly onDidAddDocuments: Observable<ExtHostDocumentData[]> = this.updates$.pipe(filter(getEvents(DocumentsAndEditorsUpdates.AddDocuments)), map(getEventPayload));
    readonly onDidRemoveDocuments: Observable<ExtHostDocumentData[]> = this.updates$.pipe(filter(getEvents(DocumentsAndEditorsUpdates.RemoveDocuments)), map(getEventPayload));
    readonly onDidChangeActiveTextEditor: Observable<ExtHostTextEditor> = this.updates$.pipe(filter(getEvents(DocumentsAndEditorsUpdates.ChangeActiveTextEditor)), map(getEventPayload));
    readonly onDidChangeVisibleTextEditors: Observable<ExtHostTextEditor[]> = this.updates$.pipe(filter(getEvents(DocumentsAndEditorsUpdates.ChangeVisibleTextEditors)), map(getEventPayload));

    constructor(
        private readonly connection: Connection,
    ) {
        console.log('[check>>>]: ext-host.ExtHostDocumentsAndEditors started')
     }

    dispose() {
        this._disposables = dispose(this._disposables);
    }

    $acceptDocumentsAndEditorsDelta(delta: IDocumentsAndEditorsDelta): void {
        console.log('[check>>>]: ext-host.$acceptDocumentsAndEditorsDelta', delta);

        const removedDocuments: ExtHostDocumentData[] = [];
        const addedDocuments: ExtHostDocumentData[] = [];
        const removedEditors: ExtHostTextEditor[] = [];

        if (delta.removedDocuments) {
            for (const uriComponent of delta.removedDocuments) {
                const uri = URI.revive(uriComponent);
                const id = uri.toString();
                const data = this._documents.get(id);
                this._documents.delete(id);
                removedDocuments.push(data);
            }
        }

        if (delta.addedDocuments) {
            for (const data of delta.addedDocuments) {
                const resource = URI.revive(data.uri);
                assert.ok(!this._documents.has(resource.toString()), `document '${resource} already exists!'`);

                const documentData = new ExtHostDocumentData(
                    createProxy(this.connection, MainContext.MainThreadDocuments.sid),
                    resource,
                    data.lines,
                    data.EOL,
                    data.modeId,
                    data.versionId,
                    data.isDirty
                );
                console.log("[check>>>]: ext-host.documentData", documentData);
                this._documents.set(resource.toString(), documentData);
                addedDocuments.push(documentData);
            }
        }

        if (delta.removedEditors) {
            for (const id of delta.removedEditors) {
                const editor = this._editors.get(id);
                this._editors.delete(id);
                removedEditors.push(editor);
            }
        }

        if (delta.addedEditors) {
            for (const data of delta.addedEditors) {
                const resource = URI.revive(data.documentUri);
                assert.ok(this._documents.has(resource.toString()), `document '${resource}' does not exist`);
                assert.ok(!this._editors.has(data.id), `editor '${data.id}' already exists!`);

                const documentData = this._documents.get(resource.toString());
                const editor = new ExtHostTextEditor(
                    createProxy(this.connection, MainContext.MainThreadTextEditors.sid),
                    data.id,
                    documentData,
                    data.selections.map(typeConverters.Selection.to),
                    data.options,
                    data.visibleRanges.map(typeConverters.Range.to),
                    typeConverters.ViewColumn.to(data.editorPosition)
                );
                this._editors.set(data.id, editor);
            }
        }

        if (delta.newActiveEditor !== undefined) {
            assert.ok(delta.newActiveEditor === null || this._editors.has(delta.newActiveEditor), `active editor '${delta.newActiveEditor}' does not exist`);
            this._activeEditorId = delta.newActiveEditor;
        }

        dispose(removedDocuments);
        dispose(removedEditors);

        // now that the internal state is complete, fire events
        if (delta.removedDocuments) {
            this.updates$.next({ type: DocumentsAndEditorsUpdates.RemoveDocuments, payload: removedDocuments });
        }

        if (delta.addedDocuments) {
            this.updates$.next({ type: DocumentsAndEditorsUpdates.AddDocuments, payload: addedDocuments });
        }

        if (delta.removedEditors || delta.addedEditors) {
            this.updates$.next({ type: DocumentsAndEditorsUpdates.ChangeActiveTextEditor, payload: this.allEditors() });
        }

        if (delta.newActiveEditor !== undefined) {
            this.updates$.next({ type: DocumentsAndEditorsUpdates.ChangeActiveTextEditor, payload: this.activeEditor()});
        }
    }

    getDocument(strUrl: string): ExtHostDocumentData {
        return this._documents.get(strUrl);
    }

    allDocuments(): ExtHostDocumentData[] {
        const result: ExtHostDocumentData[] = [];
        this._documents.forEach(data => result.push(data));
        return result;
    }

    getEditor(id: string): ExtHostTextEditor {
        return this._editors.get(id);
    }

    activeEditor(): ExtHostTextEditor | undefined {
        if (!this._activeEditorId) {
            return undefined;
        } else {
            return this._editors.get(this._activeEditorId);
        }
    }

    allEditors(): ExtHostTextEditor[] {
        const result: ExtHostTextEditor[] = [];
        this._editors.forEach(data => result.push(data));
        return result;
    }
}

