/**
 * @private
 */
Ext.define('Ext.util.PositionMap', {
    config: {
        minimumHeight: null
    },
 
    constructor: function(config) {
        var me = this;
 
        me.map = [];
        me.adjustments = {};
        me.offset = 0;
 
        me.initConfig(config);
    },
 
    populate: function(count, offset) {
        var me = this,
            map = me.map = me.map || [],
            minimumHeight = me.getMinimumHeight(),
            i, previousIndex, ln;
 
        me.adjustments = {
            indices: [],
            heights: {}
        };
 
        if (minimumHeight === null) {
            return;
        }
 
        offset = offset || 0;
 
        // We add 1 item to the count so that we can get the height of the bottom item
        count++;
        map.length = count;
 
        map[0] = 0;
        for (= offset + 1, ln = count - 1; i <= ln; i++) {
            previousIndex = i - 1;
            map[i] = map[previousIndex] + minimumHeight;
        }
 
        me.offset = 0;
        for (= 1, ln = count - 1; i <= ln; i++) {
            previousIndex = i - 1;
            me.offset += map[i] - map[previousIndex] - minimumHeight;
        }
    },
 
    setItemHeight: function(index, height) {
        height = Math.max(height, this.getMinimumHeight());
        if (height !== this.getItemHeight(index)) {
            var adjustments = this.adjustments;
            adjustments.indices.push(parseInt(index, 10));
            adjustments.heights[index] = height;
        }
    },
 
    update: function() {
        var me = this,
            adjustments = me.adjustments,
            indices = adjustments.indices,
            heights = adjustments.heights,
            map = me.map,
            ln = indices && indices.length,
            minimumHeight = me.getMinimumHeight(),
            difference = 0,
            i, j, height, index, nextIndex, currentHeight;
 
        if (!ln) {
            return false;
        }
 
        Ext.Array.sort(indices, function(a, b) {
            return a - b;
        });
 
        for (= 0; i < ln; i++) {
            index = indices[i];
            nextIndex = indices[+ 1] || map.length - 1;
 
            currentHeight = (map[index + 1] !== undefined) ? (map[index + 1] - map[index] + difference) : minimumHeight;
            height = heights[index];
 
            difference += height - currentHeight;
 
            for (= index + 1; j <= nextIndex; j++) {
                map[j] += difference;
            }
        }
 
        me.offset += difference;
        me.adjustments = {
            indices: [],
            heights: {}
        };
        return true;
    },
 
    getItemHeight: function(index) {
        return this.map[index + 1] - this.map[index];
    },
 
    getTotalHeight: function() {
        return ((this.map.length - 1) * this.getMinimumHeight()) + this.offset;
    },
 
    findIndex: function(pos) {
        return this.map.length ? this.binarySearch(this.map, pos) : 0;
    },
 
    binarySearch: function(sorted, value) {
        var start = 0,
            end = sorted.length;
 
        if (value < sorted[0]) {
            return 0;
        }
        if (value > sorted[end - 1]) {
            return end - 1;
        }
        while (start + 1 < end) {
            var mid = (start + end) >> 1,
                val = sorted[mid];
            if (val == value) {
                return mid;
            } else if (val < value) {
                start = mid;
            } else {
                end = mid;
            }
        }
        return start;
    }
});