// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD 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 widget_1 = require('./widget');
var widget_box_1 = require('./widget_box');
var phosphor_tabs_1 = require('phosphor-tabs');
var phosphor_widget_1 = require('phosphor-widget');
var _ = require('underscore');
var utils = require('./utils');
var SelectionContainerModel = (function (_super) {
    __extends(SelectionContainerModel, _super);
    function SelectionContainerModel() {
        _super.apply(this, arguments);
    }
    SelectionContainerModel.prototype.defaults = function () {
        return _.extend(_super.prototype.defaults.call(this), {
            _model_name: 'SelectionContainerModel',
            selected_index: 0,
            _titles: {}
        });
    };
    return SelectionContainerModel;
}(widget_box_1.BoxModel));
exports.SelectionContainerModel = SelectionContainerModel;
var AccordionModel = (function (_super) {
    __extends(AccordionModel, _super);
    function AccordionModel() {
        _super.apply(this, arguments);
    }
    AccordionModel.prototype.defaults = function () {
        return _.extend(_super.prototype.defaults.call(this), {
            _model_name: 'AccordionModel',
            _view_name: 'AccordionView'
        });
    };
    return AccordionModel;
}(SelectionContainerModel));
exports.AccordionModel = AccordionModel;
var AccordionView = (function (_super) {
    __extends(AccordionView, _super);
    function AccordionView() {
        _super.apply(this, arguments);
    }
    AccordionView.prototype.initialize = function (parameters) {
        _super.prototype.initialize.call(this, parameters);
        this.containers = [];
        this.model_containers = {};
        this.children_views = new widget_1.ViewList(this.add_child_view, this.remove_child_view, this);
        this.listenTo(this.model, 'change:children', function (model, value, options) {
            this.children_views.update(value);
        });
    };
    /**
     * Called when view is rendered.
     */
    AccordionView.prototype.render = function () {
        var guid = 'panel-group' + utils.uuid();
        this.el.id = guid;
        this.el.classList.add('jupyter-widgets');
        this.el.classList.add('panel-group');
        this.listenTo(this.model, 'change:selected_index', function (model, value, options) {
            this.update_selected_index(options);
        });
        this.listenTo(this.model, 'change:_titles', function (model, value, options) {
            this.update_titles(options);
        });
        this.on('displayed', function () {
            this.update_titles();
        });
        this.children_views.update(this.model.get('children'));
    };
    /**
     * Set tab titles
     */
    AccordionView.prototype.update_titles = function () {
        var titles = this.model.get('_titles');
        var that = this;
        _.each(titles, function (title, page_index) {
            var accordion = that.containers[page_index];
            if (accordion !== undefined) {
                accordion
                    .children('.panel-heading')
                    .find('.accordion-toggle')
                    .text(title);
            }
        });
    };
    /**
     * Only update the selection if the selection wasn't triggered
     * by the front-end.  It must be triggered by the back-end.
     */
    AccordionView.prototype.update_selected_index = function (options) {
        if (options === undefined || options.updated_view != this) {
            var old_index = this.model.previous('selected_index');
            var new_index = this.model.get('selected_index');
            /* old_index can be out of bounds, this check avoids raising
               a javascript error. */
            if (0 <= old_index && old_index < this.containers.length) {
                this.collapseTab(old_index);
            }
            if (0 <= new_index && new_index < this.containers.length) {
                this.expandTab(new_index);
            }
        }
    };
    /**
     * Collapses an accordion tab.
     * @param  {number} index
     */
    AccordionView.prototype.collapseTab = function (index) {
        var page = this.containers[index].children('.collapse');
        if (page.hasClass('in')) {
            page.removeClass('in');
            page.collapse('hide');
        }
    };
    /**
     * Expands an accordion tab.
     * @param  {number} index
     */
    AccordionView.prototype.expandTab = function (index) {
        var page = this.containers[index].children('.collapse');
        if (!page.hasClass('in')) {
            page.addClass('in');
            page.collapse('show');
        }
    };
    /**
     * Called when a child is removed from children list.
     * TODO: does this handle two different views of the same model as children?
     */
    AccordionView.prototype.remove_child_view = function (view) {
        var model = view.model;
        var accordion_group = this.model_containers[model.id];
        this.containers.splice(accordion_group['container_index'], 1);
        delete this.model_containers[model.id];
        accordion_group.remove();
    };
    /**
     * Called when a child is added to children list.
     */
    AccordionView.prototype.add_child_view = function (model) {
        var index = this.containers.length;
        var uuid = utils.uuid();
        var accordion_group = document.createElement('div');
        accordion_group.className = 'panel panel-default';
        this.el.appendChild(accordion_group);
        var accordion_heading = document.createElement('div');
        accordion_heading.classList.add('panel-heading');
        accordion_group.appendChild(accordion_heading);
        var that = this;
        var accordion_toggle = document.createElement('a');
        accordion_toggle.classList.add('accordion-toggle');
        accordion_toggle.setAttribute('data-toggle', 'collapse');
        accordion_toggle.setAttribute('data-parent', '#' + this.el.id);
        accordion_toggle.setAttribute('href', '#' + uuid);
        accordion_toggle.onclick = function () {
            that.model.set('selected_index', index, { updated_view: that });
            that.touch();
        };
        accordion_toggle.textContent = "Page " + index;
        accordion_heading.appendChild(accordion_toggle);
        var accordion_body = document.createElement('div');
        accordion_body.id = uuid;
        accordion_body.className = 'panel-collapse collapse';
        accordion_group.appendChild(accordion_body);
        var accordion_inner = document.createElement('div');
        accordion_inner.classList.add('panel-body');
        accordion_body.appendChild(accordion_inner);
        var container_index = this.containers.push(accordion_group) - 1;
        // TODO: Fix container_index to be an attached property or something that we keep track of, rather than an attribute directly on the node.
        accordion_group['container_index'] = container_index;
        this.model_containers[model.id] = accordion_group;
        var dummy = document.createElement('div');
        accordion_inner.appendChild(dummy);
        return this.create_child_view(model).then(function (view) {
            dummy.parentNode.replaceChild(dummy, view.el);
            that.update_selected_index();
            that.update_titles();
            // Trigger the displayed event of the child view.
            that.displayed.then(function () {
                view.trigger('displayed', that);
            });
            return view;
        }).catch(utils.reject('Could not add child view to box', true));
    };
    /**
     * We remove this widget before removing the children as an optimization
     * we want to remove the entire container from the DOM first before
     * removing each individual child separately.
     */
    AccordionView.prototype.remove = function () {
        _super.prototype.remove.call(this);
        this.children_views.remove();
    };
    return AccordionView;
}(widget_1.DOMWidgetView));
exports.AccordionView = AccordionView;
var TabModel = (function (_super) {
    __extends(TabModel, _super);
    function TabModel() {
        _super.apply(this, arguments);
    }
    TabModel.prototype.defaults = function () {
        return _.extend(_super.prototype.defaults.call(this), {
            _model_name: 'TabModel',
            _view_name: 'TabView'
        });
    };
    return TabModel;
}(SelectionContainerModel));
exports.TabModel = TabModel;
var TabView = (function (_super) {
    __extends(TabView, _super);
    function TabView() {
        _super.apply(this, arguments);
    }
    /**
     * Public constructor.
     */
    TabView.prototype.initialize = function (parameters) {
        _super.prototype.initialize.call(this, parameters);
        this.childrenViews = new widget_1.ViewList(this.addChildView, this.removeChildView, this);
        this.listenTo(this.model, 'change:children', function (model, value) { this.childrenViews.update(value); });
        this.listenTo(this.model, 'change:_titles', function (model, value, options) { this.updateTitles(options); });
    };
    /**
     * Called when view is rendered.
     */
    TabView.prototype.render = function () {
        var parent = this;
        this.el.classList.add('jupyter-widgets');
        this.el.classList.add('widget-tab');
        this.tabBar = new phosphor_tabs_1.TabBar();
        this.tabBar.tabsMovable = false;
        this.tabBar.addClass('widget-tab-bar');
        this.tabBar.currentChanged.connect(this._onTabChanged, this);
        this.tabBar.tabCloseRequested.connect(this._onTabCloseRequested, this);
        this.tabContents = document.createElement('div');
        this.tabContents.className = 'widget-tab-contents';
        this.childrenViews.update(this.model.get('children'));
        this.displayed.then(function () {
            parent.tabBar.attach(parent.el);
            parent.el.appendChild(parent.tabContents);
        });
    };
    /**
     * Called when a child is added to children list.
     */
    TabView.prototype.addChildView = function (model) {
        var parent = this;
        return this.create_child_view(model).then(function (child) {
            var current = parent.el.querySelector('.mod-active');
            if (current) {
                current.classList.remove('mod-active');
            }
            child.el.classList.add('widget-tab-child');
            child.el.classList.add('mod-active');
            parent.tabContents.appendChild(child.el);
            parent.tabBar.addItem({
                title: new phosphor_widget_1.Title({ text: '', closable: true })
            });
            var tab = parent.tabBar.itemAt(parent.tabBar.itemCount() - 1);
            parent.displayed.then(function () {
                child.trigger('displayed', parent);
                parent.update();
            });
            child.on('remove', function () { parent.tabBar.removeItem(tab); });
            return child;
        }).catch(utils.reject('Could not add child view to box', true));
    };
    TabView.prototype.removeChildView = function (child) { child.remove(); };
    /**
     * Update the contents of this view
     *
     * Called when the model is changed.  The model may have been
     * changed by another view or by a state update from the back-end.
     */
    TabView.prototype.update = function (options) {
        this.updateTitles();
        this.updateSelectedIndex(options);
        return _super.prototype.update.call(this);
    };
    /**
     * Updates the tab page titles.
     */
    TabView.prototype.updateTitles = function () {
        var titles = this.model.get('_titles') || {};
        for (var i = this.tabBar.itemCount() - 1; i > -1; i--) {
            this.tabBar.itemAt(i).title.text = titles[i] || (i + 1) + '';
        }
    };
    /**
     * Updates the selected index.
     */
    TabView.prototype.updateSelectedIndex = function (options) {
        if (options === undefined || options.updated_view !== this) {
            var index = this.model.get('selected_index');
            if (typeof index === 'undefined') {
                index = 0;
            }
            if (0 <= index && index < this.tabBar.itemCount()) {
                this.selectPage(index);
            }
        }
    };
    /**
     * Select a page.
     */
    TabView.prototype.selectPage = function (index) {
        var actives = this.el.querySelectorAll('.mod-active');
        if (actives.length) {
            for (var i = 0, len = actives.length; i < len; i++) {
                actives[i].classList.remove('mod-active');
            }
        }
        var active = this.el.querySelectorAll('.widget-tab-child')[index];
        if (active) {
            active.classList.add('mod-active');
        }
    };
    TabView.prototype.remove = function () {
        /*
         * The tab bar needs to be disposed before its node is removed by the
         * super call, otherwise phosphor's Widget.detach will throw an error.
         */
        this.tabBar.dispose();
        /*
         * We remove this widget before removing the children as an optimization
         * we want to remove the entire container from the DOM first before
         * removing each individual child separately.
         */
        _super.prototype.remove.call(this);
        this.childrenViews.remove();
    };
    TabView.prototype._onTabChanged = function (sender, args) {
        this.model.set('selected_index', args.index, { updated_view: this });
        this.touch();
    };
    TabView.prototype._onTabCloseRequested = function (sender, args) {
        /*
         * When a tab is removed, the titles dictionary must be reset for all
         * indices that are larger than the index of the tab that was removed.
         */
        var len = this.model.get('children').length;
        var titles = this.model.get('_titles') || {};
        delete titles[args.index];
        for (var i = args.index + 1; i < len; i++) {
            titles[i - 1] = titles[i];
            delete titles[i];
        }
        var children = _.filter(this.model.get('children'), function (child, index) { return index !== args.index; });
        this.model.set({ 'children': children, '_titles': titles }, { updated_view: this });
        this.touch();
    };
    return TabView;
}(widget_1.DOMWidgetView));
exports.TabView = TabView;
