// Derived from both https://github.com/phosphorjs/phosphor/blob/ed6c10e483a7aeb71bdd116ca870a9162d895662/src/ui/tabpanel.ts and https://github.com/phosphorjs/phosphor/blob/ed6c10e483a7aeb71bdd116ca870a9162d895662/src/collections/vector.ts, which have the following license:
"use strict";
var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var searching_1 = require('phosphor/lib/algorithm/searching');
var vector_1 = require('phosphor/lib/collections/vector');
var signaling_1 = require('phosphor/lib/core/signaling');
var VectorWithSelection = (function (_super) {
    __extends(VectorWithSelection, _super);
    function VectorWithSelection(values, options) {
        if (options === void 0) { options = {}; }
        _super.call(this, values);
        this._previousItem = null;
        this._insertBehavior = options.insertBehavior || 'select-item-if-needed';
        this._removeBehavior = options.removeBehavior || 'select-item-after';
    }
    /**
     * Add a value to the back of the vector.
     *
     * @param value - The value to add to the back of the vector.
     *
     * @returns The new length of the vector.
     *
     * #### Complexity
     * Constant.
     *
     * #### Iterator Validity
     * No changes.
     */
    VectorWithSelection.prototype.pushBack = function (value) {
        var length = _super.prototype.pushBack.call(this, value);
        this._adjustCurrentForInsert(length - 1, value);
        return length;
    };
    /**
     * Remove and return the value at the back of the vector.
     *
     * @returns The value at the back of the vector, or `undefined` if
     *   the vector is empty.
     *
     * #### Complexity
     * Constant.
     *
     * #### Iterator Validity
     * Iterators pointing at the removed value are invalidated.
     */
    VectorWithSelection.prototype.popBack = function () {
        var item = _super.prototype.popBack.call(this);
        this._adjustCurrentForRemove(this.length, item);
        return item;
    };
    /**
     * Insert a value into the vector at a specific index.
     *
     * @param index - The index at which to insert the value.
     *
     * @param value - The value to set at the specified index.
     *
     * @returns The new length of the vector.
     *
     * #### Complexity
     * Linear.
     *
     * #### Iterator Validity
     * No changes.
     *
     * #### Notes
     * The `index` will be clamped to the bounds of the vector.
     *
     * #### Undefined Behavior
     * An `index` which is non-integral.
     */
    VectorWithSelection.prototype.insert = function (index, value) {
        var length = _super.prototype.insert.call(this, index, value);
        // We have to manually clamp the index for the adjustment to preserve the
        // semantics of the insert operatin.
        var i = Math.max(0, Math.min(index, this.length));
        this._adjustCurrentForInsert(i, value);
        return length;
    };
    /**
     * Remove the first occurrence of a value from the vector.
     *
     * @param value - The value of interest.
     *
     * @returns The index of the removed value, or `-1` if the value
     *   is not contained in the vector.
     *
     * #### Complexity
     * Linear.
     *
     * #### Iterator Validity
     * Iterators pointing at the removed value and beyond are invalidated.
     *
     * #### Notes
     * Comparison is performed using strict `===` equality.
     */
    VectorWithSelection.prototype.remove = function (value) {
        var index = _super.prototype.remove.call(this, value);
        if (index !== -1) {
            this._adjustCurrentForRemove(index, value);
        }
        return index;
    };
    /**
     * Remove and return the value at a specific index.
     *
     * @param index - The index of the value of interest.
     *
     * @returns The value at the specified index, or `undefined` if the
     *   index is out of range.
     *
     * #### Complexity
     * Constant.
     *
     * #### Iterator Validity
     * Iterators pointing at the removed value and beyond are invalidated.
     *
     * #### Undefined Behavior
     * An `index` which is non-integral.
     */
    VectorWithSelection.prototype.removeAt = function (index) {
        var item = _super.prototype.removeAt.call(this, index);
        if (item !== void 0) {
            this._adjustCurrentForRemove(index, item);
        }
        return item;
    };
    /**
     * Remove all values from the vector.
     *
     * #### Complexity
     * Linear.
     *
     * #### Iterator Validity
     * All current iterators are invalidated.
     *
     * #### Selection
     * The current item is unselected.
     */
    VectorWithSelection.prototype.clear = function () {
        // Get the current index and item.
        var pi = this.currentIndex;
        var pt = this.currentItem;
        // Reset the current index and previous item.
        this._currentIndex = -1;
        this._previousItem = null;
        // Clear the item vector.
        _super.prototype.clear.call(this);
        // If no item was selected, there's nothing else to do.
        if (pi === -1) {
            return;
        }
        // Emit the current changed signal.
        this.currentChanged.emit({
            previousIndex: pi, previousItem: pt,
            currentIndex: -1, currentItem: null
        });
    };
    /**
     * Swap the contents of the vector with the contents of another.
     *
     * @param other - The other vector holding the contents to swap.
     *
     * #### Complexity
     * Constant.
     *
     * #### Iterator Validity
     * All current iterators remain valid, but will now point to the
     * contents of the other vector involved in the swap.
     *
     * #### Selection
     * The current index remains the same, but the current item reflects
     * the new vector.
     */
    VectorWithSelection.prototype.swap = function (other) {
        // Get the current index and item.
        var pi = this.currentIndex;
        var pt = this.currentItem;
        _super.prototype.swap.call(this, other);
        if (this.currentItem === pt) {
            return;
        }
        // Emit the current changed signal.
        this.currentChanged.emit({
            previousIndex: pi, previousItem: pt,
            currentIndex: pi, currentItem: this.currentItem
        });
    };
    Object.defineProperty(VectorWithSelection.prototype, "currentItem", {
        /**
         * Get the currently selected item.
         *
         * #### Notes
         * This will be `null` if no item is selected.
         */
        get: function () {
            var i = this._currentIndex;
            return i !== -1 ? this.at(i) : null;
        },
        /**
         * Set the currently selected item.
         *
         * #### Notes
         * If the item does not exist in the vector, the currentItem will be set to
         * `null`. This selects the first entry equal to the desired item.
         */
        set: function (value) {
            this.currentIndex = searching_1.indexOf(this, value);
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(VectorWithSelection.prototype, "currentIndex", {
        /**
         * Get the index of the currently selected item.
         *
         * #### Notes
         * This will be `-1` if no item is selected.
         */
        get: function () {
            return this._currentIndex;
        },
        /**
         * Set the index of the currently selected tab.
         *
         * @param index - The index to select.
         *
         * #### Notes
         * If the value is out of range, the index will be set to `-1`, which
         * indicates no item is selected.
         */
        set: function (index) {
            // Coerce the value to an index.
            var i = Math.floor(index);
            if (i < 0 || i >= this.length) {
                i = -1;
            }
            // Bail early if the index will not change.
            if (this._currentIndex === i) {
                return;
            }
            // Look up the previous index and item.
            var pi = this._currentIndex;
            var pt = pi === -1 ? null : this.at(pi);
            // Look up the current index and item.
            var ci = i;
            var ct = ci === -1 ? null : this.at(ci);
            // Update the current index and previous item.
            this._currentIndex = ci;
            this._previousItem = pt;
            // Emit the current changed signal.
            this.currentChanged.emit({
                previousIndex: pi, previousItem: pt,
                currentIndex: ci, currentItem: ct
            });
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(VectorWithSelection.prototype, "insertBehavior", {
        /**
         * Get the selection behavior when inserting a tab.
         */
        get: function () {
            return this._insertBehavior;
        },
        /**
         * Set the selection behavior when inserting a tab.
         */
        set: function (value) {
            this._insertBehavior = value;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(VectorWithSelection.prototype, "removeBehavior", {
        /**
         * Get the selection behavior when removing a tab.
         */
        get: function () {
            return this._removeBehavior;
        },
        /**
         * Set the selection behavior when removing a tab.
         */
        set: function (value) {
            this._removeBehavior = value;
        },
        enumerable: true,
        configurable: true
    });
    /**
     * Adjust the current index for a tab insert operation.
     *
     * @param i - The new index of the inserted item.
     * @param j - The inserted item.
     *
     * #### Notes
     * This method accounts for the tab bar's insertion behavior when adjusting
     * the current index and emitting the changed signal. This should be called
     * after the insertion.
     */
    VectorWithSelection.prototype._adjustCurrentForInsert = function (i, item) {
        // Lookup commonly used variables.
        var ct = this.currentItem;
        var ci = this._currentIndex;
        var bh = this._insertBehavior;
        // Handle the behavior where the new item is always selected,
        // or the behavior where the new item is selected if needed.
        if (bh === 'select-item' || (bh === 'select-item-if-needed' && ci === -1)) {
            this._currentIndex = i;
            this._previousItem = ct;
            this.currentChanged.emit({
                previousIndex: ci, previousItem: ct,
                currentIndex: i, currentItem: item
            });
            return;
        }
        // Otherwise, silently adjust the current index if needed.
        if (ci >= i)
            this._currentIndex++;
    };
    /**
     * Adjust the current index for move operation.
     *
     * @param i - The previous index of the item.
     * @param j - The new index of the item.
     *
     * #### Notes
     * This method will not cause the actual current item to change. It silently
     * adjusts the current index to account for the given move. This should be
     * called after the move.
     */
    VectorWithSelection.prototype._adjustCurrentForMove = function (i, j) {
        if (this._currentIndex === i) {
            this._currentIndex = j;
        }
        else if (this._currentIndex < i && this._currentIndex >= j) {
            this._currentIndex++;
        }
        else if (this._currentIndex > i && this._currentIndex <= j) {
            this._currentIndex--;
        }
    };
    /**
     * Adjust the current index for an item remove operation.
     *
     * @param i - The former index of the removed item.
     * @param item - The removed item.
     *
     * #### Notes
     * This method accounts for the remove behavior when adjusting the current
     * index and emitting the changed signal. It should be called after the item
     * is removed.
     */
    VectorWithSelection.prototype._adjustCurrentForRemove = function (i, item) {
        // Lookup commonly used variables.
        var ci = this._currentIndex;
        var bh = this._removeBehavior;
        // Silently adjust the index if the current item is not removed.
        if (ci !== i) {
            if (ci > i)
                this._currentIndex--;
            return;
        }
        // No item gets selected if the vector is empty.
        if (this.length === 0) {
            this._currentIndex = -1;
            this.currentChanged.emit({
                previousIndex: i, previousItem: item,
                currentIndex: -1, currentItem: null
            });
            return;
        }
        // Handle behavior where the next sibling item is selected.
        if (bh === 'select-item-after') {
            this._currentIndex = Math.min(i, this.length - 1);
            this.currentChanged.emit({
                previousIndex: i, previousItem: item,
                currentIndex: this._currentIndex, currentItem: this.currentItem
            });
            return;
        }
        // Handle behavior where the previous sibling item is selected.
        if (bh === 'select-item-before') {
            this._currentIndex = Math.max(0, i - 1);
            this.currentChanged.emit({
                previousIndex: i, previousItem: item,
                currentIndex: this._currentIndex, currentItem: this.currentItem
            });
            return;
        }
        // Handle behavior where the previous history item is selected.
        if (bh === 'select-previous-item') {
            if (this._previousItem) {
                this._currentIndex = searching_1.indexOf(this, this._previousItem);
                this._previousItem = null;
            }
            else {
                this._currentIndex = Math.min(i, this.length - 1);
            }
            this.currentChanged.emit({
                previousIndex: i, previousItem: item,
                currentIndex: this._currentIndex, currentItem: this.currentItem
            });
            return;
        }
        // Otherwise, no item gets selected.
        this._currentIndex = -1;
        this.currentChanged.emit({
            previousIndex: i, previousItem: item,
            currentIndex: -1, currentItem: null
        });
    };
    return VectorWithSelection;
}(vector_1.Vector));
// Define the signals for the `TabBar` class.
signaling_1.defineSignal(VectorWithSelection.prototype, 'currentChanged');
