const errors = require('./Errors');

utils = {}; //utility functions

// ==== OBJECT ORIENTED JAVASCRIPT ===============
// This is a general purpose library of functions,
//Parts of this file (consolearr, and createElement) are duplicated in dweb-transport; dweb-transports and dweb-objects repo

// Utility function to print a array of items but just show number and last.
utils.consolearr  = (arr) => ((arr && arr.length >0) ? [arr.length+" items inc:", arr[arr.length-1]] : arr );

utils.stringfrom = function(foo, hints={}) {
    try {
        // Generic way to turn anything into a string
        if (foo.constructor.name === "Url") // Can't use instanceof for some bizarre reason
            return foo.href;
        if (typeof foo === "string")
            return foo;
        return foo.toString();  // Last chance try and convert to a string based on a method of the object (could check for its existence)
    } catch (err) {
        throw new errors.CodingError(`Unable to turn ${foo} into a string ${err.message}`)
    }
};

utils.p_timeout = function(promise, ms, errorstr) {
    /* In a certain period, timeout and reject
    promise:    A promise we want to watch to completion
    ms:         Time in milliseconds to allow it to run
    errorstr:   Error message in reject error
    throws:     TimeoutError on timeout with message = errorstr
     */
    let timer = null;

    return Promise.race([
        new Promise((resolve, reject) => {
            timer = setTimeout(reject, ms, new errors.TimeoutError(errorstr || `Timed out in ${ms}ms`));
        }),
        promise.then((value) => {
            clearTimeout(timer);
            return value;
        })
    ]);
}

utils.createElement = function(tag, attrs, children) {        // Note arguments is set to tag, attrs, child1, child2 etc
    // Note identical version in dweb-transport/js/utils.js and dweb-transports/utils.js and dweb-objects/utils.js
    var element = document.createElement(tag);
    for (let name in attrs) {
        let attrname = (name.toLowerCase() === "classname" ? "class" : name);
        if (name === "dangerouslySetInnerHTML") {
            element.innerHTML = attrs[name]["__html"];
            delete attrs.dangerouslySetInnerHTML;
        }
        if (attrs.hasOwnProperty(name)) {
            let value = attrs[name];
            if (value === true) {
                element.setAttribute(attrname, name);
            } else if (typeof value === "object" && !Array.isArray(value)) {
                if (["style"].includes(attrname)) {  // e.g. style: {fontSize: "124px"}
                    for (let k in value) {
                        element[attrname][k] = value[k];
                    }
                } else {
                    // Assume we are really trying to set the value to an object, allow it
                    element[attrname] = value;  // Wont let us use setAttribute(attrname, value) unclear if because unknow attribute or object
                }
            } else if (value !== false && value != null) {
                element.setAttribute(attrname, value.toString());
            }
        }
    }
    for (let i = 2; i < arguments.length; i++) { // Everything after attrs
        let child = arguments[i];
        if (!child) {
        } else if (Array.isArray(child)) {
            child.map((c) => element.appendChild(c.nodeType == null ?
                document.createTextNode(c.toString()) : c))
        }
        else {
            element.appendChild(
                child.nodeType == null ?
                    document.createTextNode(child.toString()) : child);
        }
    }
    return element;
}


exports = module.exports = utils;