"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 };
};
/**
 * Parses PlantUML diagrams
 * Replaces <puml> tags with <pic> tags with the appropriate src attribute and generates the diagrams
 * by running the JAR executable
 */
const cheerio_1 = __importDefault(require("cheerio"));
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const child_process_1 = require("child_process");
const crypto = require("crypto");
const fsUtil = __importStar(require("../../utils/fsUtil"));
const logger = __importStar(require("../../utils/logger"));
const urlUtil = __importStar(require("../../utils/urlUtil"));
const LockManager_1 = __importDefault(require("../../utils/LockManager"));
const JAR_PATH = path_1.default.resolve(__dirname, 'plantuml.jar');
const PUML_EXT = '.png';
/**
* This Map maintains a record of processed diagrams. When a diagram is generated or regenerated,
* it's added to this map. Subsequently, if a PUML or non-PUML file is edited, leading to a hot reload,
* the generateDiagram function can avoid redundant regeneration by checking this map.
* If the diagram's identifier is present in the map,
* the generation process is bypassed, thus preventing duplicates.
 */
const processedDiagrams = new Map();
let graphvizCheckCompleted = false;
/**
 * Generates diagram and returns the file name of the diagram
 * @param imageOutputPath output path of the diagram to be generated
 * @param content puml dsl used to generate the puml diagram
 */
function generateDiagram(imageOutputPath, content) {
    var _a, _b, _c;
    const hashKey = crypto.createHash('md5').update(imageOutputPath + content).digest('hex').toString();
    // Avoid generating twice
    if (processedDiagrams.has(imageOutputPath) && ((_a = processedDiagrams.get(imageOutputPath)) === null || _a === void 0 ? void 0 : _a.hashKey) === hashKey) {
        return;
    }
    // Creates output dir if it doesn't exist
    const outputDir = path_1.default.dirname(imageOutputPath);
    if (!fs_1.default.existsSync(outputDir)) {
        fs_1.default.mkdirSync(outputDir, { recursive: true });
    }
    const lockId = LockManager_1.default.createLock();
    // Add new diagram to the map
    processedDiagrams.set(imageOutputPath, { hashKey });
    // Java command to launch PlantUML jar
    const cmd = `java -jar "${JAR_PATH}" -nometadata -pipe > "${imageOutputPath}"`;
    const childProcess = (0, child_process_1.exec)(cmd, {
        cwd: outputDir, // Invoke image generation in the same directory to avoid file inclusion issues
    });
    let errorLog = '';
    (_b = childProcess.stdin) === null || _b === void 0 ? void 0 : _b.write(content, (e) => {
        var _a;
        if (e) {
            logger.debug(e);
            logger.error(`Error generating ${imageOutputPath}`);
        }
        (_a = childProcess.stdin) === null || _a === void 0 ? void 0 : _a.end();
    });
    childProcess.on('error', (error) => {
        logger.debug(error);
        logger.error(`Error generating ${imageOutputPath}`);
        LockManager_1.default.deleteLock(lockId);
    });
    (_c = childProcess.stderr) === null || _c === void 0 ? void 0 : _c.on('data', (errorMsg) => {
        errorLog += errorMsg;
    });
    childProcess.on('exit', () => {
        // This goes to the log file, but not shown on the console
        logger.debug(errorLog);
        LockManager_1.default.deleteLock(lockId);
    });
}
module.exports = {
    tagConfig: {
        puml: {
            isSpecial: true,
            attributes: [
                {
                    name: 'name',
                    isRelative: true,
                },
                {
                    name: 'src',
                    isRelative: true,
                    isSourceFile: true,
                },
            ],
        },
    },
    beforeSiteGenerate: () => {
        graphvizCheckCompleted = false;
    },
    processNode: (_pluginContext, node, config) => {
        if (node.name !== 'puml') {
            return;
        }
        if (process.platform !== 'win32' && config.plantumlCheck && !graphvizCheckCompleted) {
            (0, child_process_1.exec)(`java -jar "${JAR_PATH}" -testdot`, (_error, _stdout, stderr) => {
                if (stderr.includes('Error: No dot executable found')) {
                    logger.warn('You are using PlantUML diagrams but Graphviz is not installed!');
                }
            });
            graphvizCheckCompleted = true;
        }
        node.name = 'pic';
        let pumlContent;
        let pathFromRootToImage;
        if (node.attribs.src) {
            const srcWithoutBaseUrl = urlUtil.stripBaseUrl(node.attribs.src, config.baseUrl);
            const srcWithoutLeadingSlash = srcWithoutBaseUrl.startsWith('/')
                ? srcWithoutBaseUrl.substring(1)
                : srcWithoutBaseUrl;
            const rawPath = path_1.default.resolve(config.rootPath, srcWithoutLeadingSlash);
            try {
                pumlContent = fs_1.default.readFileSync(rawPath, 'utf8');
            }
            catch (err) {
                logger.debug(err);
                logger.error(`Error reading ${rawPath} for <puml> tag`);
                return;
            }
            pathFromRootToImage = fsUtil.setExtension(srcWithoutLeadingSlash, PUML_EXT);
            node.attribs.src = fsUtil.ensurePosix(fsUtil.setExtension(node.attribs.src, PUML_EXT));
        }
        else {
            pumlContent = (0, cheerio_1.default)(node).text();
            if (node.attribs.name) {
                const nameWithoutBaseUrl = urlUtil.stripBaseUrl(node.attribs.name, config.baseUrl);
                const nameWithoutLeadingSlash = nameWithoutBaseUrl.startsWith('/')
                    ? nameWithoutBaseUrl.substring(1)
                    : nameWithoutBaseUrl;
                pathFromRootToImage = fsUtil.ensurePosix(fsUtil.setExtension(nameWithoutLeadingSlash, PUML_EXT));
                delete node.attribs.name;
            }
            else {
                const normalizedContent = pumlContent.replace(/\r\n/g, '\n');
                const hashedContent = crypto.createHash('md5').update(normalizedContent).digest('hex').toString();
                pathFromRootToImage = `${hashedContent}${PUML_EXT}`;
            }
            node.attribs.src = `${config.baseUrl}/${pathFromRootToImage}`;
        }
        node.children = [];
        const imageOutputPath = path_1.default.resolve(config.outputPath, pathFromRootToImage);
        generateDiagram(imageOutputPath, pumlContent);
    },
};
//# sourceMappingURL=markbind-plugin-plantuml.js.map