
/**
 * A JsObject is a definition of Json that is not a
 * primitive value nor an array.
 */
export type JsObject = {
    [propName: string]: any;
}

/**
 *
 * @param item
 * @returns {any|boolean}
 */
export function isObject(item:any) {
    return (item && typeof item === 'object' && !Array.isArray(item) && item !== null);
}

// We inform Type script compiler about the existence of
// the ES6 assign method
declare global {
    interface ObjectConstructor {
        assign(target, ...sources): any
    }
}
/*
// Polyfill for object assign
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
if (typeof Object.assign != 'function') {
    Object.assign = function(target) {
        'use strict';
        if (target == null) {
            throw new TypeError('Cannot convert undefined or null to object');
        }

        target = Object(target);
        for (var index = 1; index < arguments.length; index++) {
            var source = arguments[index];
            if (source != null) {
                for (var key in source) {
                    if (Object.prototype.hasOwnProperty.call(source, key)) {
                        target[key] = source[key];
                    }
                }
            }
        }
        return target;
    };
}*/
// see http://stackoverflow.com/questions/27936772/deep-object-merging-in-es6-es7

/**
 *
 * @param target
 * @param source
 * @returns {({}&any)|any}
 */
// TODO THis function is buggy. It canot be used accurately in HasDescriptor.updateInPlace or copy
export default function deepAssign(target:JsObject, source:JsObject) : JsObject{
    const output = Object.assign({}, target);
    if (isObject(target) && isObject(source)) {
        Object.keys(source).forEach(key => {
            if (isObject(source[key])) {
                if (!(key in target))
                    Object.assign(output, { [key]: source[key] });
                else
                    output[key] = deepAssign(target[key], source[key]);
            } else {
                Object.assign(output, { [key]: source[key] });
            }
        });
    }
    return output;
}