import get from "get-value";
import isNumber from "is-number";
import getObject from "get-object";
import { isOptions, isObject } from "../utils";
var UtilshasOwn = Object.hasOwnProperty;
export const objectHelpers = {
    /**
     * Stringify a value to JSON and escape quotes for safe HTML attribute usage.
     *
     * ```handlebars
     * {{json object}}
     * <!-- results in:  '{&quot;foo&quot;:&quot;bar&quot;}' -->
     * ```
     * @param {Object|Array|String|Number|Boolean|null} `context`
     * @return {String}
     * @api public
     */
    json: function (context) {
        var str = JSON.stringify(context);
        if (typeof str === 'undefined')
            return '';
        return str
            .replace(/'/g, '&apos;')
            .replace(/"/g, '&quot;');
    },
    /**
     * Stringify a value to JSON and return a `Handlebars.SafeString`
  
     *
     * @param {Object|Array|String|Number|Boolean|null} `context`
     * @return {Handlebars.SafeString}
     * @api public
     */
    jsonScript: function (context) {
        var str = JSON.stringify(context);
        if (typeof str === 'undefined')
            return '';
        return new Handlebars.SafeString(str.replace(/</g, '\\u003c')
            .replace(/>/g, '\\u003e')
            .replace(/&/g, '\\u0026'));
    },
    /**
     * Stringify a value to JSON and URL-encode it.
     *
     * Useful when passing structured data through query strings or
     * attributes where reserved URI characters must be escaped.
     *
     * @param {Object|Array|String|Number|Boolean|null} `context`
     * @return {String}
     * @api public
     */
    jsonEncode: function (context) {
        return encodeURIComponent(JSON.stringify(context));
    },
    /**
     * Extend the context with the properties of other objects.
     * A shallow merge is performed to avoid mutating the context.
     *
     * @param {Object} `objects` One or more objects to extend.
     * @return {Object}
     * @api public
     */
    extend: function ( /*objects*/) {
        var args = [].slice.call(arguments);
        var opts = {};
        if (isOptions(args[args.length - 1])) {
            // remove handlebars options object
            opts = args.pop().hash;
            // re-add handlebars options.hash object
            args.push(opts);
        }
        var context = {};
        for (var i = 0; i < args.length; i++) {
            var obj = args[i];
            if (isObject(obj)) {
                var keys = Object.keys(obj);
                for (var j = 0; j < keys.length; j++) {
                    var key = keys[j];
                    context[key] = obj[key];
                }
            }
        }
        return context;
    },
    /**
     * Block helper that iterates over the properties of
     * an object, exposing each key and value on the context.
     *
     * @param {Object} `context`
     * @param {Object} `options`
     * @return {String}
     * @block
     * @api public
     */
    // forIn : function (obj, options) {
    //   if (!isOptions(options)) {
    //     return obj.inverse(this)
    //   }
    //   var data = utils.createFrame(options, options.hash)
    //   var result = ""
    //   for (var key in obj) {
    //     data.key = key
    //     result += options.fn(obj[key], { data: data })
    //   }
    //   return result
    // },
    /**
     * Block helper that iterates over the **own** properties of
     * an object, exposing each key and value on the context.
     *
     * @param {Object} `obj` The object to iterate over.
     * @param {Object} `options`
     * @return {String}
     * @block
     * @api public
     */
    // forOwn : function (obj, options) {
    //   if (!isOptions(options)) {
    //     return obj.inverse(this)
    //   }
    //   var data = utils.createFrame(options, options.hash)
    //   var result = ""
    //   for (var key in obj) {
    //     if (obj.hasOwnProperty(key)) {
    //       data.key = key
    //       result += options.fn(obj[key], { data: data })
    //     }
    //   }
    //   return result
    // },
    /**
     * Take arguments and, if they are string or number, convert them to a dot-delineated object property path.
     *
     * @param {String|Number} `prop` The property segments to assemble (can be multiple).
     * @return {String}
     * @api public
     */
    toPath: function ( /*prop*/) {
        var prop = [];
        for (var i = 0; i < arguments.length; i++) {
            if (typeof arguments[i] === "string" || typeof arguments[i] === "number") {
                prop.push(arguments[i]);
            }
        }
        return prop.join(".");
    },
    /**
     * Use property paths (`a.b.c`) to get a value or nested value from
     * the context. Works as a regular helper or block helper.
     *
     * @param {String} `prop` The property to get, optionally using dot notation for nested properties.
     * @param {Object} `context` The context object
     * @param {Object} `options` The handlebars options object, if used as a block helper.
     * @return {String}
     * @block
     * @api public
     */
    get: function (prop, context, options) {
        var val = get(context, prop);
        if (options && options.fn) {
            return val ? options.fn(val) : options.inverse(context);
        }
        return val;
    },
    /**
     * Use property paths (`a.b.c`) to get an object from
     * the context. Differs from the `get` helper in that this
     * helper will return the actual object, including the
     * given property key. Also, this helper does not work as a
     * block helper.
     *
     * @param {String} `prop` The property to get, optionally using dot notation for nested properties.
     * @param {Object} `context` The context object
     * @return {String}
     * @api public
     */
    getObject: function (prop, context) {
        return getObject(context, prop);
    },
    /**
     * Return true if `key` is an own, enumerable property
     * of the given `context` object.
     *
     * ```handlebars
     * {{hasOwn context key}}
     * ```
     *
     * @param {String} `key`
     * @param {Object} `context` The context object.
     * @return {Boolean}
     * @api public
     */
    hasOwn: function (context, key) {
        return UtilshasOwn.call(context, key);
    },
    /**
     * Return true if `value` is an object.
     *
     * ```handlebars
     * {{isObject "foo"}}
     * //=> false
     * ```
     * @param {String} `value`
     * @return {Boolean}
     * @api public
     */
    // isObject : function (value) {
    //   return utils.typeOf(value) === "object"
    // },
    /**
     * Parses the given string using `JSON.parse`.
     *
     * ```handlebars
     * <!-- string: '{"foo": "bar"}' -->
     * {{JSONparse string}}
     * <!-- results in: { foo: 'bar' } -->
     * ```
     * @param {String} `string` The string to parse
     * @contributor github.com/keeganstreet
     * @block
     * @api public
     */
    JSONparse: function (str, _options) {
        return JSON.parse(str);
    },
    /**
     * Stringify an object using `JSON.stringify`.
     *
     * ```handlebars
     * <!-- object: { foo: 'bar' } -->
     * {{JSONstringify object}}
     * <!-- results in: '{"foo": "bar"}' -->
     * ```
     * @param {Object} `obj` Object to stringify
     * @return {String}
     * @api public
     */
    JSONstringify: function (obj, indent) {
        if (!isNumber(indent)) {
            indent = 0;
        }
        return JSON.stringify(obj, null, indent);
    },
    /**
     * Deeply merge the properties of the given `objects` with the
     * context object.
     *
     * @param {Object} `object` The target object. Pass an empty object to shallow clone.
     * @param {Object} `objects`
     * @return {Object}
     * @api public
     */
    merge: function ( /*, objects, options*/) {
        var args = [].slice.call(arguments);
        var opts = {};
        if (isOptions(args[args.length - 1])) {
            // remove handlebars options object
            opts = args.pop().hash;
            // re-add options.hash
            args.push(opts);
        }
        return Object.assign.apply(null, args);
    },
    /**
     * Alias for parseJSON. this will be
     * deprecated in a future release
     */
    // helpers.parseJSON = helpers.JSONparse
    /**
     * Pick properties from the context object.
     *
     * @param {Array|String} `properties` One or more properties to pick.
     * @param {Object} `context`
     * @param {Object} `options` Handlebars options object.
     * @return {Object} Returns an object with the picked values. If used as a block helper, the values are passed as context to the inner block. If no values are found, the context is passed to the inverse block.
     * @block
     * @api public
     */
    // pick : function (props, context, options) {
    //   var keys = arrayHelpers.arrayify(props)
    //   var len = keys.length,
    //     i = -1
    //   var result = {}
    //   while (++i < len) {
    //     result = objectHelpers.extend({}, result, objectHelpers.getObject(keys[i], context))
    //   }
    //   if (options.fn) {
    //     if (Object.keys(result).length) {
    //       return options.fn(result)
    //     }
    //     return options.inverse(context)
    //   }
    //   return result
    // },
    /**
     * Alias for JSONstringify. this will be
     * deprecated in a future release
     */
    stringify: function (obj, indent) {
        if (!isNumber(indent)) {
            indent = 0;
        }
        return JSON.stringify(obj, null, indent);
    },
};
