if ( !framework.isDeclared("org.iit.data.DataStore") ) {

framework.declareClass("org.iit.data.DataStore", null,
    function(name, fields, config){
        org.iit.data.DataStore._stores[name] = this;
        this.fields = fields;
        this.records = [];
        this._index = {};
        this._identifier = [];
        for ( var i = 0, l = fields.length; i < l; i++ ) {
            if ( fields[i].primary ) {
                this._identifier.push(fields[i].name);
            }
        }
        this._loaded = false;
        this._measured = false;
        this.length = 0;
        this._adapter = null;
        this._unsaved = [];
        this._pages = [];
        this.onLoad = new framework.event.OnLoadEvent();
    },
    {
        _getPrimaryKeyValue: function(pk){
            var value = "";
            if ( pk instanceof org.iit.data.DataRecord ) {
                var record = pk;
                for ( var i = 0, l = this._identifier.length; i < l; i++ ){
                    value += (i>0?",":"")+String(record.get(this._identifier[i]));
                }
            } else if ( framework.isArray(pk) ) {
                value = pk.join(",");
            } else if ( framework.isString(pk) || framework.isNumber(pk) ) {
                value = String(pk);
            }
            return value;
        },
        getByPk: function(pk) {
            var record;
            if ( record = this._index[this._getPrimaryKeyValue(pk)] ) {
                return record;
            } else {
                return null;
            }
        },
        _add: function(record){
            if ( !this._measured ){
                throw new Error("measure first");
            }
            if ( record.store == null ) {
                var l;
                if ( record.state == org.iit.data.DataRecord.STATE_CLEAN ){
                    var pKey = this._getPrimaryKeyValue(record); 
                    if ( !this._index[pKey] ) {
                        record.store = this;
                        l = this.records.push(record);
                        record.index = l-1;
                        this._index[pKey] = record;
                        if ( !this._loaded && this.length == l ){
                            this._loaded = true;
                            this.onLoad.fire();
                        }
                    } else {
                        throw new Error("primary key constraint violation");
                    }
                } else if ( record.state == org.iit.data.DataRecord.STATE_TCLEAN || record.state == org.iit.data.DataRecord.STATE_TDIRTY ) {
                    record.store = this;
                    l = this.records.push(record);
                    record.index = l-1;
                    this.length++;
                    this._unsaved.push(record);
                }
            }
            return record;
        },
        _remove: function(record){
            if ( !this._measured ){
                throw new Error("measure first");
            }
            if ( record.store == this ) {
                this.records = this.records.slice(0, record.index).concat(this.records.slice(recrod.index+1));
                for ( var i = record.index+1, l = this.records.length; i < l; i++ ){
                    this.records[i].index--;
                }
                delete this._index[this._getPrimaryKeyValue(record)];
            } else {
                throw new Error("you cannot remove recrod from another store");
            }
        },
        _create: function(){
            var data = [];
            for ( var i = 0, l = this.fields.length; i < l; i++ ) {
                data[this.fields[i].name] = null;
            }
            var record = new org.iit.data.DataRecord(data);
            record.state = org.iit.data.DataRecord.STATE_TCLEAN;
            return record;
        },
        add: function(record) {
            return this._add(record);
        },
        create: function() {
            return this._add(this._create());
        },
        createFromArray: function(dataArray) {
            var data = [];
            for ( var i = 0, l = this.fields.length; i < l; i++ ) {
                data[this.fields[i].name] = dataArray[this.fields[i].name];
            }
            var record = new org.iit.data.DataRecord(data);
            record.state = org.iit.data.DataRecord.STATE_TCLEAN;
            return record;
        },
        _search: function(criteria){
            var result = [];
            for ( var i = 0, l = this.records.length; i < l; i++ ){
                if ( criteria.test(this.records[i]) ){
                    result.push(this.records[i]);
                }
            }
            return result;
        },
        search: function(criteria){
            var dfd = new framework.Deferred(null);
            if ( this._loaded ) {
                dfd.callback(this._search(criteria));
            } else {
                if ( this._adapter.supportSearch ) {
                    if ( this._measured ) {
                        this._adapter.searchAsync(criteria).addCallback(framework.runInScope(this, function(res) {
                            dfd.callback(res);
                        }));
                    } else {
                        this._adapter.getCountAsync().addCallback(framework.runInScope(this, function(res) {
                            this._adapter.searchAsync(criteria).addCallback(framework.runInScope(this, function(res) {
                                dfd.callback(res);
                            }));
                        }));
                    }
                } else {
                    this.load().addCallback(framework.runInScope(this, function(res) {
                        dfd.callback(this._search(criteria));
                    }));
                }
            }
            return dfd;
        },
        measure: function(count) {
            this.records = [];
            this._index = {};
            this._loaded = false;
            this._measured = false;
            this.length = 0;
            this._unsaved = [];
            var dfd = new framework.Deferred(null);

            if ( this._adapter ) {
                this._adapter.getCountAsync().addCallback(framework.runInScope(this, function(res) {
                    this.setLength(res);
                    dfd.callback(this.length);
                }));
            } else {
                throw new Error("adapter is not set");
            }
            return dfd;
        },
        setLength: function(length) {
            this.records = [];
            this._index = {};
            this._unsaved = [];
            this.length = length;
            this._measured = true;
            if ( this.length == 0 ) {
                this._loaded = true;
                this.onLoad.fire();
            }
        },
        isMeasured: function(){
            return this._measured;
        },
        isLoaded: function(){
            return this._loaded;
        },
        load: function(page){
            if ( this._adapter ) {
                if ( this._measured ) {
                    return this._adapter.loadAsync(page);
                } else {
                    var dfd = new framework.Deferred(null);
                    if ( this._adapter.supportPagination && page ) {
                        this._adapter.getCountAsync().addCallback(framework.runInScope(this, function(res) {
                            this.setLength(res);
                            this._adapter.loadAsync(page).addCallback(framework.runInScope(this, function(res) {
                                dfd.callback(res);
                            }));
                        }));
                    } else {
                        this._adapter.loadAsync().addCallback(framework.runInScope(this, function(res) {
                            dfd.callback(res);
                        }));
                    }
                    return dfd;
                }
            } else {
                throw new Error("adapter is not set");
            }
        },
        setAdapter: function(adapter) {
            adapter.store = this;
            return this._adapter = adapter;
        },
        getAdapter: function() {
            return this._adapter;
        },
        save: function(){
            throw new Error("not implemented");
        },
        limit: function(offset, length){
            var result = [], i, l = offset + length > this.records.length ? this.records.length : offset + length;
            for ( i = offset; i < l; i++ ) {
                result.push(this.records[i]);
            }
            return result;
        },
        getPage: function(page, recordsPerPage) {
            var rpp = recordsPerPage||this._adapter.config.recordsPerPage;
            var resultSet = this.limit((page-1)*rpp, rpp);
            resultSet.haveToPaginate = rpp<this.records.length?true:false;
            resultSet.currentPage = page;
            resultSet.lastPage = Math.floor(this.records.length/rpp);
            return resultSet;
        }
    },
    {
        _stores: {},
        get: function(name) {
            return org.iit.data.DataStore._stores[name];
        }
    }
);

framework.declareClass("org.iit.data.DataStoreField", null,
    function(name, type, primary) {
        this.name = String(name);
        this.type = type||"string";
        this.primary = arguments.length == 3?Boolean(primary):false;
    },
    {
    }
);

framework.declareClass("org.iit.data.DataRecord", null,
    function(data) {
        this.data = data;
        this.state = org.iit.data.DataRecord.STATE_CLEAN;
        this.store = null;
        this.index = -1;
    },
    {
        set: function(name, value) {
            this.data[name] = value;
            switch (this.state) {
                case org.iit.data.DataRecord.STATE_CLEAN:
                    this.state = org.iit.data.DataRecord.STATE_DIRTY;
                    this.store._unsaved.push(this);
                    break;
                case org.iit.data.DataRecord.STATE_DIRTY:
                    this.state = org.iit.data.DataRecord.STATE_DIRTY;
                    break;
                case org.iit.data.DataRecord.STATE_TCLEAN:
                case org.iit.data.DataRecord.STATE_TDIRTY:
                    this.state = org.iit.data.DataRecord.STATE_TDIRTY;
                    break;
            }
        },
        get: function(name) {
            return this.data[name];
        },
        fromArray: function(data) {
            for ( var n in this.data ){
                if ( typeof data[n] != "undefined" ) {
                    this.data[n] = data[n];
                }
            }
        }
    },
    {
        /**
         * DataRecord is in clean state when all of its properties are loaded
         * and none of its properties are changed
         **/
        STATE_CLEAN: 1,
        /**
         * DataRecord is in transient clean state when it is created and none of its fields are modified
         */
        STATE_TCLEAN: 2,
        /**
         * DataRecord is in dirty state when its properties are changed
         */
        STATE_DIRTY: 3,
        /**
         * DataRecord is in transient dirty state when it is created
         * and some of its fields are modified but it is NOT yet persisted
         */
        STATE_TDIRTY: 4
    }
);

framework.declareClass("org.iit.Criteria", null,
    function() {
    },
    {
        test: function(record) {
        },
        toJson: function(){
        }
    }
);

framework.declareClass("org.iit.ContainsCriteria", org.iit.Criteria,
    function(fieldName, str) {
        this.fieldName = String(fieldName);
        this._str = null;
        this.setString(str);
    },
    {
        setString: function(str) {
            this._str = String(str).toLowerCase();
        },
        getString: function(str) {
            return this._str;
        },
        test: function(record) {
            if ( String(record.get(this.fieldName)).toLowerCase().indexOf(this._str) != -1 ) {
                return true;
            } else {
                return false;
            }
        },
        toJson: function(){
            return "{\"className\":\"org.iit.ContainsCriteria\",\"fieldName\":\""+this.fieldName+"\",\"value\":\""+this._str.replace(/"/g, "\\\"")+"\"}";
        }
    }
);

framework.declareClass("org.iit.BeginsWithCriteria", org.iit.Criteria,
    function(fieldName, str) {
        this.fieldName = String(fieldName);
        this._str = null;
        this._length = 0;
        this.setString(str);
    },
    {
        setString: function(str) {
            this._str = String(str).toLowerCase();
            this._length = this._str.length;
        },
        getString: function(str) {
            return this._str;
        },
        test: function(record) {
            if ( String(record.get(this.fieldName)).toLowerCase().substr(0, this._length) == this._str ) {
                return true;
            } else {
                return false;
            }
        },
        toJson: function(){
            return "{\"className\":\"org.iit.BeginsWithCriteria\",\"fieldName\":\""+this.fieldName+"\",\"value\":\""+this._str.replace(/"/g, "\\\"")+"\"}";
        }
    }
);

}

