"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.processUlNode = processUlNode;
const cheerio_1 = __importDefault(require("cheerio"));
const logger = __importStar(require("../utils/logger"));
const markdown_it_icons_1 = require("../lib/markdown-it/plugins/markdown-it-icons");
const emojiDictionary = require('../lib/markdown-it/patches/markdown-it-emoji-fixed');
const emojiData = emojiDictionary;
const ICON_ATTRIBUTES = ['icon', 'i-width', 'i-height', 'i-size', 'i-class', 'i-spacing', 'text', 't-size', 't-class', 'once'];
class TextsManager {
    constructor() {
        this.texts = [];
        this.nextTextPointer = 0;
        this.texts = [];
    }
    isInUse() {
        return this.texts.length > 0;
    }
    stopUsage() {
        this.texts = [];
        this.nextTextPointer = 0;
    }
    next() {
        if (this.texts.length === 0) {
            throw new Error('No texts');
        }
        const next = this.texts[this.nextTextPointer];
        if (this.nextTextPointer < this.texts.length - 1) {
            this.nextTextPointer += 1;
        }
        return next;
    }
    resetTexts(texts) {
        this.texts = texts;
        this.nextTextPointer = 0;
    }
}
function classifyIcon(icon) {
    const isEmoji = Object.prototype.hasOwnProperty.call(emojiData, icon);
    return {
        isEmoji,
        unicodeEmoji: isEmoji
            ? emojiData[icon]
            : undefined,
    };
}
function createTextSpan(iconAttrs) {
    if (iconAttrs.text === undefined || iconAttrs.text.length === 0) {
        return null;
    }
    const spanNode = (0, cheerio_1.default)(`<span aria-hidden="true">${iconAttrs.text}</span>`)
        .css({
        'font-size': iconAttrs.textSize,
    }).addClass(iconAttrs.textClassName || '');
    const iconSpacing = iconAttrs.spacing || '0.35em';
    return spanNode.css({
        'line-height': 'unset',
        'margin-inline-end': iconSpacing,
        'align-self': 'flex-start',
        'flex-shrink': '0',
    });
}
function createIconSpan(iconAttrs) {
    if (iconAttrs.icon === undefined || iconAttrs.icon.length === 0) {
        return null;
    }
    let spanContent;
    const { isEmoji, unicodeEmoji, } = classifyIcon(iconAttrs.icon);
    if (isEmoji) {
        spanContent = `<span aria-hidden="true">${unicodeEmoji}</span>`;
    }
    else {
        spanContent = (0, markdown_it_icons_1.processIconString)(iconAttrs.icon);
    }
    let spanNode;
    if (spanContent === null && iconAttrs.icon !== undefined) {
        const img = (0, cheerio_1.default)(`<img src='${iconAttrs.icon}' alt='Icon'>`)
            .css({ width: iconAttrs.width, height: iconAttrs.height, display: 'inline-block' })
            .addClass(iconAttrs.iconClassName || '');
        img.append('\u200B');
        spanContent = (0, cheerio_1.default)('<span></span>').append(img).css({
            'padding-bottom': '0.3em',
            'padding-top': '0.3em',
        });
        spanNode = (0, cheerio_1.default)(spanContent).css({ 'font-size': 'unset', 'min-width': '16px' });
    }
    else {
        spanNode = (0, cheerio_1.default)(spanContent).css({ 'font-size': 'unset', 'min-width': '16px' });
        spanNode = spanNode.css({ 'font-size': iconAttrs.iconSize }).addClass(iconAttrs.iconClassName || '');
    }
    // Add invisible character to avoid the element from being empty
    spanNode.append('\u200B');
    const iconSpacing = iconAttrs.text ? '0.35em' : iconAttrs.spacing || '0.35em';
    return spanNode.css({
        'line-height': 'unset',
        'margin-inline-end': iconSpacing,
        'align-self': 'flex-start',
        'flex-shrink': '0',
    });
}
function updateNodeStyle(node) {
    const nodeCheerio = (0, cheerio_1.default)(node);
    nodeCheerio.css({
        'list-style-type': 'none',
        'padding-inline-start': '0px',
    });
}
// If an item has a specified icon, that icon and its attributes will be saved and used
// for it and for subsequent items at that level to prevent duplication of icons
// attribute declarations.
// If once is true, its icons and/or attributes will only be used for that item.
// Items with once icons/attributes do not overwrite the previously saved icon/
// attributes, meaning that subsequent items will still use the last saved
// icon/attributes.
const getIconAttributes = (node, renderMdInline, iconAttrsSoFar) => {
    if ((iconAttrsSoFar === null || iconAttrsSoFar === void 0 ? void 0 : iconAttrsSoFar.icon) === undefined && node.attribs.icon === undefined
        && (iconAttrsSoFar === null || iconAttrsSoFar === void 0 ? void 0 : iconAttrsSoFar.text) === undefined && node.attribs.text === undefined) {
        return null;
    }
    return {
        icon: node.attribs.icon !== undefined ? node.attribs.icon : iconAttrsSoFar === null || iconAttrsSoFar === void 0 ? void 0 : iconAttrsSoFar.icon,
        width: node.attribs['i-width'] !== undefined ? node.attribs['i-width'] : iconAttrsSoFar === null || iconAttrsSoFar === void 0 ? void 0 : iconAttrsSoFar.width,
        height: node.attribs['i-height'] !== undefined ? node.attribs['i-height'] : iconAttrsSoFar === null || iconAttrsSoFar === void 0 ? void 0 : iconAttrsSoFar.height,
        iconSize: node.attribs['i-size'] !== undefined ? node.attribs['i-size'] : iconAttrsSoFar === null || iconAttrsSoFar === void 0 ? void 0 : iconAttrsSoFar.iconSize,
        iconClassName: node.attribs['i-class'] !== undefined
            ? node.attribs['i-class']
            : iconAttrsSoFar === null || iconAttrsSoFar === void 0 ? void 0 : iconAttrsSoFar.iconClassName,
        text: node.attribs.text !== undefined ? renderMdInline(node.attribs.text) : iconAttrsSoFar === null || iconAttrsSoFar === void 0 ? void 0 : iconAttrsSoFar.text,
        textClassName: node.attribs['t-class'] !== undefined
            ? node.attribs['t-class']
            : iconAttrsSoFar === null || iconAttrsSoFar === void 0 ? void 0 : iconAttrsSoFar.textClassName,
        textSize: node.attribs['t-size'] !== undefined ? node.attribs['t-size'] : iconAttrsSoFar === null || iconAttrsSoFar === void 0 ? void 0 : iconAttrsSoFar.textSize,
        spacing: node.attribs['i-spacing'] !== undefined ? node.attribs['i-spacing'] : iconAttrsSoFar === null || iconAttrsSoFar === void 0 ? void 0 : iconAttrsSoFar.spacing,
        once: (node.attribs.once === true || node.attribs.once === 'true'),
    };
};
const deleteAttributes = (node, attributes) => {
    attributes.forEach((attr) => {
        delete node.attribs[attr];
    });
};
function updateLi(node, iconAttributes, renderMdInline) {
    const curLiIcon = getIconAttributes(node, renderMdInline, iconAttributes);
    deleteAttributes(node, ICON_ATTRIBUTES);
    // Create a new div and span
    const div = (0, cheerio_1.default)('<div></div>');
    const iconSpan = createIconSpan(curLiIcon);
    const textSpan = createTextSpan(curLiIcon);
    div.append((0, cheerio_1.default)(node.children).remove());
    // Append iconSpan and div to the child
    if (iconSpan !== null) {
        (0, cheerio_1.default)(node).append(iconSpan);
    }
    if (textSpan !== null) {
        (0, cheerio_1.default)(node).append(textSpan);
    }
    (0, cheerio_1.default)(node).append(div);
    (0, cheerio_1.default)(node).css({
        display: 'flex',
    });
}
// This function ensures the first item at that level must also be customized.
// If not, the list will be invalidated and default bullets will be used.
// This is to prevent unintentional mixing of standard and customized lists.
// See https://github.com/MarkBind/markbind/pull/2316#discussion_r1255364486 for more details.
function handleLiNode(node, iconAttrValue, renderMdInline) {
    var _a, _b, _c, _d, _e;
    const textManager = iconAttrValue.textsManager;
    if (node.attribs.texts) {
        const texts = node.attribs.texts.replace(/(?<!\\)'/g, '"').replace(/\\'/g, '\'');
        try {
            const parsed = JSON.parse(texts);
            if (!Array.isArray(parsed)) {
                throw new Error('Texts attribute must be an array');
            }
            const parsedStringArray = parsed.map((obj) => obj.toString());
            textManager.resetTexts(parsedStringArray);
        }
        catch (e) {
            logger.error(`Error parsing texts: ${texts}, please check the format of the texts attribute\n`);
        }
    }
    if (textManager.isInUse()) {
        if (!node.attribs.text) {
            node.attribs.text = textManager.next();
        }
        else if (!node.attribs.once || node.attribs.once !== 'true') {
            textManager.stopUsage();
        }
    }
    if (iconAttrValue.isFirst) {
        const nodeIconAttrs = getIconAttributes(node, renderMdInline);
        // Check if first item is customized with icon or text
        if ((nodeIconAttrs === null || nodeIconAttrs === void 0 ? void 0 : nodeIconAttrs.icon) !== undefined || (nodeIconAttrs === null || nodeIconAttrs === void 0 ? void 0 : nodeIconAttrs.text) !== undefined) {
            iconAttrValue.addIcons = true;
        }
        // Save if the icon is not once
        if (!(nodeIconAttrs === null || nodeIconAttrs === void 0 ? void 0 : nodeIconAttrs.once)) {
            iconAttrValue.iconAttrs = nodeIconAttrs;
        }
        iconAttrValue.isFirst = false;
    }
    else if (iconAttrValue.iconAttrs) {
        const nodeIconAttrs = getIconAttributes(node, renderMdInline, iconAttrValue.iconAttrs);
        // Save if there is icon or text, and not once
        if (((nodeIconAttrs === null || nodeIconAttrs === void 0 ? void 0 : nodeIconAttrs.icon) !== undefined || (nodeIconAttrs === null || nodeIconAttrs === void 0 ? void 0 : nodeIconAttrs.text) !== undefined) && !(nodeIconAttrs === null || nodeIconAttrs === void 0 ? void 0 : nodeIconAttrs.once)) {
            iconAttrValue.iconAttrs = nodeIconAttrs;
        }
    }
    if (!iconAttrValue.addIcons) {
        return;
    }
    // for subsequent items, if first item is once, there is no previous icon
    // so future attributes that are not once will need to be saved
    if (((_a = iconAttrValue.iconAttrs) === null || _a === void 0 ? void 0 : _a.icon) === undefined && ((_b = iconAttrValue.iconAttrs) === null || _b === void 0 ? void 0 : _b.text) === undefined) {
        // There is no previous icon and no previous text
        const nodeIconAttrs = getIconAttributes(node, renderMdInline);
        // Save if current item has icon or text, and it is not once
        if (((nodeIconAttrs === null || nodeIconAttrs === void 0 ? void 0 : nodeIconAttrs.icon) !== undefined || (nodeIconAttrs === null || nodeIconAttrs === void 0 ? void 0 : nodeIconAttrs.text) !== undefined) && !(nodeIconAttrs === null || nodeIconAttrs === void 0 ? void 0 : nodeIconAttrs.once)) {
            iconAttrValue.iconAttrs = nodeIconAttrs;
        }
    }
    // update only if current item has icon/text or previous items have saved icon/text
    const nodeIconAttrs = getIconAttributes(node, renderMdInline);
    if ((nodeIconAttrs === null || nodeIconAttrs === void 0 ? void 0 : nodeIconAttrs.icon) !== undefined || ((_c = iconAttrValue.iconAttrs) === null || _c === void 0 ? void 0 : _c.icon) !== undefined
        || (nodeIconAttrs === null || nodeIconAttrs === void 0 ? void 0 : nodeIconAttrs.text) !== undefined || ((_d = iconAttrValue.iconAttrs) === null || _d === void 0 ? void 0 : _d.text) !== undefined) {
        updateLi(node, (_e = iconAttrValue.iconAttrs) !== null && _e !== void 0 ? _e : {}, renderMdInline);
    }
}
function processUlNode(node, renderMdInline) {
    const nodeAsMbNode = node;
    if (nodeAsMbNode.attribs.isIconListProcessed === 'true') {
        delete nodeAsMbNode.attribs.isIconListProcessed;
        return;
    }
    const iconAttrs = [];
    function dfs(currentNode, level) {
        if (!iconAttrs[level]) {
            iconAttrs[level] = {
                isFirst: true, addIcons: false, iconAttrs: null, textsManager: new TextsManager(),
            };
        }
        const ulNode = currentNode;
        const liNodes = ulNode.children.filter(child => child.name === 'li');
        liNodes.forEach((liNode) => {
            var _a;
            const ulChildren = (_a = liNode.children) === null || _a === void 0 ? void 0 : _a.filter(child => child.name === 'ul');
            handleLiNode(liNode, iconAttrs[level], renderMdInline);
            ulChildren === null || ulChildren === void 0 ? void 0 : ulChildren.forEach((ulChildNode) => {
                // Traverse the children if any
                dfs(ulChildNode, level + 1);
                // Insert an `isIconListProcessed` flag attribute to the node.
                if (ulChildNode.attribs) {
                    ulChildNode.attribs.isIconListProcessed = 'true';
                }
            });
        });
        if (iconAttrs[level].addIcons) {
            updateNodeStyle(ulNode);
        }
    }
    dfs(node, 0);
}
//# sourceMappingURL=CustomListIconProcessor.js.map