const _regANSI = /(?:(?:\u001b\[)|\u009b)(?:(?:[0-9]{1,3})?(?:(?:;[0-9]{0,3})*)?[A-M|f-m])|\u001b[A-M]/;
const _defColors = {
    reset: [
        'fff',
        '000'
    ],
    black: '000',
    red: 'ff0000',
    green: '209805',
    yellow: 'e8bf03',
    blue: '0000ff',
    magenta: 'ff00ff',
    cyan: '00ffee',
    lightgrey: 'f0f0f0',
    darkgrey: '888'
};
const _styles = {
    30: 'black',
    31: 'red',
    32: 'green',
    33: 'yellow',
    34: 'blue',
    35: 'magenta',
    36: 'cyan',
    37: 'lightgrey'
};
const _colorMode = {
    2: 'rgb'
};
const _openTags = {
    1: 'font-weight:bold',
    2: 'opacity:0.5',
    3: '<i>',
    4: '<u>',
    8: 'display:none',
    9: '<del>',
    38: (match)=>{
        const mode = _colorMode[match[0]];
        if ('rgb' === mode) {
            const r = match[1];
            const g = match[2];
            const b = match[3];
            match.advance(4);
            return `color: rgb(${r},${g},${b})`;
        }
    },
    48: (match)=>{
        const mode = _colorMode[match[0]];
        if ('rgb' === mode) {
            const r = match[1];
            const g = match[2];
            const b = match[3];
            match.advance(4);
            return `background-color: rgb(${r},${g},${b})`;
        }
    }
};
const _openTagToCloseTag = {
    3: '23',
    4: '24',
    9: '29'
};
const _closeTags = {
    0: (ansiCodes)=>{
        if (!ansiCodes) return '</span>';
        if (!ansiCodes.length) return '';
        let code;
        let ret = '';
        while(code = ansiCodes.pop()){
            const closeTag = _openTagToCloseTag[code];
            if (closeTag) {
                ret += _closeTags[closeTag];
                continue;
            }
            ret += '</span>';
        }
        return ret;
    },
    23: '</i>',
    24: '</u>',
    29: '</del>'
};
for (const n of [
    21,
    22,
    27,
    28,
    39,
    49
])_closeTags[n] = '</span>';
function normalizeSeq(seq) {
    if (null == seq) return null;
    if (seq.startsWith(';')) return seq.slice(1);
    return seq;
}
function ansiHTML(text) {
    if (!_regANSI.test(text)) return text;
    const ansiCodes = [];
    let ret = text.replace(/\x1b\[(?:[0-9]{1,3})?(?:(?:;[0-9]{0,3})*)?m/g, (m)=>{
        var _m_match;
        const match = null == (_m_match = m.match(/(;?\d+)/g)) ? void 0 : _m_match.map(normalizeSeq);
        Object.defineProperty(match, 'advance', {
            value: function(count) {
                this.splice(0, count);
            }
        });
        let rep = '';
        let seq;
        while(seq = match[0]){
            match.advance(1);
            rep += applySeq(seq);
        }
        return rep;
        function applySeq(seq) {
            let other = _openTags[seq];
            if (other && (other = 'function' == typeof other ? other(match) : other)) {
                let ret = '';
                if ('0' === seq) ret += _closeTags[seq](ansiCodes);
                if (-1 !== ansiCodes.indexOf(seq)) {
                    ansiCodes.pop();
                    return '</span>';
                }
                ansiCodes.push(seq);
                return ret + ('<' === other[0] ? other : `<span style="${other};">`);
            }
            const ct = _closeTags[seq];
            if ('function' == typeof ct) return ct(ansiCodes);
            if (ct) {
                ansiCodes.pop();
                return ct;
            }
            return '';
        }
    });
    const l = ansiCodes.length;
    if (l > 0) ret += Array(l + 1).join('</span>');
    return ret;
}
ansiHTML.setColors = (colors)=>{
    if ('object' != typeof colors) throw new Error('`colors` parameter must be an Object.');
    const _finalColors = {};
    for(const key in _defColors){
        let hex = colors.hasOwnProperty(key) ? colors[key] : null;
        if (!hex) {
            _finalColors[key] = _defColors[key];
            continue;
        }
        if ('reset' === key) {
            if ('string' == typeof hex) hex = [
                hex
            ];
            if (!Array.isArray(hex) || 0 === hex.length || hex.some((h)=>'string' != typeof h)) throw new Error(`The value of \`${key}\` property must be an Array and each item could only be a hex string, e.g.: FF0000`);
            const defHexColor = _defColors[key];
            if (!hex[0]) hex[0] = defHexColor[0];
            if (1 === hex.length || !hex[1]) {
                hex = [
                    hex[0]
                ];
                hex.push(defHexColor[1]);
            }
            hex = hex.slice(0, 2);
        } else if ('string' != typeof hex) throw new Error(`The value of \`${key}\` property must be a hex string, e.g.: FF0000`);
        _finalColors[key] = hex;
    }
    _setTags(_finalColors);
};
ansiHTML.reset = ()=>{
    _setTags(_defColors);
};
ansiHTML.tags = {};
if (Object.defineProperty) {
    Object.defineProperty(ansiHTML.tags, 'open', {
        get: ()=>_openTags
    });
    Object.defineProperty(ansiHTML.tags, 'close', {
        get: ()=>_closeTags
    });
} else {
    ansiHTML.tags.open = _openTags;
    ansiHTML.tags.close = _closeTags;
}
function _setTags(colors) {
    _openTags['0'] = `font-weight:normal;opacity:1;color:#${colors.reset[0]};background:#${colors.reset[1]}`;
    _openTags['7'] = `color:#${colors.reset[1]};background:#${colors.reset[0]}`;
    _openTags['90'] = `color:#${colors.darkgrey}`;
    for(const code in _styles){
        const color = _styles[code];
        const oriColor = colors[color] || '000';
        _openTags[code] = `color:#${oriColor}`;
        const codeInt = Number.parseInt(code);
        _openTags[(codeInt + 10).toString()] = `background:#${oriColor}`;
    }
}
ansiHTML.reset();
export default ansiHTML;
