"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.debugReviver = exports.debugReplacer = exports.reviverMemoize = exports.reviverExpander = exports.reviver = exports.replacer = exports.stringifyReduceLimit = exports.stringifyReduce = void 0;
const lib_types_1 = require("@shardeum-foundation/lib-types");
const __1 = require("../");
const objToString = Object.prototype.toString;
const objKeys = Object.keys ||
    ((obj) => {
        const keys = [];
        // tslint:disable-next-line: forin
        for (const name in obj) {
            keys.push(name);
        }
        return keys;
    });
const stringifyReduce = (val, isArrayProp) => {
    let i, max, str, keys, key, propVal, toStr;
    if (val === true) {
        return 'true';
    }
    if (val === false) {
        return 'false';
    }
    switch (typeof val) {
        case 'object':
            if (val === null) {
                return null;
            }
            else if (val.toJSON && typeof val.toJSON === 'function') {
                return (0, exports.stringifyReduce)(val.toJSON(), isArrayProp);
            }
            else if (val instanceof Map) {
                // let mapContainer = {stringifyReduce_map_2_array:[...val.entries()]}
                // return stringifyReduce(mapContainer)
                const mapContainer = {
                    dataType: 'stringifyReduce_map_2_array',
                    value: Array.from(val.entries()), // or with spread: value: [...originalObject]
                };
                return (0, exports.stringifyReduce)(mapContainer);
            }
            else if (val instanceof Uint8Array) {
                // this seems to work.  choosing a lossy option but that is more readable for this context
                const buffer = Buffer.from(val);
                return buffer.toString('hex');
            }
            else {
                toStr = objToString.call(val);
                if (toStr === '[object Array]') {
                    str = '[';
                    max = val.length - 1;
                    for (i = 0; i < max; i++) {
                        // eslint-disable-next-line security/detect-object-injection
                        str += (0, exports.stringifyReduce)(val[i], true) + ',';
                    }
                    if (max > -1) {
                        // eslint-disable-next-line security/detect-object-injection
                        str += (0, exports.stringifyReduce)(val[i], true);
                    }
                    return str + ']';
                }
                else if (toStr === '[object Object]') {
                    // only object is left
                    keys = objKeys(val).sort();
                    max = keys.length;
                    str = '';
                    i = 0;
                    while (i < max) {
                        // eslint-disable-next-line security/detect-object-injection
                        key = keys[i];
                        // eslint-disable-next-line security/detect-object-injection
                        propVal = (0, exports.stringifyReduce)(val[key], false);
                        if (propVal !== undefined) {
                            if (str) {
                                str += ',';
                            }
                            str += lib_types_1.Utils.safeStringify(key) + ':' + propVal;
                        }
                        i++;
                    }
                    return '{' + str + '}';
                }
                else {
                    return lib_types_1.Utils.safeStringify(val);
                }
            }
        case 'function':
        case 'undefined':
            return isArrayProp ? null : undefined;
        case 'string': {
            const reduced = (0, __1.makeShortHash)(val);
            return lib_types_1.Utils.safeStringify(reduced);
        }
        case 'bigint':
            // Add some special identifier for bigint
            // return JSON.stringify({__BigInt__: val.toString()})
            return lib_types_1.Utils.safeStringify(val.toString(16));
        default:
            if (typeof val === 'bigint') {
                val = val.toString(16);
            }
            return isFinite(val) ? val : null;
    }
};
exports.stringifyReduce = stringifyReduce;
const stringifyReduceLimit = (val, limit = 100, isArrayProp) => {
    let i, max, str, keys, key, propVal, toStr;
    if (limit < 0) {
        return str + 'LIMIT';
    }
    if (val === true) {
        return 'true';
    }
    if (val === false) {
        return 'false';
    }
    switch (typeof val) {
        case 'object':
            if (val === null) {
                return null;
            }
            else if (val.toJSON && typeof val.toJSON === 'function') {
                return (0, exports.stringifyReduceLimit)(val.toJSON(), limit, isArrayProp);
            }
            else {
                toStr = objToString.call(val);
                if (toStr === '[object Array]') {
                    str = '[';
                    max = val.length - 1;
                    for (i = 0; i < max; i++) {
                        // eslint-disable-next-line security/detect-object-injection
                        str += (0, exports.stringifyReduceLimit)(val[i], limit - str.length, true) + ',';
                        if (str.length > limit) {
                            return str + 'LIMIT';
                        }
                    }
                    if (max > -1) {
                        // eslint-disable-next-line security/detect-object-injection
                        str += (0, exports.stringifyReduceLimit)(val[i], limit - str.length, true);
                    }
                    return str + ']';
                }
                else if (toStr === '[object Object]') {
                    // only object is left
                    keys = objKeys(val).sort();
                    max = keys.length;
                    str = '';
                    i = 0;
                    while (i < max) {
                        key = keys[Number(i)];
                        // eslint-disable-next-line security/detect-object-injection
                        propVal = (0, exports.stringifyReduceLimit)(val[key], limit - str.length, false);
                        if (propVal !== undefined) {
                            if (str) {
                                str += ',';
                            }
                            str += lib_types_1.Utils.safeStringify(key) + ':' + propVal;
                        }
                        i++;
                        if (str.length > limit) {
                            return str + 'LIMIT';
                        }
                    }
                    return '{' + str + '}';
                }
                else {
                    return lib_types_1.Utils.safeStringify(val);
                }
            }
        case 'function':
        case 'undefined':
            return isArrayProp ? null : undefined;
        case 'string': {
            const reduced = (0, __1.makeShortHash)(val);
            return lib_types_1.Utils.safeStringify(reduced);
        }
        default:
            if (typeof val === 'bigint') {
                val = val.toString();
            }
            return isFinite(val) ? val : null;
    }
};
exports.stringifyReduceLimit = stringifyReduceLimit;
const replacer = (_key, value) => {
    const originalObject = value; // this[key]
    if (originalObject instanceof Map) {
        return {
            dataType: 'stringifyReduce_map_2_array',
            value: Array.from(originalObject.entries()), // or with spread: value: [...originalObject]
        };
    }
    else {
        return value;
    }
};
exports.replacer = replacer;
const reviver = (_key, value) => {
    if (typeof value === 'object' && value !== null && 'dataType' in value) {
        if (value.dataType === 'stringifyReduce_map_2_array') {
            return new Map(value.value);
        }
    }
    else {
        return value;
    }
};
exports.reviver = reviver;
const reviverExpander = (_key, value) => {
    if (typeof value === 'object' && value !== null) {
        if ('dataType' in value && value.dataType === 'stringifyReduce_map_2_array') {
            return new Map(value.value);
        }
    }
    if (typeof value === 'string' && value.length === 10 && value[4] === 'x') {
        const res = value.slice(0, 4) + '0'.repeat(55) + value.slice(5, 5 + 5);
        return res;
    }
    return value;
};
exports.reviverExpander = reviverExpander;
//Figure out certain chunky objects and store them in their own table
// commenting stringifyReduceMemoize as its not being used anywhere
//export const stringifyReduceMemoize = (val, isArrayProp?: boolean) => {}
const reviverMemoize = (_key, value) => {
    if (typeof value === 'object' && value !== null) {
        if ('dataType' in value && value.dataType === 'stringifyReduce_map_2_array') {
            return new Map(value.value);
        }
    }
    return value;
};
exports.reviverMemoize = reviverMemoize;
const debugReplacer = (key, value) => {
    const originalObject = value; // this[key]
    if (key === 'accountTempMap') {
        return {};
    }
    if (typeof originalObject === 'string') {
        const reduced = (0, __1.makeShortHash)(originalObject);
        return reduced;
    }
    if (originalObject instanceof Map) {
        return {
            dataType: 'stringifyReduce_map_2_array',
            value: Array.from(originalObject.entries()), // or with spread: value: [...originalObject]
        };
    }
    else {
        return value;
    }
};
exports.debugReplacer = debugReplacer;
const debugReviver = (_key, value) => {
    if (typeof value === 'object' && value !== null) {
        if ('dataType' in value && value.dataType === 'stringifyReduce_map_2_array') {
            return new Map(value.value);
        }
    }
    return value;
};
exports.debugReviver = debugReviver;
//# sourceMappingURL=stringifyReduce.js.map