define('jls/util/StringCodec', ['jls/lang/Class', 'jls/lang/Exception', 'jls/lang/ByteBuffer'], function (Class, Exception, ByteBuffer) {

var StringCodec;
/**
 * @namespace Provides utility classes for the jls language.
 * @name jls.util
 */

/**
 * @class This class provides various String related codecs.
 * @name jls.util.StringCodec
 */
StringCodec = Class.create({});

Object.extend(StringCodec, /** @lends jls.util.StringCodec */
{
    /**
     * Encodes a specified buffer and returns the encoded base64 string.
     *
     * @param {jls.lang.ByteBuffer} buffer The buffer to encode.
     * @param {String} [b64pad] The padding character to use.
     * @returns {String} The encoded string.
     */
    base64Encode : function(buffer, b64pad) {
        // 64 = 2^6, 6*4 = 24, 24 = 8*3
        b64pad = b64pad || '=';
        var map = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
        var s = '';
        while (buffer.remaining() > 0) {
            var r = buffer.remaining();
            var b1 = buffer.getByte();
            var b2 = buffer.remaining() > 0 ? buffer.getByte() : 0;
            var b3 = buffer.remaining() > 0 ? buffer.getByte() : 0;
            //logByte(b1, b2, b3);
            var i1 = b1 >>> 2;
            var i2 =  ((b1 & 0x03) << 4) | (b2 >>> 4);
            var i3 =  ((b2 & 0x0f) << 2) | (b3 >>> 6);
            var i4 =  b3 & 0x3f;
            //logByte(i1, i2, i3, i4);
            s += map.charAt(i1) + map.charAt(i2) + (r > 1 ? map.charAt(i3) : b64pad) + (r > 2 ? map.charAt(i4) : b64pad);
        }
        return s;
    },
    base64DecodeCharCode : function(c) {
        if ((c >= 0x41) && (c <= 0x5a)) { // A to Z
            return c - 0x41;
        } else if ((c >= 0x61) && (c <= 0x7a)) { // a to a
            return c - 0x61 + 26;
        } else if ((c >= 0x30) && (c <= 0x39)) { // 0 to 9
            return c - 0x30 + 52;
        } else if (c == 0x2b) { // +
            return 62;
        } else if (c == 0x2f) { // slash
            return 63;
        } else if (c == 0x3d) { // =
            return 0;
        } else {
            throw new Exception('Illegal base64 character code (' + c + ', 0x' + c.toString(16) + ')');
        }
    },
    /**
     * Decodes a specified base64 string and returns the decoded buffer.
     *
     * @param {String} s The string to decode.
     * @param {jls.lang.ByteBuffer} [buffer] The buffer to use.
     * @returns {jls.lang.ByteBuffer} The decoded buffer.
     */
    base64Decode : function(s, buffer) {
        var length = s.length;
        if ((length == 0) || (length % 4 != 0)) {
            throw new Exception('Invalid string length (' + length + ')');
        }
        buffer = buffer || ByteBuffer.allocate(length / 4 * 3);
        for (var i = 0; i < length; i += 4) {
            var c1 = s.charCodeAt(i);
            var c2 = s.charCodeAt(i + 1);
            var c3 = s.charCodeAt(i + 2);
            var c4 = s.charCodeAt(i + 3);
            var i1 = StringCodec.base64DecodeCharCode(c1);
            var i2 = StringCodec.base64DecodeCharCode(c2);
            var i3 = StringCodec.base64DecodeCharCode(c3);
            var i4 = StringCodec.base64DecodeCharCode(c4);
            var b1 = (i1 << 2) | (i2 >>> 4);
            var b2 = ((i2 & 0x0f) << 4) | (i3 >>> 2);
            var b3 = ((i3 & 0x03) << 6) | i4;
            buffer.putByte(b1);
            if (c3 == 0x3d) {
                break;
            }
            buffer.putByte(b2);
            if (c4 == 0x3d) {
                break;
            }
            buffer.putByte(b3);
        }
        return buffer;
    },
    /**
     * Encodes a specified buffer and returns the encoded hexadecimal string.
     *
     * @param {jls.lang.ByteBuffer} buffer The buffer to encode.
     * @param {String} [b64pad] The padding character to use.
     * @returns {String} The encoded string.
     */
    hexEncode : function(buffer, upperCase) {
        var map = upperCase ? "0123456789ABCDEF" : "0123456789abcdef";
        var s = '';
        while (buffer.remaining() > 0) {
            var b = buffer.getByte();
            s += map.charAt((b >>> 4) & 0x0f) + map.charAt(b & 0x0f);
        }
        return s;
    },
    /**
     * Decodes a specified hexadecimal string and returns the decoded buffer.
     *
     * @param {String} s The string to decode.
     * @param {jls.lang.ByteBuffer} [buffer] The buffer to use.
     * @returns {jls.lang.ByteBuffer} The decoded buffer.
     */
    hexDecode : function(s, buffer) {
        var length = s.length;
        if ((length == 0) || (length % 2 != 0)) {
            throw new Exception('Invalid string length (' + length + ')');
        }
        buffer = buffer || ByteBuffer.allocate(length / 2);
        for (var i = 0; i < length; i += 2) {
            buffer.putByte(parseInt(s.substr(i, 2), 16));
        }
        return buffer;
    }
});

return StringCodec;
});
