import {defineFunctionBuilders} from "../defineFunction";
import {mathsym, makeOrd, makeSpan} from "../buildCommon";
import {MathNode, TextNode} from "../mathMLTree";
import ParseError from "../ParseError";

// A map of CSS-based spacing functions to their CSS class.
const cssSpace = new Map<string, string>([
    ["\\nobreak", "nobreak"],
    ["\\allowbreak", "allowbreak"],
]);

// A lookup table to determine whether a spacing function/symbol should be
// treated like a regular space character.  If a symbol or command is a key
// in this table, then it should be a regular space character.  Furthermore,
// the associated value may have a `className` specifying an extra CSS class
// to add to the created `span`.
const regularSpace = new Map<string, {className?: string}>([
    [" ", {}],
    ["\\ ", {}],
    ["~", {className: "nobreak"}],
    ["\\space", {}],
    ["\\nobreakspace", {className: "nobreak"}],
]);

// ParseNode<"spacing"> created in Parser.js from the "spacing" symbol Groups in
// src/symbols.js.
defineFunctionBuilders({
    type: "spacing",
    htmlBuilder(group, options) {
        const regularSpaceItem = regularSpace.get(group.text);
        const cssSpaceClass = cssSpace.get(group.text);

        if (regularSpaceItem) {
            const className = regularSpaceItem.className || "";
            // Spaces are generated by adding an actual space. Each of these
            // things has an entry in the symbols table, so these will be turned
            // into appropriate outputs.
            if (group.mode === "text") {
                const ord = makeOrd(group, options);
                ord.classes.push(className);
                return ord;
            } else {
                return makeSpan(["mspace", className],
                    [mathsym(group.text, group.mode, options)],
                    options);
            }
        } else if (cssSpaceClass) {
            // Spaces based on just a CSS class.
            return makeSpan(
                ["mspace", cssSpaceClass],
                [], options);
        } else {
            throw new ParseError(`Unknown type of space "${group.text}"`);
        }
    },
    mathmlBuilder(group, options) {
        let node;

        if (regularSpace.has(group.text)) {
            node = new MathNode(
                "mtext", [new TextNode("\u00a0")]);
        } else if (cssSpace.has(group.text)) {
            // CSS-based MathML spaces (\nobreak, \allowbreak) are ignored
            return new MathNode("mspace");
        } else {
            throw new ParseError(`Unknown type of space "${group.text}"`);
        }

        return node;
    },
});
