// @ts-nocheck
/**
 * TokenStack pattern
 *
 * @typedef module:@citation-js/core.util.TokenStack~pattern
 * @type {String|RegExp|module:@citation-js/core.util.TokenStack~match|Array<module:@citation-js/core.util.TokenStack~pattern>}
 */
/**
 * TokenStack pattern sequence
 *
 * @typedef module:@citation-js/core.util.TokenStack~sequence
 * @type {String|Array<module:@citation-js/core.util.TokenStack~pattern>}
 */
/**
 * @callback module:@citation-js/core.util.TokenStack~match
 * @param {String} token - token
 * @param {Number} index - token index
 * @param {Array<String>} stack - token stack
 * @return {Boolean} match or not
 */
/**
 * @callback module:@citation-js/core.util.TokenStack~tokenMap
 * @param {String} token - token
 * @return {String} new token
 */
/**
 * @callback module:@citation-js/core.util.TokenStack~tokenFilter
 * @param {String} token - token
 * @return {Boolean} keep or not
 */
/**
 * Create a TokenStack for parsing strings with complex escape sequences.
 *
 * @memberof module:@citation-js/core.util
 *
 * @param {Array<String>} array - list of tokens
 */
class TokenStack {
    constructor(array) {
        this.stack = array;
        this.index = 0;
        this.current = this.stack[this.index];
    }
    /**
     * Get string representation of pattern.
     *
     * @access protected
     *
     * @param {String|RegExp} pattern - pattern
     *
     * @return {String} string representation
     */
    static getPatternText(pattern) {
        return `"${pattern instanceof RegExp ? pattern.source : pattern}"`;
    }
    /**
     * Get a single callback to match a token against one or several patterns.
     *
     * @access protected
     *
     * @param {module:@citation-js/core.util.TokenStack~pattern} pattern - pattern
     *
     * @return {module:@citation-js/core.util.TokenStack~match} Match callback
     */
    static getMatchCallback(pattern) {
        if (Array.isArray(pattern)) {
            const matches = pattern.map(TokenStack.getMatchCallback);
            return (token) => matches.some((matchCallback) => matchCallback(token));
        }
        else if (pattern instanceof Function) {
            return pattern;
        }
        else if (pattern instanceof RegExp) {
            return (token) => pattern.test(token);
        }
        else {
            return (token) => pattern === token;
        }
    }
    /**
     * Get a number representing the number of tokens that are left.
     *
     * @access protected
     *
     * @return {Number} tokens left
     */
    tokensLeft() {
        return this.stack.length - this.index;
    }
    /**
     * Match current token against pattern.
     *
     * @access protected
     *
     * @param {module:@citation-js/core.util.TokenStack~pattern} pattern - pattern
     *
     * @return {Boolean} match
     */
    matches(pattern) {
        return TokenStack.getMatchCallback(pattern)(this.current, this.index, this.stack);
    }
    /**
     * Match current token against pattern.
     *
     * @access protected
     *
     * @param {module:@citation-js/core.util.TokenStack~sequence} pattern - pattern
     *
     * @return {Boolean} match
     */
    matchesSequence(sequence) {
        const part = this.stack.slice(this.index, this.index + sequence.length).join('');
        return typeof sequence === 'string'
            ? part === sequence
            : sequence.every((pattern, index) => TokenStack.getMatchCallback(pattern)(part[index]));
    }
    /**
     * Consume a single token if possible, and throw if not.
     *
     * @access protected
     *
     * @param {module:@citation-js/core.util.TokenStack~pattern} [pattern=/^[\s\S]$/] - pattern
     * @param {Object} options
     * @param {Boolean} [options.inverse=false] - invert pattern
     * @param {Boolean} [options.spaced=true] - allow leading and trailing whitespace
     *
     * @return {String} token
     * @throws {SyntaxError} Unexpected token at index: Expected pattern, got token
     */
    consumeToken(pattern = /^[\s\S]$/, { inverse = false, spaced = true } = {}) {
        if (spaced) {
            this.consumeWhitespace();
        }
        const token = this.current;
        const match = TokenStack.getMatchCallback(pattern)(token, this.index, this.stack);
        if (match) {
            this.current = this.stack[++this.index];
        }
        else {
            throw new SyntaxError(`Unexpected token at index ${this.index}: Expected ${TokenStack.getPatternText(pattern)}, got "${token}"`);
        }
        if (spaced) {
            this.consumeWhitespace();
        }
        return token;
    }
    /**
     * Consume a single token if possible, and throw if not.
     *
     * @access protected
     *
     * @param {module:@citation-js/core.util.TokenStack~pattern} [pattern=/^\s$/] - whitespace pattern
     * @param {Object} options
     * @param {Boolean} [options.optional=true] - allow having no whitespace
     *
     * @return {String} matched whitespace
     * @throws {SyntaxError} Unexpected token at index: Expected whitespace, got token
     */
    consumeWhitespace(pattern = /^\s$/, { optional = true } = {}) {
        return this.consume(pattern, { min: +!optional });
    }
    /**
     * Consume n tokens. Throws if not enough tokens left
     *
     * @access protected
     *
     * @param {Number} length - number of tokens
     *
     * @return {String} consumed tokens
     * @throws {SyntaxError} Not enough tokens left
     */
    consumeN(length) {
        if (this.tokensLeft() < length) {
            throw new SyntaxError('Not enough tokens left');
        }
        const start = this.index;
        while (length--) {
            this.current = this.stack[++this.index];
        }
        return this.stack.slice(start, this.index).join('');
    }
    /**
     * Consume a pattern spanning multiple tokens ('sequence').
     *
     * @access protected
     *
     * @param {module:@citation-js/core.util.TokenStack~sequence} sequence - sequence
     *
     * @return {String} consumed tokens
     * @throws {SyntaxError} Expected sequence, got tokens
     */
    consumeSequence(sequence) {
        if (this.matchesSequence(sequence)) {
            return this.consumeN(sequence.length);
        }
        else {
            throw new SyntaxError(`Expected "${sequence}", got "${this.consumeN(sequence.length)}"`);
        }
    }
    /**
     * Consumes all consecutive tokens matching pattern. Throws if number of matched tokens not within range min-max.
     *
     * @access protected
     *
     * @param {module:@citation-js/core.util.TokenStack~pattern} [pattern=/^[\s\S]$/] - pattern
     * @param {Object} options
     * @param {Boolean} [options.inverse=false] - invert pattern
     * @param {Number} [options.min=0] - mininum number of consumed tokens
     * @param {Number} [options.max=Infinity] - maximum number of matched tokens
     * @param {module:@citation-js/core.util.TokenStack~tokenMap} [options.tokenMap] - map tokens before returning
     * @param {module:@citation-js/core.util.TokenStack~tokenFilter} [options.tokenFilter] - filter tokens before returning
     *
     * @return {String} consumed tokens
     * @throws {SyntaxError} Not enough tokens
     * @throws {SyntaxError} Too many tokens
     */
    consume(pattern = /^[\s\S]$/, { min = 0, max = Infinity, inverse = false, tokenMap, tokenFilter } = {}) {
        const start = this.index;
        const match = TokenStack.getMatchCallback(pattern);
        while (match(this.current, this.index, this.stack) !== inverse) {
            this.current = this.stack[++this.index];
        }
        let consumed = this.stack.slice(start, this.index);
        if (consumed.length < min) {
            throw new SyntaxError(`Not enough ${TokenStack.getPatternText(pattern)}`);
        }
        else if (consumed.length > max) {
            throw new SyntaxError(`Too many ${TokenStack.getPatternText(pattern)}`);
        }
        if (tokenMap) {
            consumed = consumed.map(tokenMap);
        }
        if (tokenFilter) {
            consumed = consumed.filter(tokenFilter);
        }
        return consumed.join('');
    }
}
export default TokenStack;
