/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Stefan Goessner - 2017-18. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *  Modified by Le Tan for MathJax support in VNote.
 *  We mark all the formulas and enclose them with $ in class 'tex-to-render' for
 *  further processing by MathJax.
 *--------------------------------------------------------------------------------------------*/
'use strict';

function texmath(md, options) {
    let delimiters = ['dollars'];
    if (options && options.delimiters) {
        delimiters = options.delimiters;
    }

    for (let i = 0; i < delimiters.length; ++i) {
        let deli = delimiters[i];
        if (deli in texmath.rules) {
            for (let rule of texmath.rules[deli].inline) {
                md.inline.ruler.before('escape', rule.name, texmath.inline(rule));  // ! important
                md.renderer.rules[rule.name] = (tokens, idx) => rule.tmpl.replace(/\$1/,texmath.render(tokens[idx].content,false));
            }

            for (let rule of texmath.rules[deli].block) {
                md.block.ruler.before('fence', rule.name, texmath.block(rule));
                md.renderer.rules[rule.name] = (tokens, idx) => rule.tmpl.replace(/\$2/,tokens[idx].info)  // equation number
                                                                         .replace(/\$1/,texmath.render(tokens[idx].content,true));
            }
        }
    }
}

texmath.applyRule = function(rule, str, beg) {
    let pre, match, post;
    rule.rex.lastIndex = beg;
    pre = str.startsWith(rule.tag,beg) && (!rule.pre || rule.pre(str,beg));
    match = pre && rule.rex.exec(str);
    if (match) {
        match.lastIndex = rule.rex.lastIndex;
        post = !rule.post || rule.post(str, match.lastIndex-1);
    }
    rule.rex.lastIndex = 0;
    return post && match;
}

texmath.inline = (rule) =>
    function(state, silent) {
        let res = texmath.applyRule(rule, state.src, state.pos);
        if (res) {
            if (!silent) {
                let token = state.push(rule.name, 'math', 0);
                token.content = res[1];  // group 1 from regex ..
                token.markup = rule.tag;
            }
            state.pos = res.lastIndex;
        }
        return !!res;
    }

texmath.block = (rule) =>
    function(state, begLine, endLine, silent) {
        let res = texmath.applyRule(rule, state.src, state.bMarks[begLine] + state.tShift[begLine]);
        if (res) {
            if (!silent) {
                let token = state.push(rule.name, 'math', 0);
                token.block = true;
                token.content = res[1];
                token.info = res[2];
                token.markup = rule.tag;
            }
            for (let line=begLine, endpos=res.lastIndex-1; line < endLine; line++)
                if (endpos >= state.bMarks[line] && endpos <= state.eMarks[line]) { // line for end of block math found ...
                    state.line = line+1;
                    break;
                }
            state.pos = res.lastIndex;
        }
        return !!res;
    }

texmath.render = function(tex, isblock) {
    let res;
    if (isblock) {
        res = '$$$$' + tex + '$$$$';
    } else {
        res = '$$' + tex + '$$';
    }

    return res;
}

texmath.$_pre = (str,beg) => {
    let prv = beg > 0 ? str[beg-1].charCodeAt(0) : false;
    return !prv || prv !== 0x5c                // no backslash,
                && (prv < 0x30 || prv > 0x39); // no decimal digit .. before opening '$'
}
texmath.$_post = (str,end) => {
    let nxt = str[end+1] && str[end+1].charCodeAt(0);
    return !nxt || nxt < 0x30 || nxt > 0x39;   // no decimal digit .. after closing '$'
}

texmath.rules = {
    brackets: {
        inline: [
            {   name: 'math_inline',
                rex: /\\\((.+?)\\\)/gy,
                tmpl: '<x-eq class="tex-to-render">$1</x-eq>',
                tag: '\\('
            }
        ],
        block: [
            {   name: 'math_block_eqno',
                rex: /\\\[(.+?)\\\]\s*?\(([^)$\r\n]+?)\)\s*$/gmy,
                tmpl: '<x-eqs><x-eqn class="tex-to-render">$1</x-eqn><span>($2)</span></x-eqs>',
                tag: '\\['
            },
            {   name: 'math_block',
                rex: /\\\[(.+?)\\\]\s*$/gmy,
                tmpl: '<x-eqn class="tex-to-render">$1</x-eqn>',
                tag: '\\['
            }
        ]
    },
    gitlab: {
        inline: [
            {   name: 'math_inline',
                rex: /\$`(.+?)`\$/gy,
                tmpl: '<x-eq class="tex-to-render">$1</x-eq>',
                tag: '$`'
            }
        ],
        block: [
            {   name: 'math_block_eqno',
                rex: /`{3}math\s+?([^`]+?)\s+?`{3}\s*?\(([^)$\r\n]+?)\)\s*$/gmy,
                tmpl: '<x-eqs><x-eqn class="tex-to-render">$1</x-eqn><span>($2)</span></x-eqs>',
                tag: '```math'
            },
            {   name: 'math_block',
                rex: /`{3}math\s+?([^`]+?)\s+?`{3}\s*$/gmy,
                tmpl: '<x-eqn class="tex-to-render">$1</x-eqn>',
                tag: '```math'
            }
        ]
    },
    dollars: {
        inline: [
            {   name: 'math_inline',
                rex: /\$(\S[^$\r\n]*?[^\s\\]{1}?)\$/gy,
                tmpl: '<x-eq class="tex-to-render">$1</x-eq>',
                tag: '$',
                pre: texmath.$_pre,
                post: texmath.$_post
            },
            {   name: 'math_single',
                rex: /\$([^$\s\\]{1}?)\$/gy,
                tmpl: '<x-eq class="tex-to-render">$1</x-eq>',
                tag: '$',
                pre: texmath.$_pre,
                post: texmath.$_post
            }
        ],
        block: [
            {   name: 'math_block_eqno',
                rex: /\${2}((?:[^$]|\$(?!\$))*?)\${2}\s*?\(([^)$\r\n]+?)\)\s*$/gmy,
                tmpl: '<x-eqs><x-eqn class="tex-to-render">$1</x-eqn><span>($2)</span></x-eqs>',
                tag: '$$'
            },
            {   name: 'math_block',
                rex: /\${2}((?:[^$]|\$(?!\$))*?)\${2}\s*$/gmy,
                tmpl: '<x-eqn class="tex-to-render">$1</x-eqn>',
                tag: '$$'
            }
        ]
    },
    raw: {
        inline: [],
        block: [
            {
                name: 'math_block',
                rex: /(\\begin\s*\{([^{}\s\r\n]+)\}(?:[^\\]|\\(?!end\s*\{\2\}))*\\end\s*\{\2\})\s*$/gmy,
                tmpl: '<x-eqn class="tex-to-render">$1</x-eqn>',
                tag: '\\begin'
            }
        ]
    }
};

if (typeof module === "object" && module.exports)
   module.exports = texmath;
