"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ParsedParam = exports.ParameterizedCompletion = exports.highlight_extension = void 0;
const state_1 = require("@codemirror/state");
const view_1 = require("@codemirror/view");
const Autocompletion_js_1 = require("../core/Autocompletion.js");
// code mirror effect that you will use to define the effect you want (the decoration)
const addHighlight = state_1.StateEffect.define();
const removeHighlight = state_1.StateEffect.define();
// define a new field that will be attached to your view state as an extension, update will be called at each editor's change
exports.highlight_extension = state_1.StateField.define({
    create() { return view_1.Decoration.none; },
    update(value, transaction) {
        value = value.map(transaction.changes);
        for (let effect of transaction.effects) {
            if (effect.is(addHighlight))
                value = value.update({ add: effect.value, sort: true });
            else if (effect.is(removeHighlight))
                value = value.update({ filter: effect.value });
        }
        return value;
    },
    provide: f => view_1.EditorView.decorations.from(f),
});
// this is your decoration where you can define the change you want : a css class or directly css attributes
const highlight_decoration = view_1.Decoration.mark({
    inclusive: true,
    attributes: { style: "/* background-color: #b7c1e8; */border: gray 1px solid;" },
    tagName: "span",
    // class: 'red_back'
});
class MyWidgetType extends view_1.WidgetType {
    constructor() {
        super();
    }
    toDOM(_view) {
        let span = document.createElement("span");
        span.setAttribute("style", "border-right: gray 1px solid; margin-left: 1px;");
        return span;
    }
}
const end_decoration = view_1.Decoration.widget({
    // attributes: {style: "border-right: gray 1px solid;"},
    // inclusive: false,
    // tagName: "span",
    widget: new MyWidgetType(),
    side: 1000,
});
// your editor's view
// let main_view = new EditorView({ 
//   extensions: [highlight_extension]
// });
function highlight(editor, from, to) {
    // this is where the change takes effect by the dispatch. The of method instanciate the effect. You need to put this code where you want the change to take place
    editor.dispatch({
        effects: addHighlight.of([highlight_decoration.range(from, to)])
    });
}
function highlightEnd(editor, at) {
    editor.dispatch({
        effects: addHighlight.of([end_decoration.range(at, at)])
    });
}
function removeHighlights(editor) {
    editor.dispatch({
        effects: removeHighlight.of(() => false)
    });
}
class ParameterizedCompletion {
    constructor(tc) {
        this.parameters = [];
        this.forAutocompletion = undefined;
        this.parameterChangeListener = undefined;
        this.tc = tc;
    }
    getForAutocompletion() {
        return this.forAutocompletion;
    }
    setParameterChangeListener(l) {
        this.parameterChangeListener = l;
    }
    replaceSelection(autocompletion) {
        this.forAutocompletion = autocompletion;
        this.parameters = [];
        const insertionString = ParameterizedCompletion.parseParameters(autocompletion, this.parameters);
        const offset = this.tc.state.selection.main.anchor;
        this.tc.dispatch(this.tc.state.replaceSelection(insertionString));
        removeHighlights(this.tc);
        for (let pp of this.parameters) {
            highlight(this.tc, offset + pp.i0, offset + pp.i1);
        }
        const atEnd = offset + insertionString.length;
        highlightEnd(this.tc, atEnd);
        this.cycle(0);
    }
    next() {
        const ds = this.tc.state.field(exports.highlight_extension);
        let it = ds.iter(0);
        let cursor = this.tc.state.selection.main.head;
        // iterate over all parameters (ranges)
        let i = 0;
        while (it.value !== null) {
            if (cursor < it.from || i === ds.size - 1) {
                this.cycle(i);
                return;
            }
            it.next();
            i++;
        }
    }
    getParamIndexForCursor() {
        const ds = this.tc.state.field(exports.highlight_extension);
        let it = ds.iter(0);
        let cursor = this.tc.state.selection.main.head;
        // iterate over all parameters (ranges)
        let i = 0;
        while (it.value !== null) {
            if (cursor >= it.from && cursor <= it.to) {
                return i;
            }
            it.next();
            i++;
        }
        return undefined;
    }
    getCurrentParameter() {
        const idx = this.getParamIndexForCursor();
        if (idx === undefined)
            return undefined;
        return this.parameters[idx];
    }
    previous() {
        const ds = this.tc.state.field(exports.highlight_extension);
        let it = ds.iter(0);
        let cursor = this.tc.state.selection.main.head;
        // iterate over all parameters (ranges)
        let i = 0;
        while (it.value !== null) {
            if (cursor <= it.to) {
                this.cycle(i - 1);
                return;
            }
            it.next();
            i++;
        }
        this.cycle(i - 1); // needed in case the cursor is behind the last mark
    }
    cycle(currentParameterIndex) {
        const ds = this.tc.state.field(exports.highlight_extension);
        const nParameters = ds.size;
        if (nParameters === 0)
            return;
        if (currentParameterIndex === -1)
            return;
        let it = ds.iter(0);
        for (let i = 0; i < nParameters && i < currentParameterIndex; i++) {
            it.next();
        }
        const last = currentParameterIndex === nParameters - 1;
        // const from: number = last ? it.from + 1 : it.from;
        const from = it.from;
        this.tc.dispatch({ selection: { anchor: from, head: it.to } });
        if (this.parameterChangeListener !== undefined) {
            this.parameterChangeListener(currentParameterIndex, last);
        }
        // if(last)
        //     this.cancel();
    }
    cancel() {
        removeHighlights(this.tc);
        this.parameterChangeListener = undefined;
    }
    handleKeyEvent(event) {
        if (this.tc.state.field(exports.highlight_extension).size == 0) {
            this.cancel();
            return;
        }
        if (event.key === "Tab" || event.key === "Enter") {
            event.preventDefault();
            if (event.shiftKey) {
                this.previous();
            }
            else {
                this.next();
            }
        }
    }
    // static parseParametersOld(paramString: string, ret: ParsedParam[]): string {
    //     let varName: string | undefined = undefined;
    //     let insertString = "";
    //     const l = paramString.length;
    //     let hlStart = -1;
    //     let i = 0;
    //     while(i < l) {
    //         let cha = paramString[i];
    //         if(cha === '$' && i < l - 1 && paramString[i + 1] === '{') {
    //             if(varName === undefined) {
    //                 varName = "";
    //                 hlStart = insertString.length;
    //                 i = i + 1;
    //             }
    //             else {
    //                 throw new Error("Expected '}' before next '${'");
    //             }
    //         }
    //         else if(varName !== undefined && cha === '}') {
    //             const hlEnd = insertString.length;
    //             ret.push(new ParsedParam(varName, hlStart, hlEnd)); // hlEnd is exclusive
    //             varName = undefined;
    //         }
    //         else if(varName !== undefined) {
    //             varName = varName + cha;
    //             insertString = insertString + cha;
    //         }
    //         else {
    //             insertString = insertString + cha;
    //         }
    //         i = i + 1;
    //     }
    //     return insertString;
    // }
    static parseParameters(autocompletion, ret, offset = 0) {
        if (autocompletion instanceof Autocompletion_js_1.Autocompletion.Literal)
            return autocompletion.getCompletion(Autocompletion_js_1.Autocompletion.Purpose.FOR_INSERTION);
        if (autocompletion instanceof Autocompletion_js_1.Autocompletion.Parameterized) {
            const s = autocompletion.getParamName();
            ret.push(new ParsedParam(s, 0, s.length, autocompletion));
            return s;
        }
        if (autocompletion instanceof Autocompletion_js_1.Autocompletion.EntireSequence) {
            const entireSequence = autocompletion;
            const sequenceOfCompletions = entireSequence.getSequenceOfCompletions();
            const sequence = autocompletion.getSequence();
            let insertionString = "";
            for (const [idx, autocompletions] of sequenceOfCompletions.entries()) {
                const n = autocompletions.length;
                if (n > 1) {
                    const name = sequence.getNameForChild(idx);
                    const p = new Autocompletion_js_1.Autocompletion.Parameterized(sequence.getChildren()[idx], name, name);
                    const i0 = offset + insertionString.length;
                    const i1 = i0 + name.length;
                    ret.push(new ParsedParam(name, i0, i1, p));
                    insertionString += name;
                }
                else if (n === 1) {
                    const single = autocompletions[0];
                    if (single instanceof Autocompletion_js_1.Autocompletion.Literal) {
                        insertionString += single.getCompletion(Autocompletion_js_1.Autocompletion.Purpose.FOR_INSERTION);
                    }
                    else if (single instanceof Autocompletion_js_1.Autocompletion.Parameterized) {
                        const parameterized = single;
                        const s = parameterized.getParamName();
                        const i0 = offset + insertionString.length;
                        const i1 = i0 + s.length;
                        ret.push(new ParsedParam(s, i0, i1, parameterized));
                        insertionString += s;
                    }
                    else if (single instanceof Autocompletion_js_1.Autocompletion.EntireSequence) {
                        const entire = single;
                        const offs = offset + insertionString.length;
                        const s = ParameterizedCompletion.parseParameters(entire, ret, offs);
                        insertionString += s;
                    }
                    else {
                        console.log("Unknown completion type: " + typeof (autocompletion));
                    }
                }
            }
            return insertionString;
        }
        throw new Error("Unexpected completion type: " + typeof (autocompletion));
    }
}
exports.ParameterizedCompletion = ParameterizedCompletion;
class ParsedParam {
    constructor(name, i0, i1, autocompletion) {
        this.name = name;
        this.i0 = i0;
        this.i1 = i1;
        this.autocompletion = autocompletion;
    }
    toString() {
        return this.name;
    }
}
exports.ParsedParam = ParsedParam;
//# sourceMappingURL=ParameterizedCompletion.js.map