
import { Position } from '../core/position.js';
import { Range } from '../core/range.js';
import { PositionAffinity } from '../model.js';
import { filterValidationDecorations, filterFontDecorations } from '../config/editorOptions.js';
import { ViewModelDecoration, isModelDecorationVisible } from './viewModelDecoration.js';
import { InlineDecoration, InlineDecorationType } from './inlineDecorations.js';

class ViewModelDecorations {
    constructor(editorId, model, configuration, linesCollection, coordinatesConverter) {
        this.editorId = editorId;
        this.model = model;
        this.configuration = configuration;
        this._linesCollection = linesCollection;
        this._coordinatesConverter = coordinatesConverter;
        this._decorationsCache = Object.create(null);
        this._cachedModelDecorationsResolver = null;
        this._cachedModelDecorationsResolverViewRange = null;
    }
    _clearCachedModelDecorationsResolver() {
        this._cachedModelDecorationsResolver = null;
        this._cachedModelDecorationsResolverViewRange = null;
    }
    dispose() {
        this._decorationsCache = Object.create(null);
        this._clearCachedModelDecorationsResolver();
    }
    reset() {
        this._decorationsCache = Object.create(null);
        this._clearCachedModelDecorationsResolver();
    }
    onModelDecorationsChanged() {
        this._decorationsCache = Object.create(null);
        this._clearCachedModelDecorationsResolver();
    }
    onLineMappingChanged() {
        this._decorationsCache = Object.create(null);
        this._clearCachedModelDecorationsResolver();
    }
    _getOrCreateViewModelDecoration(modelDecoration) {
        const id = modelDecoration.id;
        let r = this._decorationsCache[id];
        if (!r) {
            const modelRange = modelDecoration.range;
            const options = modelDecoration.options;
            let viewRange;
            if (options.isWholeLine) {
                const start = this._coordinatesConverter.convertModelPositionToViewPosition(( new Position(modelRange.startLineNumber, 1)), PositionAffinity.Left, false, true);
                const end = this._coordinatesConverter.convertModelPositionToViewPosition(( new Position(
                    modelRange.endLineNumber,
                    this.model.getLineMaxColumn(modelRange.endLineNumber)
                )), PositionAffinity.Right);
                viewRange = ( new Range(start.lineNumber, start.column, end.lineNumber, end.column));
            } else {
                viewRange = this._coordinatesConverter.convertModelRangeToViewRange(modelRange, PositionAffinity.Right);
            }
            r = ( new ViewModelDecoration(viewRange, options));
            this._decorationsCache[id] = r;
        }
        return r;
    }
    getMinimapDecorationsInRange(range) {
        return this._getDecorationsInRange(range, true, false).decorations;
    }
    getDecorationsViewportData(viewRange) {
        let cacheIsValid = (this._cachedModelDecorationsResolver !== null);
        cacheIsValid = cacheIsValid && (viewRange.equalsRange(this._cachedModelDecorationsResolverViewRange));
        if (!cacheIsValid) {
            this._cachedModelDecorationsResolver = this._getDecorationsInRange(viewRange, false, false);
            this._cachedModelDecorationsResolverViewRange = viewRange;
        }
        return this._cachedModelDecorationsResolver;
    }
    getDecorationsOnLine(lineNumber, onlyMinimapDecorations = false, onlyMarginDecorations = false) {
        const range = ( new Range(
            lineNumber,
            this._linesCollection.getViewLineMinColumn(lineNumber),
            lineNumber,
            this._linesCollection.getViewLineMaxColumn(lineNumber)
        ));
        return this._getDecorationsInRange(range, onlyMinimapDecorations, onlyMarginDecorations);
    }
    _getDecorationsInRange(viewRange, onlyMinimapDecorations, onlyMarginDecorations) {
        const modelDecorations = this._linesCollection.getDecorationsInRange(
            viewRange,
            this.editorId,
            filterValidationDecorations(this.configuration.options),
            filterFontDecorations(this.configuration.options),
            onlyMinimapDecorations,
            onlyMarginDecorations
        );
        const startLineNumber = viewRange.startLineNumber;
        const endLineNumber = viewRange.endLineNumber;
        const decorationsInViewport = [];
        let decorationsInViewportLen = 0;
        const inlineDecorations = [];
        const hasVariableFonts = [];
        for (let j = startLineNumber; j <= endLineNumber; j++) {
            inlineDecorations[j - startLineNumber] = [];
            hasVariableFonts[j - startLineNumber] = false;
        }
        for (let i = 0, len = modelDecorations.length; i < len; i++) {
            const modelDecoration = modelDecorations[i];
            const decorationOptions = modelDecoration.options;
            if (!isModelDecorationVisible(this.model, modelDecoration)) {
                continue;
            }
            const viewModelDecoration = this._getOrCreateViewModelDecoration(modelDecoration);
            const viewRange = viewModelDecoration.range;
            decorationsInViewport[decorationsInViewportLen++] = viewModelDecoration;
            if (decorationOptions.inlineClassName) {
                const inlineDecoration = ( new InlineDecoration(
                    viewRange,
                    decorationOptions.inlineClassName,
                    decorationOptions.inlineClassNameAffectsLetterSpacing ? InlineDecorationType.RegularAffectingLetterSpacing : InlineDecorationType.Regular
                ));
                const intersectedStartLineNumber = Math.max(startLineNumber, viewRange.startLineNumber);
                const intersectedEndLineNumber = Math.min(endLineNumber, viewRange.endLineNumber);
                for (let j = intersectedStartLineNumber; j <= intersectedEndLineNumber; j++) {
                    inlineDecorations[j - startLineNumber].push(inlineDecoration);
                    if (decorationOptions.affectsFont) {
                        hasVariableFonts[j - startLineNumber] = true;
                    }
                }
            }
            if (decorationOptions.beforeContentClassName) {
                if (startLineNumber <= viewRange.startLineNumber && viewRange.startLineNumber <= endLineNumber) {
                    const inlineDecoration = ( new InlineDecoration(( new Range(
                        viewRange.startLineNumber,
                        viewRange.startColumn,
                        viewRange.startLineNumber,
                        viewRange.startColumn
                    )), decorationOptions.beforeContentClassName, InlineDecorationType.Before));
                    inlineDecorations[viewRange.startLineNumber - startLineNumber].push(inlineDecoration);
                    if (decorationOptions.affectsFont) {
                        hasVariableFonts[viewRange.startLineNumber - startLineNumber] = true;
                    }
                }
            }
            if (decorationOptions.afterContentClassName) {
                if (startLineNumber <= viewRange.endLineNumber && viewRange.endLineNumber <= endLineNumber) {
                    const inlineDecoration = ( new InlineDecoration(( new Range(
                        viewRange.endLineNumber,
                        viewRange.endColumn,
                        viewRange.endLineNumber,
                        viewRange.endColumn
                    )), decorationOptions.afterContentClassName, InlineDecorationType.After));
                    inlineDecorations[viewRange.endLineNumber - startLineNumber].push(inlineDecoration);
                    if (decorationOptions.affectsFont) {
                        hasVariableFonts[viewRange.endLineNumber - startLineNumber] = true;
                    }
                }
            }
        }
        return {
            decorations: decorationsInViewport,
            inlineDecorations: inlineDecorations,
            hasVariableFonts
        };
    }
}

export { ViewModelDecorations };
