define('jls/io/cs/UTF_8', [
  'jls/lang/Class',
  'jls/lang/Logger',
  'jls/lang/ByteBuffer',
  'jls/lang/CharBuffer',
  'jls/io/cs/Charset'
], function (
  Class,
  Logger,
  ByteBuffer,
  CharBuffer,
  Charset
) {

var UTF_8;
UTF_8 = Class.create(Charset,
{
    initialize : function($super) {
    	$super('UTF-8');
    },
    newDecoder : function() {
        return new UTF_8.Decoder(this);
    },
    newEncoder : function() {
        return new UTF_8.Encoder(this);
    }
});

// static
Charset.addCharset(new UTF_8());

UTF_8.Decoder = Class.create(
{
    initialize : function(charset) {
        this._charset = charset;
        this._averBytes = 2.0;
        this._replacement = '?'.charCodeAt(0);
    },
    decode : function(input, buffer) {
    	var output = buffer || CharBuffer.allocate(input.remaining() + 1, true);
        //jls.logger.info('UTF_8.Decoder.decode(), input.remaining(): ' + input.remaining() + ', output.remaining(): ' + output.remaining());
    	while (input.remaining() > 0) {
    		var b = input.getByte();
            if (b <= 0x7f) {
                // nothing to do
            } else if (b <= 0xdf) {
                // TODO Check that b2 starts with 10
                b2 = input.getByte() & 0x3f;
                b = ((b & 0x1f) << 6) | b2;
            } else if (b <= 0xef) {
                b2 = input.getByte() & 0x3f;
                b3 = input.getByte() & 0x3f;
                b = ((b & 0x0f) << 12) | (b2 << 6) | b3;
            } else if (b <= 0xf7) {
                b2 = input.getByte() & 0x3f;
                b3 = input.getByte() & 0x3f;
                b4 = input.getByte() & 0x3f;
                b = ((b & 0x07) << 18) | (b2 << 12) | (b3 << 6) | b4;
            } else {
                b = this._replacement;
            }
            output.putChar(b);
    	}
        return output;
    }
});

UTF_8.Encoder = Class.create(
{
    initialize : function(charset) {
        this._charset = charset;
        this._averBytes = 2.0;
        this._replacement = '?'.charCodeAt(0);
    },
    encode : function(input, buffer) {
    	var length = Math.round(input.remaining() * this._averBytes);
    	var output = buffer || ByteBuffer.allocate(length + 1);
        Logger.getInstance().debug('UTF_8.Encoder.encode(), input.remaining(): ' + input.remaining() + ', length: ' + length + ', output.remaining(): ' + output.remaining());
    	while (input.remaining() > 0) {
    		var c = input.getChar();
            if (c <= 0x007f) {
                output.putByte(c);
            } else if (c <= 0x07ff) {
                output.putByte(0xc0 | ((c >>> 6) & 0x1f));
                output.putByte(0x80 | (c & 0x3f));
            } else if (c <= 0xffff) {
                output.putByte(0xe0 | ((c >>> 12) & 0x0f));
                output.putByte(0x80 | ((c >>> 6) & 0x3f));
                output.putByte(0x80 | (c & 0x3f));
            } else if (c <= 0x1fffff) {
                output.putByte(0xf0 | ((c >>> 18) & 0x07));
                output.putByte(0x80 | ((c >>> 12) & 0x3f));
                output.putByte(0x80 | ((c >>> 6) & 0x3f));
                output.putByte(0x80 | (c & 0x3f));
    		} else {
                output.putByte(this._replacement);
    		}
    	}
        return output;
    }
});


return UTF_8;
});
