var OU = OU || {};

OU.utils = {
    svgNS: 'http://www.w3.org/2000/svg',
    svgXLinkNS: 'http://www.w3.org/1999/xlink',
    rootPath: 'components/Highlighter/',

    createElement: function(type, classes, id) {
        var el = document.createElement(type);

        if (typeof id == 'string') {
            el.id = id;
        }
        if (typeof classes != 'undefined') {
            el.className = (Array.isArray(classes)) ? classes.join(' ') : classes.toString();
        }

        return el;
    },

    resizeIFrame: function (height) {
        var html, styles;

        if (!!window.frameElement) {
            if (VLE.serverversion) {
                VLE.resize_iframe();
                return;
            }

            html = document.getElementsByTagName('html')[0];
            styles = getComputedStyle(html);

            window.frameElement.height = (height || parseFloat(styles.marginTop) + parseFloat(styles.marginBottom) + html.offsetHeight);
        }
    },

    triggerCustomEvent: function(el, type, detail, bubbles) {
        //console.log('triggered: ' + type);

        el.dispatchEvent(new CustomEvent(type, {
            detail: detail || {},
            bubbles: (bubbles !== false),
            cancelable: true
        }));
    },

    escapeXml: function(str) {
        return str.replace(/[<>&'"]/g, function (c) {
            switch (c) {
                case '<': return '&lt;';
                case '>': return '&gt;';
                case '&': return '&amp;';
                case '\'': return '&apos;';
                case '"': return '&quot;';
            }
        });
    },

    escapeRegExp: function(str) {
        return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
    },

    getQueryStringAsObj: function() {
        var source = window.location.search;
        var obj = {};
        var groups, tmp, i;

        if (source === '') {
            return false;
        }

        groups = source.substr(1).split('&');

        groups.forEach(function(group) {
            tmp = group.split('=');
            obj[decodeURIComponent(tmp[0])] = decodeURIComponent(tmp[1]);
        });

        return obj;
    },

    getObjectProperties: function(obj) {
        var copy = {};
        var k;

        for (k in obj) {
            if (obj.hasOwnProperty(k) || obj instanceof SVGRect) {
                copy[k] = obj[k];
            }
        }

        return copy;
    },

    getAttributes: function(el) {
        var attrs, i;
        var obj = {};

        if (el.hasAttributes()) {
            attrs = el.attributes;
            for (i = attrs.length - 1; i >= 0; i--) {
                obj[attrs[i].name] = attrs[i].value;
            }
        } else {
            return false;
        }

        return obj;
    },

    setCfg: function(target, source) {
        var i;

        for (i in source) {
            if (source.hasOwnProperty(i)) {
                if (typeof source[i] == 'object' && !(source[i] instanceof Element)) {
                    target[i] = target[i] || (Array.isArray(source[i]) ? [] : {});
                    this.setCfg(target[i], source[i]);
                }
                else {
                    if (Array.isArray(target) || target.hasOwnProperty(i)) {
                        target[i] = source[i];
                    }
                }
            }
        }
    },

    extendDeep: function(target, source) {
        var i;

        target = target || {};

        for (i in source) {
            if (source.hasOwnProperty(i)) {
                if (typeof source[i] == 'object' && !(source[i] instanceof Element)) {
                    if (Array.isArray(source[i])) {
                        target[i] = source[i].slice(0);
                    } else {
                        // target[i] = target[i] || (Array.isArray(source[i]) ? [] : {});
                        target[i] = target[i] || {};
                        this.extendDeep(target[i], source[i]);
                    }
                } else {
                    target[i] = source[i];
                }
            }
        }
    },

    setStyles: function(el, styles) {
        var p;

        for (p in styles) {
            if (styles.hasOwnProperty(p)) {
                switch (p) {
                    case 'backgroundImage':
                        el.style[p] = 'url(' + styles[p] + ')';
                        el.style.backgroundRepeat = 'no-repeat';
                        break;
                    case 'width':
                    case 'height':
                    case 'marginLeft':
                    case 'marginRight':
                    case 'marginTop':
                    case 'marginBottom':
                        if (!isNaN(parseFloat(styles[p])) && isFinite(styles[p])) {
                            el.style[p] = styles[p] + 'px';
                        }
                        break;
                    default:
                        el.style[p] = styles[p];
                }
            }
        }
    },

    removeFalseyValues: function(array) {
        if (Array.isArray(array)) {
            return array.reduce(function(prev, curr) {
                return prev + (curr ? ' ' + curr : '');
            }, '');
        }
    },

    //TODO can we keep track of new order??
    shuffle: function(a) {
        var j, x, i;

        for (i = a.length; i; i--) {
            j = Math.floor(Math.random() * i);
            x = a[i - 1];
            a[i - 1] = a[j];
            a[j] = x;
        }
    },

    toArray: function(arrLike) { // or asArray(), or array(), or *whatever*
        return [].slice.call(arrLike);
    },

    forIn: function(obj, callback) {
        var p, i = 0;

        for (p in obj) {
            if (obj.hasOwnProperty(p)) {
                callback(obj[p], p, i++);
            }
        }
    },

    spliceArray: function(array, index, arrayToInsert) {
        Array.prototype.splice.apply(array, [index, 0].concat(arrayToInsert));
    },

    getPopulatedArray: function(num, val){
        var arr = [], i;

        for (i = 0; i < num; i++){
            arr.push(val);
        }

        return arr;
    },

    //use get/setAttribute for class related functions for SVG
    hasClass: function(el, selector) {
        var className = el.getAttribute('class');

        if (className) {
            return className.split(' ').indexOf(selector) > -1;
        }

        return false;
    },

    addClass: function(el, selector) {
        if (this.hasClass(el, selector)) {
            return;
        }

        var className = el.getAttribute('class') || '';

        el.setAttribute('class', className += (className === '') ? selector : ' ' + selector);
    },

    removeClass: function(el, selector) {
        var className = el.getAttribute('class');
        var array, index;

        if (className) {
            array = className.split(' ');
            index = array.indexOf(selector);

            if (index > -1) {
                if (array.length == 1) {
                    el.removeAttribute('class');
                }
                else {
                    array.splice(index, 1);
                    el.setAttribute('class', array.join(' '));
                }
            }
        }
    },

    /**
     * get all parents of node
     * @param node
     * @returns {[*]}
     */
    parents: function(node) {
        var nodes = [node],
            parent = node.parentNode;

        while (parent) {
            nodes.unshift(parent);
            parent = parent.parentNode;
        }

        /*for (; node; node = node.parentNode) {
            nodes.unshift(node)
        }*/

        return nodes;
    },

    commonAncestor: function(node1, node2) {
        if (node1 == node2) {
            return node1.parentNode;
        }

        var p1 = this.parents(node1),
            p2 = this.parents(node2),
            i;

        if (p1[0] != p2[0]) {
            throw 'No common ancestor!';
        }

        for (i = 0; i < p1.length; i++) {
            if (p1[i] != p2[i]) {
                return p1[i - 1];
            }
        }
    },

    firstBlockParent: function(el) {
        return this.parents(el).reverse().find(function(el) {
            return el.nodeType == 1 && getComputedStyle(el).display !== 'inline';
        });
    },

    emptyElement: function(el) {
        while (el.firstChild){
            el.removeChild(el.firstChild);
        }
    },

    invertArray: function(arr) {
        var i, len;
        var inverted = [];

        for (i = 0, len = arr.length; i < len; i++) {
            if (Array.isArray(arr[i])) {
                inverted[i] = this.invertArray(arr[i]);
            }
            else {
                inverted[i] = (arr[i] <= 0) ? Math.abs(arr[i]) : -Math.abs(arr[i]);
            }
        }

        return inverted;
    },

    arrayMove: function(arr, from, to) {
        arr.splice(to, 0, arr.splice(from, 1)[0]);
    },

    detectProtocol: (function() {
        var protocol = location.protocol;
        var el = document.getElementById('container');
        if (protocol !== "http:" && protocol !== "https:") {
            if (el) {
                el.className = 'not-http';
            }
        }
    })(),

    getOffset: function(el) {
        var test = el, top = 0, left = 0;

        while (!!test && test.tagName.toLowerCase() !== 'body') {
            top += test.offsetTop;
            left += test.offsetLeft;
            test = test.offsetParent;
        }

        return {
            top: top,
            left: left
        };
    },

    /**
     * @param selector {(string|object)} CSS selector, DOM element or jQuery object
     * @param degree {(number|string)} degrees to rotate element
     */
    rotate: function(selector, degree) {
        $(selector).css({
            '-ms-transform': 'rotate(' + degree + 'deg)',
            //'-ms-transform-origin': '50% 50%',
            '-webkit-transform': 'rotate(' + degree + 'deg)',
            //'-webkit-transform-origin': '50% 50%',
            'transform': 'rotate(' + degree + 'deg)'
            //'transform-origin': '50% 50%'
        });
    },

    /**
     * @param svg {object} SVG DOM element
     * @param args {object} arguments object
     */
    improveSVGAccessibility: function(svg, args) {
        var title = document.createElementNS('http://www.w3.org/2000/svg', 'title');
        var description = document.createElementNS('http://www.w3.org/2000/svg', 'desc');

        if (svg.nodeName.toLowerCase() != 'svg') {
            console.log('No SVG element passed');
            return;
        }

        if (args) {
            title.textContent = (!!args.title) ? args.title : 'Interactive diagram';
            description.textContent = (!!args.description) ? args.description : 'An interactive diagram with clickable areas';
        }

        svg.setAttribute('aria-labelledby', 'title desc'); //aria-describedby should be used when better supported
        svg.insertBefore(description, svg.firstChild);
        svg.insertBefore(title, svg.firstChild);
    },

    /**
     * @param selector {string} CSS selector
     * @param context {object} SVG element
     * @param toggle boolean
     */
    makeSvgElementTabbable: function(selector, context, toggle) {
        var elements = Array.prototype.slice.call(context.querySelectorAll(selector));
        var a, i, attrs, ref, array = [];

        elements.forEach(function(el) {
            a = document.createElementNS('http://www.w3.org/2000/svg', 'a');
            ref = el.previousSibling;

            a.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', 'javascript:void(0);');
            a.setAttribute('role', 'button');

            if (toggle) {
                a.setAttribute('aria-pressed', 'false');
            }

            if (el.hasAttributes()) {
                attrs = el.attributes;

                for(i = attrs.length - 1; i >= 0; i--) {
                    a.setAttribute(attrs[i].name, attrs[i].value);
                }
            }

            while (el.lastChild) {
                a.appendChild(el.firstChild);
            }

            array.push(a);
            el.parentNode.replaceChild(a, el);
        });

        return array;
    },

    /**
     * @param selector {string} CSS selector
     * @param context {object} an ancestor DOM element to use as context for selector
     */
    reverseChildren: function(selector, context) {
        context = (typeof context === 'undefined') ? document : context;
        var children = context.querySelectorAll(selector);
        var parent = children[0].parentNode;
        var i;

        if (parent.nodeName.toLowerCase() == 'svg') {
            parent = parent.insertBefore(document.createElementNS(this.svgNS, 'g'), children[0]);

            for (i = 0; i < children.length; i++) {
                parent.appendChild(children[i]);
            }
        }

        i = parent.childNodes.length;

        while (i--) {
            parent.appendChild(parent.childNodes[i]);
        }
    },

    isCanvasSupported: function() {
        var elem = document.createElement('canvas');
        return !!(elem.getContext && elem.getContext('2d'));
    },

    isSVGSupported: function() {
        return document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1");
    },

    showUnsupportedMsg: function(el, msg) {
        var msgDiv = document.createElement('div');

        el = el || document.getElementsByTagName('body')[0];
        msg = msg || 'Your browser does not support certain technologies used in this activity. To view the activity, please reopen it in a modern browser (e.g. IE9+, Chrome or Firefox).';

        msgDiv.className = 'unsupported-msg';
        msgDiv.innerHTML = msg;

        console.log(el, msgDiv);

        el.appendChild(msgDiv);
    },

    capFirstLetter: function(string) {
        return string.charAt(0).toUpperCase() + string.slice(1);
    },

    inWords: function(num) {
        var a = ['', 'one ', 'two ', 'three ', 'four ', 'five ', 'six ', 'seven ', 'eight ', 'nine ', 'ten ', 'eleven ', 'twelve ', 'thirteen ', 'fourteen ', 'fifteen ', 'sixteen ', 'seventeen ', 'eighteen ', 'nineteen '];
        var b = ['', '', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety'];
        var c = ['thousand', 'million', ''];
        var words = '';

        num = num.toString();

        if (num.length > 9) {
            return ''; // Number is larger than what function can deal with
        }

        //num = ('000000000' + num).substr(-9); // Make number into a predictable nine character string
        num = ('000000000' + num).slice(-9); // Make number into a predictable nine character string
        num = num.match(/.{3}/g); // Split string into chunks of three numbers then reverse order of returned array

        for (var i = 0; i < c.length; i++) {
            var n = num[i];
            var str = '';

            str += (words !== '') ? ' ' + c[i] + ' ' : '';
            str += (parseInt(n[0]) !== 0) ? (a[Number(n[0])] + 'hundred ') : '';
            n = n.substr(1);
            str += (parseInt(n) !== 0) ? (( str !== '' ) ? 'and ' : '') + (a[Number(n)] || b[n[0]] + ' ' + a[n[1]]) : '';
            words += str;
        }

        return words;
    },

    isObject: function(obj) {
        return obj === Object(obj);
    }
};