(function () {
    var C = typeof window === 'undefined' ? require('./Crypto').Crypto : window.Crypto;

    // Shortcuts
    var util = C.util;
    var charenc = C.charenc;
    var UTF8 = charenc.UTF8;
    var Binary = charenc.Binary;
    C.HMAC = function (hasher, message, key, options) {
        // Convert to byte arrays
        if (message.constructor == String) {
            message = UTF8.stringToBytes(message);
        }
        if (key.constructor == String) {
            key = UTF8.stringToBytes(key);
        }
        /* else, assume byte arrays already */

        // Allow arbitrary length keys
        if (key.length > hasher._blocksize * 4) {
            key = hasher(key, {
                asBytes: true
            });
        }

        // XOR keys with pad constants
        var okey = key.slice(0);
        var ikey = key.slice(0);
        for (var i = 0; i < hasher._blocksize * 4; i++) {
            okey[i] ^= 92;
            ikey[i] ^= 54;
        }
        var hmacbytes = hasher(
            okey.concat(
                hasher(ikey.concat(message), {
                    asBytes: true
                })
            ),
            {
                asBytes: true
            }
        );
        return options && options.asBytes ? hmacbytes : options && options.asString ? Binary.bytesToString(hmacbytes) : util.bytesToHex(hmacbytes);
    };
})();
