const MASK_FLAGS = new Set('aACL09#&?');
const MASK_REQUIRED_FLAGS = new Set('0#LA&');
const MASK_PATTERNS = new Map([
    ['C', /(?!^$)/u],
    ['&', /[^\p{Separator}]/u],
    ['a', /[\p{Letter}\p{Number}\p{Separator}]/u],
    ['A', /[\p{Letter}\p{Number}]/u],
    ['?', /[\p{Letter}\p{Separator}]/u],
    ['L', /\p{Letter}/u],
    ['0', /\p{Number}/u],
    ['9', /[\p{Number}\p{Separator}]/u],
    ['#', /[\p{Number}\-+]/u],
]);
function replaceIMENumbers(string) {
    return string.replace(/[０１２３４５６７８９]/g, (num) => ({
        '１': '1',
        '２': '2',
        '３': '3',
        '４': '4',
        '５': '5',
        '６': '6',
        '７': '7',
        '８': '8',
        '９': '9',
        '０': '0',
    })[num]);
}
function validate(char, flag) {
    return MASK_PATTERNS.get(flag)?.test(char) ?? false;
}
const MaskDefaultOptions = {
    format: 'CCCCCCCCCC',
    promptCharacter: '_',
};
export class MaskParser {
    get literalPositions() {
        return this._literalPositions;
    }
    get escapedMask() {
        return this._escapedMask;
    }
    get emptyMask() {
        return this.apply();
    }
    get mask() {
        return this._options.format;
    }
    set mask(value) {
        this._options.format = value || this._options.format;
        this._parseMaskLiterals();
    }
    get prompt() {
        return this._options.promptCharacter;
    }
    set prompt(value) {
        this._options.promptCharacter = value
            ? value.substring(0, 1)
            : this._options.promptCharacter;
    }
    constructor(options) {
        this._literals = new Map();
        this._literalPositions = new Set();
        this._escapedMask = '';
        this._options = { ...MaskDefaultOptions, ...options };
        this._parseMaskLiterals();
    }
    _parseMaskLiterals() {
        const mask = this.mask;
        const length = mask.length;
        const escapedMaskChars = [];
        let currentPos = 0;
        this._literals.clear();
        for (let i = 0; i < length; i++) {
            const [current, next] = [mask.charAt(i), mask.charAt(i + 1)];
            if (current === '\\' && MASK_FLAGS.has(next)) {
                this._literals.set(currentPos, next);
                escapedMaskChars.push(next);
                i++;
            }
            else if (MASK_FLAGS.has(current)) {
                escapedMaskChars.push(current);
            }
            else {
                this._literals.set(currentPos, current);
                escapedMaskChars.push(current);
            }
            currentPos++;
        }
        this._escapedMask = escapedMaskChars.join('');
        this._literalPositions = new Set(this._literals.keys());
    }
    _getRequiredNonLiteralPositions() {
        const literalPositions = this.literalPositions;
        const escapedMask = this._escapedMask;
        const length = escapedMask.length;
        const result = [];
        for (let i = 0; i < length; i++) {
            const char = escapedMask[i];
            if (MASK_REQUIRED_FLAGS.has(char) && !literalPositions.has(i)) {
                result.push(i);
            }
        }
        return result;
    }
    getPreviousNonLiteralPosition(start) {
        const literalPositions = this.literalPositions;
        for (let i = start; i > 0; i--) {
            if (!literalPositions.has(i)) {
                return i;
            }
        }
        return start;
    }
    getNextNonLiteralPosition(start) {
        const literalPositions = this.literalPositions;
        const length = this._escapedMask.length;
        for (let i = start; i < length; i++) {
            if (!literalPositions.has(i)) {
                return i;
            }
        }
        return start;
    }
    replace(maskString, value, start, end) {
        const literalPositions = this.literalPositions;
        const escapedMask = this._escapedMask;
        const length = this._escapedMask.length;
        const prompt = this.prompt;
        const endBoundary = Math.min(end, length);
        const maskedChars = Array.from(maskString || this.apply(''));
        const inputChars = Array.from(replaceIMENumbers(value));
        const inputLength = inputChars.length;
        for (let i = start; i < endBoundary; i++) {
            if (!literalPositions.has(i)) {
                maskedChars[i] = prompt;
            }
        }
        let cursor = start;
        let inputIndex = 0;
        let maskPosition = start;
        for (; maskPosition < length && inputIndex < inputLength; maskPosition++) {
            if (literalPositions.has(maskPosition)) {
                cursor = maskPosition + 1;
                continue;
            }
            const char = inputChars[inputIndex];
            if (validate(char, escapedMask[maskPosition]) && char !== prompt) {
                maskedChars[maskPosition] = char;
                cursor = maskPosition + 1;
                inputIndex++;
            }
            else {
                inputIndex++;
                maskPosition--;
            }
        }
        while (cursor < length && literalPositions.has(cursor)) {
            cursor++;
        }
        return {
            value: maskedChars.join(''),
            end: cursor,
        };
    }
    parse(masked = '') {
        const literalPositions = this.literalPositions;
        const prompt = this.prompt;
        const length = masked.length;
        let result = '';
        for (let i = 0; i < length; i++) {
            const char = masked[i];
            if (!literalPositions.has(i) && char !== prompt) {
                result += char;
            }
        }
        return result;
    }
    isValidString(input = '') {
        const prompt = this.prompt;
        return this._getRequiredNonLiteralPositions().every((position) => {
            const char = input.charAt(position);
            return (validate(char, this._escapedMask.charAt(position)) && char !== prompt);
        });
    }
    apply(input = '') {
        const literals = this._literals;
        const prompt = this.prompt;
        const escapedMask = this._escapedMask;
        const length = escapedMask.length;
        const inputLength = input.length;
        const result = new Array(escapedMask.length).fill(prompt);
        for (const [position, literal] of literals.entries()) {
            result[position] = literal;
        }
        if (!input) {
            return result.join('');
        }
        let inputIndex = 0;
        for (let i = 0; i < length; i++) {
            if (inputIndex >= inputLength) {
                break;
            }
            if (literals.has(i)) {
                continue;
            }
            if (validate(input.charAt(inputIndex), escapedMask.charAt(i))) {
                result[i] = input.charAt(inputIndex);
            }
            inputIndex++;
        }
        return result.join('');
    }
}
//# sourceMappingURL=mask-parser.js.map