/* tslint:disable */
import { Observable, Subscription } from 'rxjs';
import { equals as objectEquals } from '@vscode/monaco-editor/esm/vs/base/common/objects';
import { URI, UriComponents } from '@vscode/monaco-editor/esm/vs/base/common/uri';
import { MainContext, ExtHostEditorsShape, IApplyEditsOptions, IExtHostContext, ITextDocumentShowOptions, ITextEditorConfigurationUpdate, 
    ITextEditorPositionData, IUndoStopOptions, MainThreadTextEditorsShape, WorkspaceEditDto, ExtHostContext, reviveWorkspaceEditDto,
 } from '@workbench-stack/extension-api';
import { TextEditorRevealType } from '@workbench-stack/extension-api/lib/core/types/ext-host-types';
import { ISelection, Selection } from '@vscode/monaco-editor/esm/vs/editor/common/core/selection';
import { ISingleEditOperation, EndOfLineSequence } from '@vscode/monaco-editor/esm/vs/editor/common/model';
import { IRange } from '@vscode/monaco-editor/esm/vs/editor/common/core/range';
import { ILineChange } from '@vscode/monaco-editor/esm/vs/editor/common/editorCommon';
import { MainThreadDocumentsAndEditors } from './client-document-and-editors';
import { MainThreadTextEditor } from './client-editor';
import { disposed } from '@vscode/monaco-editor/esm/vs/base/common/errors';
import { IDisposable, dispose } from '@vscode/monaco-editor/esm/vs/base/common/lifecycle';
import { IEditorOptions, ITextEditorOptions } from '@vscode/monaco-editor/esm/vs/platform/editor/common/editor';

export class MainThreadTextEditors implements MainThreadTextEditorsShape {

    private subscriptions = new Subscription();
    private static INSTANCE_COUNT: number = 0;

    private _instanceId: string;
    private _proxy: ExtHostEditorsShape;
    private readonly _documentsAndEditors: MainThreadDocumentsAndEditors;
    private _toDispose: IDisposable[];
    private _textEditorsListenersMap: { [editorId: string]: IDisposable[]; };
    private _editorPositionData: ITextEditorPositionData | null;
    private _registeredDecorationTypes: { [decorationType: string]: boolean; };

