(function() {
    'use strict';

    exports.Utils = {
        /**
         * generate a random id
         *
         * @param {string} prefix - string to prefix to the random id string
         * @return {string}
         */
        getRandomId: function(prefix) {
            return (prefix || 'g') + (Math.random() + Date.now() + '' + Math.random()).replace(/\./g, '');
        },
        /**
         * @return {object} - { resolve: promiseResolveCallback, then: promiseThenCallback }
         */
        getPromise: function() {
            var promise = {};
            promise.promise = new Promise(function(resolve) {
                promise.resolve = resolve;
            });
            promise.then = function(callback) {
                promise.promise.then(callback);
            };
            return promise;
        },

        cookie: { // cookie functions
            get: function(name) {
                return decodeURIComponent(
                    document.cookie.replace(
                        new RegExp(
                            '(?:(?:^|.*;)\\s*' + encodeURIComponent(name).replace(/[\-\.\+\*]/g, '\\$&') + '\\s*\\=\\s*([^;]*).*$)|^.*$'),
                            '$1'
                        )
                ) || null;
            },
            /**
             * @param {string} key - key
             * @param {*} value - value to save
             * @param {int} expire - expire in seconds
             * @param {object} options - { domain: '', path: '', secure: true/false }
             * @param {string} options.domain - domain
             * @param {string} options.path - path
             * @param {bool} options.secure - https
             */
            set: function(key, value, expire, options) {
                if (!key || /^(?:expires|max\-age|path|domain|secure)$/i.test(key)) {
                    window.console.warn('invalid key for cookie: ', key);
                } else {
                    var cookie = encodeURIComponent(key) + '=' + encodeURIComponent(value) +
                        '; expires=' + (new Date(Date.now() + expire * 1000)).toUTCString();
                    if (!options) {
                        options = options || {};
                    }
                    if (options.domain) {
                        cookie += '; domain=' + options.domain;
                    }
                    if (options.path) {
                        cookie += '; path=' + options.path;
                    }
                    if (options.secure) {
                        cookie += '; secure';
                    }
                    document.cookie = cookie;
                }
            }
        },

        /**
         * cache.set(key, value, expire_in_seconds)
         * cache.get(key)
         */
        cache: {
            set: function(key, value, expire) {
                localStorage.setItem(
                    key,
                    JSON.stringify({
                        expire: Date.now() + expire * 1000,
                        value: value
                    })
                );
            },
            get: function(key) {
                var stringValue = localStorage.getItem(key);
                if (stringValue) {
                    var cache = JSON.parse(stringValue);
                    if (cache.expire > Date.now()) {
                        return cache.value;
                    }
                    // remove expired values
                    localStorage.removeItem(key);
                }
                return null;
            }
        },

        /**
         * disabled until it is actually needed
         *
         * @param {function} processCallback - function for calculation and processing
         *                                   DOM, window, document, parent are not available in processCallback()
         * @param {function} responseCallback - function to process the result from processCallback()
         * @param {mixed} params - parameters to be passed into processCallback().
         *                       if objects are used, data will be copied i.e. original object will not be modified
         * @see https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers#Example_1_Create_a_generic_asynchronous_eval%28%29
         */
        // processInWorker: function (processCallback, responseCallback, params) {
            // if (window.Worker) {
                // var worker = new Worker('data:text/javascript;base64,' + btoa('\
                        // onmessage = function(e) {\
                            // postMessage(\
                                // (' + processCallback.toString() + ')(e.data)\
                            // );\
                        // }\
                    // '));

                // worker.onmessage = function(e) {
                    // responseCallback(e.data, e);
                // };

                // worker.postMessage(params);
            // } else {
                // responseCallback(processCallback(params));
            // }
        // }

        /*
        extend: function(parentClass, extension, constructor) {
            var childClass = function() {
                parentClass.apply(this, arguments);
                if (constructor) {
                    constructor.apply(this, arguments);
                }
            };
            childClass.prototype = Object.create(parentClass.prototype);
            childClass.prototype.constructor = childClass;
            if (extension) {
                for (var method in extension) {
                    childClass.prototype[method] = extension[method];
                }
            }
            return childClass;
        }
        */
    };

})();
