"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (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 (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.create = exports.stringify = void 0;
const common = __importStar(require("./common"));
const fs_1 = require("fs");
const jsondiffpatch = require('jsondiffpatch').create({});
const remove__prefixedProperties = (key, value) => key.startsWith("__") ? undefined : value;
const uniqueId = Symbol('uniqueId');
let nextId = 0;
const ignoreProperties = {
    location: true
};
function ignore(property, value) {
    if (value == null) {
        return true;
    }
    return ignoreProperties[property] || property.startsWith("_") || /^\/ion\b/.test(property);
}
function stringify(object, indent = 2) {
    return JSON.stringify(cloneWithJsonReferences(object), null, indent);
}
exports.stringify = stringify;
function cloneWithJsonReferences(object, path = []) {
    let type = typeof object;
    if (object == null || type == 'string' || type == 'number' || type == 'boolean')
        return object;
    let className = object.constructor.path || object.constructor.name;
    if (object.toJSON)
        object = object.toJSON();
    let clone = Array.isArray(object) ? [] : className === "Object" ? {} : { "": className };
    if (object instanceof Map) {
        for (let key of object.keys()) {
            let value = object.get(key);
            if (ignore(key, value)) {
                continue;
            }
            clone[key] = cloneWithJsonReferences(value, path);
        }
    }
    if (object instanceof Set) {
        let index = 0;
        for (let value of object.values()) {
            clone[index++] = cloneWithJsonReferences(value, path);
        }
    }
    else {
        for (let property in object) {
            let value = object[property];
            if (ignore(property, value)) {
                continue;
            }
            path.push(property);
            clone[property] = cloneWithJsonReferences(value, path);
            path.pop();
        }
    }
    return clone;
}
function format(html) {
    //  replace { "": "TypeName" ... } with TypeName { ... }
    return html.replace(/{\s+&quot;&quot;:\s+&quot;(\w+)&quot;,/gi, "$1 {")
        //  replace multiline object with a single value as a single line
        .replace(/{\s+([^\n]+)\s+}/gi, "{ $1 }")
        //  replace line ends with breaks
        .replace(/\\n/g, '<br>');
}
function create(outputPath) {
    let stylePath = require.resolve("jsondiffpatch/public/formatters-styles/html.css");
    let rfs = fs_1.readFileSync; // abstraction to prevent parcel from resolving
    let style = rfs(stylePath);
    // console.log(style)
    // let outputToStyle = np.relative(np.dirname(outputPath), "node_modules/jsondiffpatch/dist/formatters-styles/html.css")
    let passes = [];
    return function (names, ast) {
        if (names != null) {
            if (!Array.isArray(names))
                names = [names];
            passes.push([names, cloneWithJsonReferences(ast)]);
            // passes.push([names, JSON.parse(JSON.stringify(cloneWithJsonReferences(ast), remove__prefixedProperties))])
        }
        else {
            // convert to HTML
            let previous = null;
            let html = [
                `
<html>
    <head>
        <style>
        ${style.toString()}
        </style>
        <style>
        :root {
            --border: solid 1px gray;
        }
        body {
            display: flex;
            flex-direction: row;
            font-size: 12px;
            font-family: Monaco;
        }
        article {
            border: var(--border);
        }
        article {
            border: var(--border);
            padding: 0px;
        }
        article:not(:last-child) {
            border-right: none;
        }
        article header {
            color: rgb(150,150,150);
            background-color: rgb(45,45,45);
            font-weight: bold;
            padding: 8px;
            margin: 0px;
            border-bottom: var(--border);
        }
        article header:hover {
            display: flex;
            flex-direction: column;
        }
        article p {
            padding: 0px 8px;
            white-space: pre;
        }
        ins {
            color: lightgreen;
            text-decoration: none;
        }
        del {
            color: red;
            text-decoration: none;
        }
        </style>
    </head>
    <body onclick="location.reload(true)">
`,
                ...passes.map(([names, ast]) => {
                    if (!Array.isArray(names))
                        names = [names];
                    // convert to show refs
                    let delta = previous != null ? jsondiffpatch.diff(previous, ast) : null;
                    let html;
                    if (typeof ast === "string") {
                        html = ast;
                    }
                    else {
                        html = require('jsondiffpatch/src/formatters/html').format(delta || {}, previous || ast);
                    }
                    previous = ast;
                    return `
        <article>
            <header><span>${names.join(', </span><span>')}</span></header>
            <p>${format(html)}</p>
        </article>
    `;
                }),
                `
    </body>
</html>
`
            ].join('');
            common.write(outputPath, html);
            console.log('debug written to ', outputPath);
        }
    };
}
exports.create = create;
