/**
 * @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 (i = offset + 1, ln = count - 1; i <= ln; i++) {
            previousIndex = i - 1;
            map[i] = map[previousIndex] + minimumHeight;
        }

        me.offset = 0;
        for (i = 1, ln = count - 1; i <= ln; i++) {
            previousIndex = i - 1;
            this.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 adjustments = this.adjustments,
            indices = adjustments.indices,
            heights = adjustments.heights,
            map = this.map,
            ln = indices.length,
            minimumHeight = this.getMinimumHeight(),
            difference = 0,
            i, j, height, index, nextIndex, currentHeight;

        if (!adjustments.indices.length) {
            return false;
        }

        Ext.Array.sort(indices, function(a, b) {
            return a - b;
        });

        for (i = 0; i < ln; i++) {
            index = indices[i];
            nextIndex = indices[i + 1] || map.length - 1;

            currentHeight = (map[index + 1] !== undefined) ? (map[index + 1] - map[index] + difference) : minimumHeight;
            height = heights[index];

            difference += height - currentHeight;

            for (j = index + 1; j <= nextIndex; j++) {
                map[j] += difference;
            }
        }

        this.offset += difference;
        this.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;
    }
});
