// *****************************************************************************
// Copyright (C) 2020 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-only WITH Classpath-exception-2.0
// *****************************************************************************

import { injectable } from '@theia/core/shared/inversify';
import { CommentInfoMain } from './comments-service';
import { CommentingRanges, Range } from '../../../common/plugin-api-rpc-model';
import * as monaco from '@theia/monaco-editor-core';

@injectable()
export class CommentingRangeDecorator {

    private decorationOptions: monaco.editor.IModelDecorationOptions;
    private commentingRangeDecorations: CommentingRangeDecoration[] = [];

    constructor() {
        this.decorationOptions = {
            isWholeLine: true,
            linesDecorationsClassName: 'comment-range-glyph comment-diff-added'
        };
    }

    public update(editor: monaco.editor.ICodeEditor, commentInfos: CommentInfoMain[]): void {
        const model = editor.getModel();
        if (!model) {
            return;
        }

        const commentingRangeDecorations: CommentingRangeDecoration[] = [];
        for (const info of commentInfos) {
            info.commentingRanges.ranges.forEach(range => {
                commentingRangeDecorations.push(new CommentingRangeDecoration(editor, info.owner, info.extensionId, info.label,
                    range, this.decorationOptions, info.commentingRanges));
            });
        }

        const oldDecorations = this.commentingRangeDecorations.map(decoration => decoration.id);
        editor.deltaDecorations(oldDecorations, []);

        this.commentingRangeDecorations = commentingRangeDecorations;
    }

    public getMatchedCommentAction(line: number): { ownerId: string, extensionId: string | undefined, label: string | undefined, commentingRangesInfo: CommentingRanges }[] {
        const result = [];
        for (const decoration of this.commentingRangeDecorations) {
            const range = decoration.getActiveRange();
            if (range && range.startLineNumber <= line && line <= range.endLineNumber) {
                result.push(decoration.getCommentAction());
            }
        }

        return result;
    }
}

class CommentingRangeDecoration {
    private decorationId: string;

    public get id(): string {
        return this.decorationId;
    }

    constructor(private _editor: monaco.editor.ICodeEditor, private _ownerId: string, private _extensionId: string | undefined,
        private _label: string | undefined, private _range: Range, commentingOptions: monaco.editor.IModelDecorationOptions,
        private commentingRangesInfo: CommentingRanges) {
        const startLineNumber = _range.startLineNumber;
        const endLineNumber = _range.endLineNumber;
        const commentingRangeDecorations = [{
            range: {
                startLineNumber: startLineNumber, startColumn: 1,
                endLineNumber: endLineNumber, endColumn: 1
            },
            options: commentingOptions
        }];

        this.decorationId = this._editor.deltaDecorations([], commentingRangeDecorations)[0];
    }

    public getCommentAction(): { ownerId: string, extensionId: string | undefined, label: string | undefined, commentingRangesInfo: CommentingRanges } {
        return {
            extensionId: this._extensionId,
            label: this._label,
            ownerId: this._ownerId,
            commentingRangesInfo: this.commentingRangesInfo
        };
    }

    public getOriginalRange(): Range {
        return this._range;
    }

    public getActiveRange(): Range | undefined {
        const range = this._editor.getModel()!.getDecorationRange(this.decorationId);
        if (range) {
            return range;
        }
    }
}