    constructor(
        documentsAndEditors: MainThreadDocumentsAndEditors,
        extHostContext: IExtHostContext,
    ) {
        this._instanceId = String(++MainThreadTextEditors.INSTANCE_COUNT);
        this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostEditors);
        this._documentsAndEditors = documentsAndEditors;
        this._toDispose = [];
        this._textEditorsListenersMap = Object.create(null);
        this._editorPositionData = null;
    }

    public dispose(): void {
    }

    public unsubscribe(): void {
        this.subscriptions.unsubscribe();
    }

    private _onTextEditorAdd(textEditor: MainThreadTextEditor): void {
        const id = textEditor.getId();
        const toDispose: IDisposable[] = [];
        toDispose.push(textEditor.onPropertiesChanged((data) => {
            this._proxy.$acceptEditorPropertiesChanged(id, data);
        }));

        this._textEditorsListenersMap[id] = toDispose;

    }

    private _onTextEditorRemove(id: string): void {
        dispose(this._textEditorsListenersMap[id]);
        delete this._textEditorsListenersMap[id];
    }

    private _updateActiveAndVisibleTextEditors(): void {

        // editor columns
        // const editorPositionData = this._getTextEditorPositionData();
        // if (!objectEquals(this._editorPositionData, editorPositionData)) {
        //     this._editorPositionData = editorPositionData;
        //     this._proxy.$acceptEditorPositionData(this._editorPositionData);
        // }
    }

    private _getTextEditorPositionData(): ITextEditorPositionData {
        // const result: ITextEditorPositionData = Object.create(null);
        // for (let workbenchEditor of this._editorService.visibleControls) {
        // 	const id = this._documentsAndEditors.findTextEditorIdFor(workbenchEditor);
        // 	if (id) {
        // 		result[id] = editorGroupToViewColumn(this._editorGroupService, workbenchEditor.group);
        // 	}
        // }
        // return result;
        return null;

    }
    // --- from extension host process

    $tryShowTextDocument(resource: UriComponents, options: ITextDocumentShowOptions): Promise<string | undefined> {
        const uri = URI.revive(resource);

        const editorOptions: ITextEditorOptions = {
            preserveFocus: options.preserveFocus,
            pinned: options.pinned,
            selection: options.selection,
        };

        const input = {
            resource: uri,
            options: editorOptions,
        }

        // return this._editorService.openEditor(input, viewColumnToEditorGroup(this._editorGroupService, options.position)).then(editor => {
        //     if (!editor) {
        //         return undefined;
        //     }
        //     return this._documentsAndEditors.findTextEditorIdFor(editor);
        // });
        return Promise.resolve('not implemented');
    }

    $tryShowEditor(id: string, position): Promise<void> {
        console.warn('not yet implemented');
        // const mainThreadEditor = this._documentsAndEditors.getEditor(id);
        // if (mainThreadEditor) {
        // 	const model = mainThreadEditor.getModel();
        // 	return this._editorService.openEditor({
        // 		resource: model.uri,
        // 		options: { preserveFocus: false }
        // 	}, viewColumnToEditorGroup(this._editorGroupService, position)).then(() => { return; });
        // }
        // return Promise.resolve();
        return undefined;
    }

    $tryHideEditor(id: string): Promise<void> {
        // const mainThreadEditor = this._documentsAndEditors.getEditor(id);
        // if (mainThreadEditor) {
        // 	const editors = this._editorService.visibleControls;
        // 	for (let editor of editors) {
        // 		if (mainThreadEditor.matches(editor)) {
        // 			return editor.group.closeEditor(editor.input).then(() => { return; });
        // 		}
        // 	}
        // }
        // return Promise.resolve();
        return undefined;
    }

    $trySetSelections(id: string, selections: ISelection[]): Promise<void> {
        if (!this._documentsAndEditors.getEditor(id)) {
            return Promise.reject(disposed(`TextEditor(${id})`));
        }
        this._documentsAndEditors.getEditor(id).setSelections(selections);
        return Promise.resolve(undefined);
    }

    $trySetDecorations(id: string, key: string, ranges): Promise<void> {
        key = `${this._instanceId}-${key}`;
        if (!this._documentsAndEditors.getEditor(id)) {
            return Promise.reject(disposed(`TextEditor(${id})`));
        }
        this._documentsAndEditors.getEditor(id).setDecorations(key, ranges);
        return Promise.resolve(undefined);
    }

    $trySetDecorationsFast(id: string, key: string, ranges: number[]): Promise<void> {
        key = `${this._instanceId}-${key}`;
        if (!this._documentsAndEditors.getEditor(id)) {
            return Promise.reject(disposed(`TextEditor(${id})`));
        }
        this._documentsAndEditors.getEditor(id).setDecorationsFast(key, ranges);
        return Promise.resolve(undefined);
    }

    $tryRevealRange(id: string, range: IRange, revealType: TextEditorRevealType): Promise<void> {
        if (!this._documentsAndEditors.getEditor(id)) {
            return Promise.reject(disposed(`TextEditor(${id})`));
        }
        this._documentsAndEditors.getEditor(id).revealRange(range, revealType);
        return Promise.resolve();
    }

    $trySetOptions(id: string, options: ITextEditorConfigurationUpdate): Promise<void> {
        if (!this._documentsAndEditors.getEditor(id)) {
            return Promise.reject(disposed(`TextEditor(${id})`));
        }
        this._documentsAndEditors.getEditor(id).setConfiguration(options);
        return Promise.resolve(undefined);
    }

    $tryApplyEdits(id: string, modelVersionId: number, edits: ISingleEditOperation[], opts: IApplyEditsOptions): Promise<boolean> {
        if (!this._documentsAndEditors.getEditor(id)) {
            return Promise.reject(disposed(`TextEditor(${id})`));
        }
        return Promise.resolve(this._documentsAndEditors.getEditor(id).applyEdits(modelVersionId, edits, opts));
    }

    $tryApplyWorkspaceEdit(dto: WorkspaceEditDto): Promise<boolean> {
        const { edits } = reviveWorkspaceEditDto(dto);
        // return this._bulkEditService.apply({ edits }, undefined).then(() => true, err => false);
        return Promise.resolve(false);
    }

    $tryInsertSnippet(id: string, template: string, ranges: IRange[], opts: IUndoStopOptions): Promise<boolean> {
        console.warn('not yet implemented');
        return Promise.resolve(true);
    }

    $registerTextEditorDecorationType(key: string, options): void {
        key = `${this._instanceId}-${key}`;
        this._registeredDecorationTypes[key] = true;
        // this._codeEditorService.registerDecorationType(key, options);
    }

    $removeTextEditorDecorationType(key: string): void {
        key = `${this._instanceId}-${key}`;
        delete this._registeredDecorationTypes[key];
        // this._codeEditorService.removeDecorationType(key);
        console.warn('not yet implemented');
    }

    $getDiffInformation(id: string): Promise<ILineChange[]> {
        const editor = this._documentsAndEditors.getEditor(id);

        if (!editor) {
            return Promise.reject(new Error('No such TextEditor'));
        }

        const codeEditor = editor.getCodeEditor();
        if (!codeEditor) {
            return Promise.reject(new Error('No such CodeEditor'));
        }

        const codeEditorId = codeEditor.getId();
        // const diffEditors = this._codeEditorService.listDiffEditors();
        // const [diffEditor] = diffEditors.filter(d => d.getOriginalEditor().getId() === codeEditorId || d.getModifiedEditor().getId() === codeEditorId);

        // if (diffEditor) {
        //     return Promise.resolve(diffEditor.getLineChnages() || []);
        // }

        const dirtyDiffContribution = codeEditor.getContribution('editor.contrib.dirtydiff');

        if (dirtyDiffContribution) {
            return Promise.resolve((dirtyDiffContribution as any).getChanges());
        }
        return Promise.resolve([]);
    }
}
