/**
* Common utility functions.
*/
(function(context,$) {

    "use strict";

    context.JK = context.JK || {};
    var logger = context.JK.logger;

    context.JK.stringToBool = function(s) {
        switch(s.toLowerCase()){
            case "true": case "yes": case "1": return true;
            case "false": case "no": case "0": case null: return false;
            default: return Boolean(s);
        }
    };

    // Build up two maps of images, for each instrument id.
    // This map is a simple base map of instrument id to the basic image name.
    // Below, a loop goes through this and builds two size-specific maps.
    // In the future, we should test Whether having a single larger image
    // available, and allowing the browser to resize offers better quality.
    var icon_map_base = {
        "accordion":"accordion",
        "acoustic guitar":"acoustic",
        "banjo":"banjo",
        "bass guitar":"bass",
        "cello":"cello",
        "clarinet":"clarinet",
        "computer":"computer",
        "default":"default",
        "drums":"drums",
        "electric guitar":"guitar",
        "euphonium":"euphonium",
        "flute":"flute",
        "french horn":"frenchhorn",
        "harmonica":"harmonica",
        "keyboard":"keyboard",
        "mandolin":"mandolin",
        "oboe":"oboe",
        "other":"other",
        "saxophone":"saxophone",
        "trombone":"trombone",
        "trumpet":"trumpet",
        "tuba":"tuba",
        "ukulele":"ukelele",
        "viola":"viola",
        "violin":"violin",
        "voice":"vocals"
    };

    var instrumentIconMap24 = {};
    var instrumentIconMap45 = {};

    $.each(context._.keys(icon_map_base), function(index, instrumentId) {
        var icon = icon_map_base[instrumentId];
        instrumentIconMap24[instrumentId] = "../assets/content/icon_instrument_" + icon + "24.png";
        instrumentIconMap45[instrumentId] = "../assets/content/icon_instrument_" + icon + "45.png";
    });

    // Uber-simple templating
    // var template = "Hey {name}";
    // var vals = { name: "Jon" };
    // _fillTemplate(template, vals);
    // --> "Hey Jon"
    context.JK.fillTemplate = function(template, vals) {
        for(var val in vals)
            template=template.replace(new RegExp('{'+val+'}','g'), vals[val]);
        return template;
    };

    context.JK.resolveAvatarUrl = function(photo_url) {
        return photo_url ? photo_url : "/assets/shared/avatar_generic.png";
    };

    context.JK.getInstrumentIconMap24 = function() {
        return instrumentIconMap24;
    };

    context.JK.getInstrumentIconMap45 = function() {
        return instrumentIconMap45;
    };

    context.JK.getInstrumentIcon24 = function(instrument) {
        if (instrument in instrumentIconMap24) {
            return instrumentIconMap24[instrument];
        }

        return instrumentIconMap24["default"];
    };

    context.JK.getInstrumentIcon45 = function(instrument) {
        if (instrument in instrumentIconMap45) {
            return instrumentIconMap45[instrument];
        }

        return instrumentIconMap45["default"];
    };

    context.JK.listInstruments = function(app, callback) {
        var url = "/api/instruments";
        $.ajax({
            type: "GET",
            dataType: "json",
            url: url,
            success: function(response) {
                callback(response);
            },
            error: app.ajaxError
        });
    };

    context.JK.showErrorDialog = function(app, msg, title) {
        app.layout.showDialog('error-dialog');
        $('#error-msg', 'div[layout-id="error-dialog"]').html(msg);
        $('#error-summary', 'div[layout-id="error-dialog"]').html(title);
    }

    // TODO: figure out how to handle this in layout.js for layered popups
    context.JK.showOverlay = function() {
        $('.dialog-overlay').show();
    }

    context.JK.hideOverlay = function() {
        $('.dialog-overlay').hide();
    }

    /*
     * Loads a listbox or dropdown with the values in input_array, setting the option value
     * to the id_field and the option text to text_field. It will preselect the option with
     * value equal to selected_id.
     */
    context.JK.loadOptions = function(templateHtml, listbox_id, input_array, id_field, text_field, selected_id) {
        $.each(input_array, function(index, val) {
            var isSelected = "";
            if (val[id_field] === selected_id) {
                isSelected = "selected";
            }
            var html = context.JK.fillTemplate(templateHtml, {
                value: val[id_field],
                label: val[text_field],
                selected: isSelected
            });

            listbox_id.append(html);
        });
    }

    context.JK.trimString  = function(str) {
        return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
    };

    context.JK.padString = function(str, max) {
        var retVal = '' + str;
        while (retVal.length < max) {
            retVal = '0' + retVal;
        }

        return retVal;
    }

    context.JK.formatDate = function(dateString) {
        var date = new Date(dateString);
        return date.getFullYear() + "-" + context.JK.padString(date.getMonth()+1, 2) + "-" + context.JK.padString(date.getDate(), 2) + " @ " + date.toLocaleTimeString();
    }

    context.JK.search = function(query, app, callback) {
        $.ajax({
            type: "GET",
            dataType: "json",
            contentType: 'application/json',
            url: "/api/search?query=" + query,
            processData: false,
            success: function(response) {
                callback(response);
            },
            error: app.ajaxError
        });
    };

    context.JK.sendFriendRequest = function(app, userId, callback) {
        var url = "/api/users/" + context.JK.currentUserId + "/friend_requests";
        $.ajax({
            type: "POST",
            dataType: "json",
            contentType: 'application/json',
            url: url,
            data: '{"friend_id":"' + userId + '"}',
            processData: false,
            success: function(response) {
                callback(userId);
                context.JK.GA.trackFriendConnect(context.JK.GA.FriendConnectTypes.request);
            },
            error: app.ajaxError
        });
    };

    /*
    * Get the length of a dictionary
    */
    context.JK.dlen = function(d) {
        var count = 0;
        for (var i in d) {
            if (d.hasOwnProperty(i)) {
                count++;
            }
        }
        return count;
    };

    /**
     * Finds the first error associated with the field.
     * @param fieldName the name of the field
     * @param errors_data response from a 422 response in ajax
     * @returns null if no error for the field name, or the 1st error associated with that field
     */
    context.JK.get_first_error = function(fieldName, errors_data) {
        var errors = errors_data["errors"];
        if(errors == null) return null;

        if(errors[fieldName] && errors[fieldName].length > 0) {
            return errors[fieldName][0]
        }
        else {
            return null;
        }
    }

    /**
     * Returns a ul with an li per field name.
     * @param fieldName the name of the field
     * @param errors_data error data return by a 422 ajax response
     * @returns null if no error for the field name; otherwise a ul/li
     */
    context.JK.format_errors = function(fieldName, errors_data) {
        var errors = errors_data["errors"];
        if(errors == null) return null;

        var field_errors = errors[fieldName];
        if(field_errors == null) {
            return null;
        }

        var ul = $('<ul class="error-text"></ul>')

        $.each(field_errors, function(index, item) {
            ul.append(context.JK.fillTemplate("<li>{message}</li>", {message: item}))
        })

        return ul;
    }


    /**
    * Way to verify that a number of parallel tasks have all completed.
    * Provide a function to evaluate completion, and a callback to
    * invoke when that function evaluates to true.
    * NOTE: this does not pause execution, it simply ensures that
    * when the test function evaluates to true, the callback will
    * be invoked.
    */
    context.JK.joinCalls = function(completionTestFunction, callback, interval) {
        function doneYet() {
            if (completionTestFunction()) {
                callback();
            } else {
                context.setTimeout(doneYet, interval);
            }
        }
        doneYet();
    };

    /**
     * Returns 'MacOSX' if the os appears to be macintosh,
     * 'Win32' if the os appears to be windows,
     * 'Unix' if the OS appears to be linux,
     * and null if unknown
     * @returns {*}
     */
    context.JK.detectOS = function() {
        if(!navigator.platform) { return null; }

        if (navigator.platform.toLowerCase().indexOf( 'mac' ) !== -1) {
            return "MacOSX";
        }
        if (navigator.platform.toLowerCase().indexOf( 'win' ) !== -1 ) {
            return "Win32"
        }
        if (navigator.platform.toLowerCase().indexOf('linux') !== -1 ) {
            return "Unix"
        }

        return null;
    }

    context.JK.popExternalLinks = function() {
        // Allow any a link with a rel="external" attribute to launch
        // the link in the default browser, using jamClient:
        $('a[rel="external"]').click(function(evt) {
            evt.preventDefault();
            var href = $(this).attr("href");
            if (href) {
                // make absolute if not already
                if(href.indexOf('http') != 0 && href.indexOf('mailto') != 0) {
                    href = window.location.protocol + '//' + window.location.host + href;
                }

                context.jamClient.OpenSystemBrowser(href);
            }
            return false;
        });
    }

    /*
     * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
     * Digest Algorithm, as defined in RFC 1321.
     * Copyright (C) Paul Johnston 1999 - 2000.
     * Updated by Greg Holt 2000 - 2001.
     * See http://pajhome.org.uk/site/legal.html for details.
     */

    /*
     * Convert a 32-bit number to a hex string with ls-byte first
     */
    var hex_chr = "0123456789abcdef";
    function rhex(num) {
        var str, j;
        str = "";
        for(j=0; j<=3; j++) {
            str += hex_chr.charAt((num >> (j * 8 + 4)) & 0x0F) +
            hex_chr.charAt((num >> (j * 8)) & 0x0F);
        }
        return str;
    }

    /*
     * Convert a string to a sequence of 16-word blocks, stored as an array.
     * Append padding bits and the length, as described in the MD5 standard.
     */
    function str2blks_MD5(str) {
        var nblk, blks, i;
        nblk = ((str.length + 8) >> 6) + 1;
        blks = new Array(nblk * 16);
        for(i = 0; i < nblk * 16; i++) blks[i] = 0;
            for(i = 0; i < str.length; i++)
                blks[i >> 2] |= str.charCodeAt(i) << ((i % 4) * 8);
        blks[i >> 2] |= 0x80 << ((i % 4) * 8);
        blks[nblk * 16 - 2] = str.length * 8;
        return blks;
    }

    /*
     * Add integers, wrapping at 2^32. This uses 16-bit operations internally
     * to work around bugs in some JS interpreters.
     */
    function add(x, y)
    {
      var lsw = (x & 0xFFFF) + (y & 0xFFFF);
      var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
      return (msw << 16) | (lsw & 0xFFFF);
    }

    /*
     * Bitwise rotate a 32-bit number to the left
     */
    function rol(num, cnt)
    {
      return (num << cnt) | (num >>> (32 - cnt));
    }

    /*
     * These functions implement the basic operation for each round of the
     * algorithm.
     */
    function cmn(q, a, b, x, s, t)
    {
      return add(rol(add(add(a, q), add(x, t)), s), b);
    }
    function ff(a, b, c, d, x, s, t)
    {
      return cmn((b & c) | ((~b) & d), a, b, x, s, t);
    }
    function gg(a, b, c, d, x, s, t)
    {
      return cmn((b & d) | (c & (~d)), a, b, x, s, t);
    }
    function hh(a, b, c, d, x, s, t)
    {
      return cmn(b ^ c ^ d, a, b, x, s, t);
    }
    function ii(a, b, c, d, x, s, t)
    {
      return cmn(c ^ (b | (~d)), a, b, x, s, t);
    }

    /*
     * Take a string and return the hex representation of its MD5.
     */
    context.JK.calcMD5 = function(str) {
        var x, a, b, c, d, i,
            olda, oldb, oldc, oldd;
        x = str2blks_MD5(str);
        a =  1732584193;
        b = -271733879;
        c = -1732584194;
        d =  271733878;

        for(i = 0; i < x.length; i += 16)
            {
                olda = a;
                oldb = b;
                oldc = c;
                oldd = d;

                a = ff(a, b, c, d, x[i+ 0], 7 , -680876936);
                d = ff(d, a, b, c, x[i+ 1], 12, -389564586);
                c = ff(c, d, a, b, x[i+ 2], 17,  606105819);
                b = ff(b, c, d, a, x[i+ 3], 22, -1044525330);
                a = ff(a, b, c, d, x[i+ 4], 7 , -176418897);
                d = ff(d, a, b, c, x[i+ 5], 12,  1200080426);
                c = ff(c, d, a, b, x[i+ 6], 17, -1473231341);
                b = ff(b, c, d, a, x[i+ 7], 22, -45705983);
                a = ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
                d = ff(d, a, b, c, x[i+ 9], 12, -1958414417);
                c = ff(c, d, a, b, x[i+10], 17, -42063);
                b = ff(b, c, d, a, x[i+11], 22, -1990404162);
                a = ff(a, b, c, d, x[i+12], 7 ,  1804603682);
                d = ff(d, a, b, c, x[i+13], 12, -40341101);
                c = ff(c, d, a, b, x[i+14], 17, -1502002290);
                b = ff(b, c, d, a, x[i+15], 22,  1236535329);

                a = gg(a, b, c, d, x[i+ 1], 5 , -165796510);
                d = gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
                c = gg(c, d, a, b, x[i+11], 14,  643717713);
                b = gg(b, c, d, a, x[i+ 0], 20, -373897302);
                a = gg(a, b, c, d, x[i+ 5], 5 , -701558691);
                d = gg(d, a, b, c, x[i+10], 9 ,  38016083);
                c = gg(c, d, a, b, x[i+15], 14, -660478335);
                b = gg(b, c, d, a, x[i+ 4], 20, -405537848);
                a = gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
                d = gg(d, a, b, c, x[i+14], 9 , -1019803690);
                c = gg(c, d, a, b, x[i+ 3], 14, -187363961);
                b = gg(b, c, d, a, x[i+ 8], 20,  1163531501);
                a = gg(a, b, c, d, x[i+13], 5 , -1444681467);
                d = gg(d, a, b, c, x[i+ 2], 9 , -51403784);
                c = gg(c, d, a, b, x[i+ 7], 14,  1735328473);
                b = gg(b, c, d, a, x[i+12], 20, -1926607734);

                a = hh(a, b, c, d, x[i+ 5], 4 , -378558);
                d = hh(d, a, b, c, x[i+ 8], 11, -2022574463);
                c = hh(c, d, a, b, x[i+11], 16,  1839030562);
                b = hh(b, c, d, a, x[i+14], 23, -35309556);
                a = hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
                d = hh(d, a, b, c, x[i+ 4], 11,  1272893353);
                c = hh(c, d, a, b, x[i+ 7], 16, -155497632);
                b = hh(b, c, d, a, x[i+10], 23, -1094730640);
                a = hh(a, b, c, d, x[i+13], 4 ,  681279174);
                d = hh(d, a, b, c, x[i+ 0], 11, -358537222);
                c = hh(c, d, a, b, x[i+ 3], 16, -722521979);
                b = hh(b, c, d, a, x[i+ 6], 23,  76029189);
                a = hh(a, b, c, d, x[i+ 9], 4 , -640364487);
                d = hh(d, a, b, c, x[i+12], 11, -421815835);
                c = hh(c, d, a, b, x[i+15], 16,  530742520);
                b = hh(b, c, d, a, x[i+ 2], 23, -995338651);

                a = ii(a, b, c, d, x[i+ 0], 6 , -198630844);
                d = ii(d, a, b, c, x[i+ 7], 10,  1126891415);
                c = ii(c, d, a, b, x[i+14], 15, -1416354905);
                b = ii(b, c, d, a, x[i+ 5], 21, -57434055);
                a = ii(a, b, c, d, x[i+12], 6 ,  1700485571);
                d = ii(d, a, b, c, x[i+ 3], 10, -1894986606);
                c = ii(c, d, a, b, x[i+10], 15, -1051523);
                b = ii(b, c, d, a, x[i+ 1], 21, -2054922799);
                a = ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
                d = ii(d, a, b, c, x[i+15], 10, -30611744);
                c = ii(c, d, a, b, x[i+ 6], 15, -1560198380);
                b = ii(b, c, d, a, x[i+13], 21,  1309151649);
                a = ii(a, b, c, d, x[i+ 4], 6 , -145523070);
                d = ii(d, a, b, c, x[i+11], 10, -1120210379);
                c = ii(c, d, a, b, x[i+ 2], 15,  718787259);
                b = ii(b, c, d, a, x[i+ 9], 21, -343485551);

                a = add(a, olda);
                b = add(b, oldb);
                c = add(c, oldc);
                d = add(d, oldd);
            }
            return rhex(a) + rhex(b) + rhex(c) + rhex(d);
    };

 })(window,jQuery);