import { indentRange, syntaxTree } from '@codemirror/language';
import { BreedType } from '../classes/structures';
import { GetCursorInsideMode, GetCursorUntilMode } from '../utils/cursors';
import { preprocessStateExtension } from '../../codemirror/extension-state-preprocess';
import { reserved } from '../keywords';
import { getSingularName } from '../parsers/breed';
import { getCodeName } from '../utils/code';
/** CodeEditing: Functions for editing code. */
export class CodeEditing {
    /** Constructor: Create a new code editing service. */
    constructor(View) {
        this.View = View;
        this.Galapagos = View.state.field(preprocessStateExtension).Editor;
    }
    /** ChangeCode: Send a changeset into the editor. */
    ChangeCode(Changes) {
        this.View.dispatch({ changes: Changes });
    }
    /** GetSlice: Get a slice of the code. */
    GetSlice(From, To) {
        return this.View.state.sliceDoc(From, To);
    }
    /** FindFirstChild: Find the first child that matches a condition. */
    FindFirstChild(Parent, Field, Condition) {
        var _a;
        for (let Child of Parent.getChildren(Field)) {
            if ((_a = Condition === null || Condition === void 0 ? void 0 : Condition(Child)) !== null && _a !== void 0 ? _a : true)
                return Child;
        }
        return null;
    }
    /** AddTermToBracket: Add a term to a bracket. */
    AddTermToBracket(Contents, Node) {
        // De-duplicate the contents
        var Identifiers = Node.node.getChildren('Identifier').map((Child) => this.GetSlice(Child.from, Child.to).trim());
        Contents = Contents.filter((Content) => Identifiers.indexOf(Content) == -1);
        if (Contents.length == 0)
            return false;
        // Find the spaces between the brackets
        let Seperator = this.View.state.sliceDoc(Node.from, Node.to).includes('\n') ? '\n' : ' ';
        var From = this.FindFirstChild(Node, 'CloseBracket').from;
        // Insert the contents
        for (let Content of Contents.reverse()) {
            this.ChangeCode({
                from: From,
                to: From,
                insert: Content + Seperator,
            });
            this.ChangeCode(indentRange(this.View.state, From, From + 1 + Content.length));
        }
        return true;
    }
    // #endregion
    // #region "Global Statements"
    /** AppendGlobals: Append items of a global statement to the editor. */
    AppendGlobals(Field, Items) {
        Items = [...new Set(Items.filter((Item) => reserved.indexOf(Item) == -1))];
        if (Items.length == 0)
            return false;
        // Find the cursor
        let Cursor = GetCursorUntilMode(this.View.state);
        // Find the first global statement
        if (Cursor) {
            var Statement = this.FindFirstChild(Cursor.node, Field);
            if (Statement)
                return this.AddTermToBracket(Items, Statement);
        }
        // If not found, append a new global statement
        this.ChangeCode({
            from: 0,
            to: 0,
            insert: `${Field.toLowerCase()} [ ${Items.join(' ')} ]\n`,
        });
        return true;
    }
    /** AppendBreed: Append a breed to the editor. */
    AppendBreed(Type, Plural, Singular) {
        // Check if the breed already exists
        if (Plural == 'patches' || Singular == 'patch')
            return false;
        if (Plural == Singular)
            Singular = getSingularName(Plural);
        for (let [Sin, Breed] of this.Galapagos.LintContext.Breeds) {
            if (Breed.Plural == Plural || Breed.Singular == Singular || Breed.Singular == Plural || Breed.Plural == Singular)
                return false;
        }
        // TODO: Find the most appropriate place to insert the breed
        // Add the breed
        var Name = 'breed';
        if (Type == BreedType.DirectedLink)
            Name = 'directed-link-breed';
        if (Type == BreedType.UndirectedLink)
            Name = 'undirected-link-breed';
        this.ChangeCode({
            from: 0,
            to: 0,
            insert: `${Name} [ ${Plural} ${Singular} ]\n`,
        });
        return true;
    }
    /** AppendBreedVariables: Add variables to a breed. */
    AppendBreedVariables(Plural, Variables) {
        Variables = [...new Set(Variables.filter((Item) => reserved.indexOf(Item) == -1))];
        if (Variables.length == 0)
            return false;
        let Cursor = GetCursorInsideMode(this.View.state);
        // Find the first existing statement
        if (Cursor) {
            while (true) {
                if (Cursor.node.name == 'BreedsOwn') {
                    var Own = this.FindFirstChild(Cursor.node, 'Own');
                    var Name = this.GetSlice(Own.from, Own.to).trim().toLowerCase();
                    if (Name === Plural.toLowerCase() + '-own') {
                        this.AddTermToBracket(Variables, Cursor.node);
                        return true;
                    }
                }
                if (!Cursor.nextSibling() || Cursor.node.name === 'Procedure')
                    break;
            }
        }
        // TODO: Find the most appropriate place to insert the breed
        // If not found, append a new statement
        this.ChangeCode({
            from: 0,
            to: 0,
            insert: `${Plural.toLowerCase()}-own [ ${Variables.join(' ')} ]\n`,
        });
        return true;
    }
    // #endregion
    // #region "Procedures"
    ReplaceProcedure(view, name, content) {
        let index = 0;
        name = name.trim().toLowerCase();
        syntaxTree(view.state)
            .cursor()
            .iterate((node) => {
            if (node.name == 'Procedure' && getCodeName(view.state, node) == name) {
                index = node.from + content.length;
                view.dispatch({
                    changes: {
                        from: node.from,
                        to: node.to,
                        insert: content,
                    },
                });
            }
        });
    }
}
//# sourceMappingURL=code-editing.js.map