"use strict";
const should = require("should");
const _ = require("underscore");
const BinaryStream = require("node-opcua-binary-stream").BinaryStream;
const hexDump = require("node-opcua-debug").hexDump;
const factories = require("node-opcua-factory");
const assert_arrays_are_equal = require("node-opcua-test-helpers/src/typedarray_helpers").assert_arrays_are_equal;
const packet_analyzer = require("..").packet_analyzer;
const analyze_object_binary_encoding = require("..").analyze_object_binary_encoding;
function dump_block_in_debug_mode(buffer, id, options) {
    if (process.env.DEBUG) {
        console.log(hexDump(buffer));
        packet_analyzer(buffer, id, 0, 0, options);
    }
}
function isTypedArray(v) {
    if (v && v.buffer && v.buffer instanceof ArrayBuffer) {
        return true;
    }
    return false;
}
function isArrayOrTypedArray(v) {
    return isTypedArray(v) || v instanceof Array;
}
function compare(obj_reloaded,obj) {
    function displayError(p,expected,actual) {
        console.log(" ---------------------------------- error in encode_decode_round_trip_test".yellow);
        console.log(" key ".red, p);
        console.log(" expected ".red, JSON.stringify(expected));
        console.log(" actual   ".cyan, JSON.stringify(actual));
    }
    Object.keys(obj_reloaded).forEach(function (p) {
        try {
            if (isArrayOrTypedArray(obj[p])) {
                assert_arrays_are_equal(obj_reloaded[p], obj[p]);
            } else {
                JSON.stringify(obj_reloaded[p]).should.eql(JSON.stringify(obj[p]));
            }
        } catch (err) {
            displayError(p,obj[p],obj_reloaded[p]);
            console.log(obj.toString());
            console.log(obj_reloaded.toString());
            // re throw exception
            throw err;
        }
    });
}
/**
 * @method encode_decode_round_trip_test
 * @param obj {Object} : object to test ( the object must provide a binaryStoreSize,encode,decode method
 * @param [options]
 * @param callback_buffer
 * @return {*}
 */
function encode_decode_round_trip_test(obj, options, callback_buffer) {
    if (!callback_buffer && _.isFunction(options)) {
        callback_buffer = options;
        options = {};
    }
    callback_buffer = callback_buffer || dump_block_in_debug_mode;
    should.exist(obj);
    const expandedNodeId = obj.encodingDefaultBinary;
    const size = obj.binaryStoreSize(options);
    const stream = new BinaryStream(Buffer.alloc(size));
    obj.encode(stream, options);
    callback_buffer(stream._buffer, obj.encodingDefaultBinary, options);
    stream.rewind();
    const obj_reloaded = factories.constructObject(expandedNodeId);
    obj_reloaded.decode(stream, options);
    function redirectToNull(functor) {
        const old = console.log;
        console.log = function () { };
        try {
            functor();
        }
        catch(err) {
            throw err;
        }
        finally  {
            console.log = old;
        }
    }
    redirectToNull(function () {
        analyze_object_binary_encoding(obj);
    });
    compare(obj_reloaded,obj);
    return obj_reloaded;
}
exports.encode_decode_round_trip_test = encode_decode_round_trip_test;
function json_encode_decode_round_trip_test(obj, options, callback_buffer) {
    if (!callback_buffer && _.isFunction(options)) {
        callback_buffer = options;
        options = {};
    }
    callback_buffer = callback_buffer || dump_block_in_debug_mode;
    should.exist(obj);
    const json =    JSON.stringify(obj);
    const obj_reloaded = JSON.parse(json);
    //xx console.log(json);
    compare(obj_reloaded,obj);
    return obj_reloaded;
}
exports.json_encode_decode_round_trip_test =  json_encode_decode_round_trip_test;