define('jls/lang/CharBuffer', [
  'jls/lang/Class',
  'jls/lang/Exception',
  'jls/lang/Buffer',
  'jls/lang/ByteBuffer',
  'jls/lang/IllegalArgumentException',
  'jls/lang/BufferOverflowException',
  'jls/lang/BufferUnderflowException'
], function (
  Class,
  Exception,
  Buffer,
  ByteBuffer,
  IllegalArgumentException,
  BufferOverflowException,
  BufferUnderflowException
) {

var CharBuffer;
/**
 * @augments jls.lang.Buffer
 * @class The buffer class provides facilities to get and put datas from/to a native string.
 * @name jls.lang.CharBuffer
 */
CharBuffer = Class.create(Buffer, /** @lends jls.lang.CharBuffer.prototype */
{
    initialize : function($super, barray, offset, length, limit, position) {
        if (! (barray instanceof _native.core.ByteArray)) {
            throw new Exception('Invalid barray argument (' + (typeof barray) + ')');
        }
        this._barray = barray;
        offset = offset || 0;
        var capacity = (typeof length != 'undefined') ? length : (this._barray.size() >>> 1) - offset;
        $super(capacity, limit, position, offset);
        this._byteOrder = CharBuffer.DEFAULT_BYTE_ORDER;
    },
    /**
     * Returns the native byte array of this buffer.
     * 
     * @returns {_native.core.ByteArray} The native byte array.
     */
    byteArray : function() {
        return this._barray;
    },
    /**
     * Free the associated byte array.
     * 
     * @returns {jls.lang.Buffer} This buffer.
     */
    free : function() {
        this._barray.free();
        return this;
    },
    /**
     * Creates a new buffer sharing the native string.
     * 
     * @returns {jls.lang.Buffer} The new buffer.
     */
    duplicate : function() {
        return new CharBuffer(this._barray, this._offset, this._capacity, this._limit, this._position);
    },
    /**
     * Creates a new buffer starting at the current position and with the remaining character.
     * 
     * @returns {jls.lang.Buffer} The new buffer.
     */
    slice : function() {
        return new CharBuffer(this._barray, this.position(), this.remaining());
    },
    /**
     * Puts a character into this buffer at the current position, and then increments the position.
     * 
     * @param {Number} c The character code to put.
     * @returns {jls.lang.Buffer} This buffer.
     */
    putChar : function(c) {
        if (this.remaining() < 1) {
            throw new BufferOverflowException();
        }
        if (typeof c == 'string') {
        	if (c.length != 1) {
                throw new Exception('Invalid character argument');
        	}
        	c = c.charCodeAt(0);
        }
        var os = this.offset() * 2;
        if (this._byteOrder == Buffer.BIG_ENDIAN) {
            this._barray.put(os, (c >>> 8) & 0xff);
            this._barray.put(os + 1, c & 0xff);
        } else {
            this._barray.put(os, c & 0xff);
            this._barray.put(os + 1, (c >>> 8) & 0xff);
        }
        this._position++;
        return this;
    },
    /**
     * Gets a character from this buffer at a specified position.
     * 
     * @param {Number} index The position of the character to read.
     * @returns {Number} The character code.
     */
    getCharAt : function(index) {
        if (index > this.limit()) {
            throw new Exception('Index out of bound');
        }
        var value;
        var os = (this.getOffset() + index) * 2;
        if (this._byteOrder == Buffer.BIG_ENDIAN) {
            value = this._barray.get(os) << 8;
            value |= this._barray.get(os + 1);
        } else {
            value = this._barray.get(os);
            value |= this._barray.get(os + 1) << 8;
        }
        return value;
    },
    /**
     * Gets a character from this buffer at the current position, and then increments the position.
     * 
     * @returns {Number} The character code.
     */
    getChar : function() {
        if (this.remaining() < 1) {
            throw new BufferUnderflowException();
        }
        var value = this.getCharAt(this.position());
        this._position++;
        return value;
    },
    putString : function(s) {
        if (this.remaining() < s.length) {
            throw new BufferOverflowException();
        }
        // TODO wrap + memcpy?
    	for (var i = 0; i < s.length; i++) {
    		this.putChar(s.charCodeAt(i));
    	}
        return this;
    },
    getString : function(length) {
        if (length) {
            if (this.remaining() < length) {
                throw new BufferUnderflowException();
            }
        } else {
            length = this.remaining();
        }
        /*var value = '';
    	while (--length >= 0) {
    		value += String.fromCharCode(this.getChar());
    	}*/
        //jls.logger.warn('getString() p: ' + this.position() + ', l: ' + length);
        var value = this._barray.getChars(this.position() * 2, length);
        this._position += length;
        return value;
    },
    putBuffer : function(buffer, length) {
		length = length || buffer.remaining();
        if (this.remaining() < length) {
            throw new BufferOverflowException();
        }
        this._barray.memcpy(this.offset() * 2, buffer.byteArray(), buffer.position() * 2, length * 2);
        this._position += length;
        buffer.incrementPosition(length);
        return this;
    },
    toNewString : function(length) {
		length = length || this.limit();
    	if (this.getOffset() != 0) {
            throw new Exception('Invalid CharBuffer (offset != 0)');
    	}
        return this._barray.toNewChars(length);
    },
    toString : function() {
        return this.getString();
    }
});

Object.extend(CharBuffer, /** @lends jls.lang.CharBuffer */
{
    /**
     * Allocates a new buffer.
     * 
     * @param {Number} capacity The capacity of the character buffer.
     * @returns {jls.lang.Buffer} The new buffer.
     */
    allocate : function(capacity, adoptable) {
        return CharBuffer.wrap(new _native.core.ByteArray((capacity + 1) * 2, adoptable));
    },
    /**
     * Wraps an existing native byte array into a new character buffer.
     * 
     * @param {_native.core.ByteArray} barray The native byte array to wrap.
     * @param {Number} offset The offset of the byte array to use for this buffer.
     * @param {Number} length The length of the buffer.
     * @returns {jls.lang.Buffer} The new buffer.
     */
    wrap : function(barray, offset, length) {
        if (typeof barray == 'string') {
            length = length || barray.length;
            //jls.logger.trace('CharBuffer.wrap("' + barray + '")');
        	barray = new _native.core.ByteArray(barray);
            //jls.logger.trace('CharBuffer.wrap(), barray.size(): ' + barray.size());
            var buffer = new CharBuffer(barray, offset, length);
            //jls.logger.trace('CharBuffer.wrap(), buffer.remaining(): ' + buffer.remaining() + ', buffer.capacity(): ' + buffer.capacity());
            return buffer;
        }
        return new CharBuffer(barray, offset, length);
    },
    // Byte order constants.
    DEFAULT_BYTE_ORDER : Buffer.LITTLE_ENDIAN
});


return CharBuffer;
});
