/**
 * XBlockContainerPage is used to display Studio's container page for an xblock which has children.
 * This page allows the user to understand and manipulate the xblock and its children.
 */
define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/container",
        "js/views/xblock", "js/views/components/add_xblock", "js/views/modals/edit_xblock", "js/models/xblock_info"],
    function ($, _, gettext, BaseView, ContainerView, XBlockView, AddXBlockComponent, EditXBlockModal, XBlockInfo) {
        var XBlockContainerPage = BaseView.extend({
            // takes XBlockInfo as a model

            view: 'container_preview',

            initialize: function() {
                BaseView.prototype.initialize.call(this);
                this.xblockView = new ContainerView({
                    el: this.$('.wrapper-xblock'),
                    model: this.model,
                    view: this.view
                });
            },

            render: function(options) {
                var self = this,
                    xblockView = this.xblockView,
                    loadingElement = this.$('.ui-loading');
                loadingElement.removeClass('is-hidden');

                // Hide both blocks until we know which one to show
                xblockView.$el.addClass('is-hidden');

                if (!options || !options.refresh) {
                    // Add actions to any top level buttons, e.g. "Edit" of the container itself.
                    // Do not add the actions on "refresh" though, as the handlers are already registered.
                    self.addButtonActions(this.$el);
                }

                // Render the xblock
                xblockView.render({
                    success: function(xblock) {
                        xblockView.xblock.runtime.notify("page-shown", self);
                        xblockView.$el.removeClass('is-hidden');
                        self.renderAddXBlockComponents();
                        self.onXBlockRefresh(xblockView);
                        self.refreshTitle();
                        loadingElement.addClass('is-hidden');
                        self.delegateEvents();
                    }
                });
            },

            findXBlockElement: function(target) {
                return $(target).closest('.studio-xblock-wrapper');
            },

            getURLRoot: function() {
                return this.xblockView.model.urlRoot;
            },

            refreshTitle: function() {
                var title = this.$('.xblock-header .header-details .xblock-display-name').first().text().trim();
                this.$('.page-header-title').text(title);
                this.$('.page-header .subtitle a').last().text(title);
            },

            onXBlockRefresh: function(xblockView) {
                this.addButtonActions(xblockView.$el);
                this.xblockView.refresh();
            },

            renderAddXBlockComponents: function() {
                var self = this;
                this.$('.add-xblock-component').each(function(index, element) {
                    var component = new AddXBlockComponent({
                        el: element,
                        createComponent: _.bind(self.createComponent, self),
                        collection: self.options.templates
                    });
                    component.render();
                });
            },

            addButtonActions: function(element) {
                var self = this;
                element.find('.edit-button').click(function(event) {
                    event.preventDefault();
                    self.editComponent(self.findXBlockElement(event.target));
                });
                element.find('.duplicate-button').click(function(event) {
                    event.preventDefault();
                    self.duplicateComponent(self.findXBlockElement(event.target));
                });
                element.find('.delete-button').click(function(event) {
                    event.preventDefault();
                    self.deleteComponent(self.findXBlockElement(event.target));
                });
            },

            editComponent: function(xblockElement) {
                var self = this,
                    modal = new EditXBlockModal({ });
                modal.edit(xblockElement, this.model, {
                    refresh: function() {
                        self.refreshXBlock(xblockElement);
                    }
                });
            },

            createComponent: function(template, target) {
                // A placeholder element is created in the correct location for the new xblock
                // and then onNewXBlock will replace it with a rendering of the xblock. Note that
                // for xblocks that can't be replaced inline, the entire parent will be refreshed.
                var parentElement = this.findXBlockElement(target),
                    parentLocator = parentElement.data('locator'),
                    buttonPanel = target.closest('.add-xblock-component'),
                    listPanel = buttonPanel.prev(),
                    scrollOffset = this.getScrollOffset(buttonPanel),
                    placeholderElement = $('<div class="studio-xblock-wrapper"></div>').appendTo(listPanel),
                    requestData = _.extend(template, {
                        parent_locator: parentLocator
                    });
                return $.postJSON(this.getURLRoot() + '/', requestData,
                    _.bind(this.onNewXBlock, this, placeholderElement, scrollOffset))
                    .fail(function() {
                        // Remove the placeholder if the update failed
                        placeholderElement.remove();
                    });
            },

            duplicateComponent: function(xblockElement) {
                // A placeholder element is created in the correct location for the duplicate xblock
                // and then onNewXBlock will replace it with a rendering of the xblock. Note that
                // for xblocks that can't be replaced inline, the entire parent will be refreshed.
                var self = this,
                    parent = xblockElement.parent();
                this.runOperationShowingMessage(gettext('Duplicating&hellip;'),
                    function() {
                        var scrollOffset = self.getScrollOffset(xblockElement),
                            placeholderElement = $('<div class="studio-xblock-wrapper"></div>').insertAfter(xblockElement),
                            parentElement = self.findXBlockElement(parent),
                            requestData = {
                                duplicate_source_locator: xblockElement.data('locator'),
                                parent_locator: parentElement.data('locator')
                            };
                        return $.postJSON(self.getURLRoot() + '/', requestData,
                            _.bind(self.onNewXBlock, self, placeholderElement, scrollOffset))
                            .fail(function() {
                                // Remove the placeholder if the update failed
                                placeholderElement.remove();
                            });
                    });
            },

            deleteComponent: function(xblockElement) {
                var self = this;
                this.confirmThenRunOperation(gettext('Delete this component?'),
                    gettext('Deleting this component is permanent and cannot be undone.'),
                    gettext('Yes, delete this component'),
                    function() {
                        self.runOperationShowingMessage(gettext('Deleting&hellip;'),
                            function() {
                                return $.ajax({
                                    type: 'DELETE',
                                    url: self.getURLRoot() + "/" +
                                        xblockElement.data('locator')
                                }).success(_.bind(self.onDelete, self, xblockElement));
                            });
                    });
            },

            onDelete: function(xblockElement) {
                // get the parent so we can remove this component from its parent.
                var xblockView = this.xblockView,
                    xblock = xblockView.xblock,
                    parent = this.findXBlockElement(xblockElement.parent());
                xblockElement.remove();
                xblockView.updateChildren(parent);
                xblock.runtime.notify('deleted-child', parent.data('locator'));
            },

            onNewXBlock: function(xblockElement, scrollOffset, data) {
                this.setScrollOffset(xblockElement, scrollOffset);
                xblockElement.data('locator', data.locator);
                return this.refreshXBlock(xblockElement);
            },

            /**
             * Refreshes the specified xblock's display. If the xblock is an inline child of a
             * reorderable container then the element will be refreshed inline. If not, then the
             * parent container will be refreshed instead.
             * @param element An element representing the xblock to be refreshed.
             */
            refreshXBlock: function(element) {
                var xblockElement = this.findXBlockElement(element),
                    parentElement = xblockElement.parent(),
                    rootLocator = this.xblockView.model.id;
                if (xblockElement.length === 0 || xblockElement.data('locator') === rootLocator) {
                    this.render({refresh: true});
                } else if (parentElement.hasClass('reorderable-container')) {
                    this.refreshChildXBlock(xblockElement);
                } else {
                    this.refreshXBlock(this.findXBlockElement(parentElement));
                }
            },

            /**
             * Refresh an xblock element inline on the page, using the specified xblockInfo.
             * Note that the element is removed and replaced with the newly rendered xblock.
             * @param xblockElement The xblock element to be refreshed.
             * @returns {promise} A promise representing the complete operation.
             */
            refreshChildXBlock: function(xblockElement) {
                var self = this,
                    xblockInfo,
                    TemporaryXBlockView,
                    temporaryView;
                xblockInfo = new XBlockInfo({
                    id: xblockElement.data('locator')
                });
                // There is only one Backbone view created on the container page, which is
                // for the container xblock itself. Any child xblocks rendered inside the
                // container do not get a Backbone view. Thus, create a temporary view
                // to render the content, and then replace the original element with the result.
                TemporaryXBlockView = XBlockView.extend({
                    updateHtml: function(element, html) {
                        // Replace the element with the new HTML content, rather than adding
                        // it as child elements.
                        this.$el = $(html).replaceAll(element);
                    }
                });
                temporaryView = new TemporaryXBlockView({
                    model: xblockInfo,
                    view: 'reorderable_container_child_preview',
                    el: xblockElement
                });
                return temporaryView.render({
                    success: function() {
                        self.onXBlockRefresh(temporaryView);
                        temporaryView.unbind();  // Remove the temporary view
                    }
                });
            }
        });

        return XBlockContainerPage;
    }); // end define();
