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

var Struct;
/*
 * TODO Struct of Struct
 */
Struct = Class.create( /** @lends jls.lang.Struct.prototype */
{
    /**
     * Creates a structure.
     * 
     * @param {Array} structDef The structure definition.
     * @param {jls.lang.ByteBuffer} [buffer] The buffer to use for this structure.
     * @constructs
     * @class This class represents a structure. A structure specifies the format of a record,
     * including the names and types of its members, and the order in which they are stored.
     */
    initialize : function(structDef, buffer) {
        structDef = structDef || [];
        this._struct = {};
        var position = 0;
        for (var i = 0; i < structDef.length; i++) {
            var def = structDef[i];
            if (! (def.type in Struct.TYPE_SIZE)) {
                throw new Exception('Invalid type ' + def.type);
            }
            var size = Struct.TYPE_SIZE[def.type];
            var length = ('length' in def) ? def.length : 1;
            this._struct[def.name] = {
                position: position,
                type: def.type,
                length: length
            };
            position += size * length;
            //jls.logger.warn('Struct() ' + def.name + ', ' + def.type + ', ' + size + ', ' + position);
        }
        this._size = position;
        if (buffer) {
            if (buffer.remaining() < this._size) {
                throw new Exception('Remaining buffer size too small (' + buffer.remaining() + '<' + this._size + ')');
            }
            this._buffer = buffer.slice();
        } else {
            this._buffer = ByteBuffer.allocate(this._size);
        }
    },
    /**
     * Returns the buffer.
     *
     * @returns {jls.lang.ByteBuffer} The buffer.
     */
    buffer : function() {
        return this._buffer;
    },
    clear : function() {
        this._buffer.clear();
        this._buffer.setLimit(this._size);
    },
    /**
     * Returns the size of the struct.
     *
     * @returns {Number} The size.
     */
    size : function() {
        return this._size;
    },
    has : function(name) {
        return (typeof name == 'string') && (name in this._struct);
    },
    getFieldPosition : function(name) {
        if (! (name in this._struct)) {
            throw new Exception('Invalid name ' + name);
        }
        return this._struct[name].position;
    },
    getFieldSize : function(name) {
        if (! (name in this._struct)) {
            throw new Exception('Invalid name ' + name);
        }
        var d = this._struct[name];
        return d.length * Struct.TYPE_SIZE[d.type];
    },
    /**
     * Gets a field in this structure.
     *
     * @param {String} name The field name.
     * @param {Number} [index] The index of the value to get.
     * @returns {Number|String} The field value.
     */
    get : function(name, index) {
        if (! (name in this._struct)) {
            throw new Exception('Invalid name ' + name);
        }
        var d = this._struct[name];
        this._buffer.setPosition(d.position);
        switch (d.type) {
        case 'SignedByte':
            return this._buffer.getByte(true);
        case 'UnsignedByte':
            return this._buffer.getByte(false);
        case 'SignedShort':
            return this._buffer.getShort(true);
        case 'UnsignedShort':
            return this._buffer.getShort(false);
        case 'SignedInt':
            return this._buffer.getInt(true);
        case 'UnsignedInt':
            return this._buffer.getInt(false);
        case 'SignedLong':
            return this._buffer.getLong(true);
        case 'UnsignedLong':
            return this._buffer.getLong(false);
        case 'Pointer':
            return this._buffer.getPointer();
        }
    },
    /**
     * Puts a field in this structure.
     *
     * @param {String} name The field name.
     * @param {Number|String} value The field value to set.
     * @param {Number} [index] The index of the value to get.
     * @returns {jls.lang.Struct} This structure.
     */
    put : function(name, value, index) {
        if (! (name in this._struct)) {
            throw new Exception('Invalid name ' + name);
        }
        //jls.logger.warn('Struct.put("' + name + '", ' + value + ')');
        var d = this._struct[name]
        this._buffer.setPosition(d.position);
        switch (d.type) {
        case 'SignedByte':
        case 'UnsignedByte':
            this._buffer.putByte(value);
            break;
        case 'SignedShort':
        case 'UnsignedShort':
            this._buffer.putShort(value);
            break;
        case 'SignedInt':
        case 'UnsignedInt':
            this._buffer.putInt(value);
            break;
        case 'SignedLong':
        case 'UnsignedLong':
            this._buffer.putLong(value);
            break;
        case 'Pointer':
            this._buffer.putPointer(value);
            break;
        }
        return this;
    }
});

Object.extend(Struct, /** @lends jls.lang.Struct */
{
    TYPE_SIZE : {
        'SignedByte': 1,
        'UnsignedByte': 1,
        'SignedShort': 2,
        'UnsignedShort': 2,
        'SignedInt': 4,
        'UnsignedInt': 4,
        'SignedLong': 8,
        'UnsignedLong': 8,
        'Pointer': ByteBuffer.POINTER_SIZE
    }
});


return Struct;
});
