# Source: src/js/views/maps/LayerDetailsView.js

    'use strict';

    define(
      [
        'jquery',
        'underscore',
        'backbone',
        'models/maps/assets/MapAsset',
        'text!templates/maps/layer-details.html',
        // Sub-Views
        'views/maps/LayerDetailView',
        'views/maps/LayerOpacityView',
        'views/maps/LayerInfoView',
        'views/maps/LayerNavigationView',
        'views/maps/LegendView'
      ],
      function (
        $,
        _,
        Backbone,
        MapAsset,
        Template,
        // Sub-Views
        LayerDetailView,
        LayerOpacityView,
        LayerInfoView,
        LayerNavigationView,
        LegendView
      ) {

        /**
        * @class LayerDetailsView
        * @classdesc A panel with additional information about a Layer (a Map Asset like
        * imagery or vector data), plus some UI for updating the appearance of the Layer on
        * the map, such as the opacity.
        * @classcategory Views/Maps
        * @name LayerDetailsView
        * @extends Backbone.View
        * @screenshot views/maps/LayerDetailsView.png
        * @since 2.18.0
        * @constructs
        */
        var LayerDetailsView = Backbone.View.extend(
          /** @lends LayerDetailsView.prototype */{

            /**
            * The type of View this is
            * @type {string}
            */
            type: 'LayerDetailsView',

            /**
            * The HTML classes to use for this view's element
            * @type {string}
            */
            className: 'layer-details',

            /**
            * The MapAsset model that this view uses
            * @type {MapAsset}
            */
            model: undefined,

            /**
             * The primary HTML template for this view
             * @type {Underscore.template}
             */
            template: _.template(Template),

            /**
             * Classes that are used to identify the HTML elements that comprise this view.
             * @type {Object}
             * @property {string} open The class to add to the outermost HTML element for this
             * view when the layer details view is open/expanded (not hidden)
             * @property {string} toggle The element in the template that acts as a toggle to
             * close/hide the details view
             * @property {string} sections The container for all of the LayerDetailViews.
             * @property {string} label The label element for the layer that displays a title
             * in the header of the details view
             * @property {string} notification The element that holds the notification message,
             * if there is one. Inserted before all the details sections.
             * @property {string} badge The class to add to the badge element that is shown
             * when the layer has a notification message.
             */
            classes: {
              open: 'layer-details--open',
              toggle: 'layer-details__toggle',
              sections: 'layer-details__sections',
              label: 'layer-details__label',
              notification: 'layer-details__notification',
              badge: 'map-view__badge'
            },

            /**
             * Configuration for a Layer Detail section to show within this Layer Details
             * view.
             * @typedef {Object} DetailSectionOption
             * @property {string} label The name to display for this section
             * @property {Backbone.View} view Any view that will render content for the Layer
             * Detail section. This view will be passed the MapAsset model. The view should
             * display information about the MapAsset and/or allow some aspect of the
             * MapAsset's appearance to be edited - e.g. a LayerInfoView or a
             * LayerOpacityView.
             * @property {boolean} collapsible Whether or not this section should be
             * expandable and collapsible.
             * @property {boolean} showTitle Whether or not to show the title/label for this
             * section.
             * @property {boolean} hideIfError Set to true to hide this section when there is
             * an error loading the layer. Example: we should hide the opacity slider for
             * layers that are not visible on the map
             */

            /**
             * A list of sections to render within this view that give details about the
             * MapAsset, or allow editing of the MapAsset appearance. Each section will have a
             * title and its content will be collapsible.
             * @type {DetailSectionOption[]}
             */
            sections: [
              {
                label: 'Navigation',
                view: LayerNavigationView,
                collapsible: false,
                showTitle: false,
                hideIfError: true
              },
              {
                label: "Legend",
                view: LegendView,
                collapsible: false,
                showTitle: true,
                hideIfError: true
              },
              {
                label: 'Opacity',
                view: LayerOpacityView,
                collapsible: false,
                showTitle: true,
                hideIfError: true
              },
              {
                label: 'Info & Data',
                view: LayerInfoView,
                collapsible: true,
                showTitle: true,
                hideIfError: false
              }
            ],

            /**
            * Creates an object that gives the events this view will listen to and the
            * associated function to call. Each entry in the object has the format 'event
            * selector': 'function'.
            * @returns {Object}
            */
            events: function () {
              var events = {};
              // Close the layer details panel when the toggle button is clicked. Get the
              // class of the toggle button from the classes property set in this view.
              events['click .' + this.classes.toggle] = 'close'
              return events
            },

            /**
             * Whether or not the layer details view is open
             * @type {Boolean}
             */
            isOpen: false,

            /**
            * Executed when a new LayerDetailsView is created
            * @param {Object} [options] - A literal object with options to pass to the view
            */
            initialize: function (options) {

              try {
                // Get all the options and apply them to this view
                if (typeof options == 'object') {
                  for (const [key, value] of Object.entries(options)) {
                    this[key] = value;
                  }
                }
              } catch (e) {
                console.log('A LayerDetailsView failed to initialize. Error message: ' + e);
              }

            },

            /**
            * Renders this view
            * @return {LayerDetailsView} Returns the rendered view element
            */
            render: function () {

              try {

                // Save a reference to this view
                var view = this;
                var model = this.model;

                // Show the layer details box as open if the view is set to have it open
                // already
                if (this.isOpen) {
                  this.el.classList.add(this.classes.open);
                }

                // Insert the template into the view
                this.$el.html(this.template({
                  label: model ? model.get('label') || '' : ''
                }));

                // Ensure the view's main element has the given class name
                this.el.classList.add(this.className);

                // Select elements in the template that we will need to manipulate
                const sectionsContainer = this.el.querySelector('.' + this.classes.sections)
                const labelEl = this.el.querySelector('.' + this.classes.label)

                // Render each section in the Details panel
                this.renderedSections = _.clone(this.sections)

                this.renderedSections.forEach(function (section) {
                  var detailSection = new LayerDetailView({
                    label: section.label,
                    contentView: section.view,
                    model: model,
                    collapsible: section.collapsible,
                    showTitle: section.showTitle
                  })
                  sectionsContainer.append(detailSection.el)
                  detailSection.render()
                  // Hide the section if there is an error with the asset, and this section
                  // does make sense to show for a layer that can't be displayed
                  if (section.hideIfError && model) {
                    if (model && model.get('status') === 'error') {
                      detailSection.el.style.display = 'none'
                    }
                  }
                  section.renderedView = detailSection
                })

                // Hide/show sections with the 'hideIfError' property when the status of the
                // MapAsset changes
                this.stopListening(model, 'change:status')
                this.listenTo(model, 'change:status', function (model, status) {
                  const hideIfErrorSections = _.filter(this.renderedSections, function (section) {
                    return section.hideIfError
                  })
                  let displayProperty = ''
                  if (status === 'error') {
                    displayProperty = 'none'
                  }
                  hideIfErrorSections.forEach(function (section) {
                    section.renderedView.el.style.display = displayProperty
                  })
                })

                // If this layer has a notification, show the badge and notification
                // message
                const notice = model ? model.get('notification') : null
                if (notice && (notice.message || notice.badge)) {
                  // message
                  if (notice.message) {
                    const noticeEl = document.createElement('div')
                    noticeEl.classList.add(this.classes.notification)
                    noticeEl.innerText = notice.message
                    if (notice.style) {
                      const badgeClass = this.classes.notification + '--' + notice.style
                      noticeEl.classList.add(badgeClass)
                    }
                    sectionsContainer.prepend(noticeEl)
                  }
                  // badge
                  if (notice.badge) {
                    const badge = document.createElement('span')
                    badge.classList.add(this.classes.badge)
                    badge.innerText = notice.badge
                    if (notice.style) {
                      const badgeClass = this.classes.badge + '--' + notice.style
                      badge.classList.add(badgeClass)
                    }
                    labelEl.append(badge)
                  }

                }

                return this

              }
              catch (error) {
                console.log(
                  'There was an error rendering a LayerDetailsView' +
                  '. Error details: ' + error
                );
              }
            },

            /**
             * Show/expand the Layer Details panel. Opening the panel also changes the
             * MapAsset model's 'selected attribute' to true.
             */
            open: function () {
              try {
                this.el.classList.add(this.classes.open);
                this.isOpen = true;
                // Ensure that the model is marked as selected
                if (this.model) {
                  this.model.set('selected', true)
                }
              }
              catch (error) {
                console.log(
                  'There was an error opening the LayerDetailsView' +
                  '. Error details: ' + error
                );
              }
            },

            /**
             * Hide/collapse the Layer Details panel. Closing the panel also changes the
             * MapAsset model's 'selected attribute' to false.
             */
            close: function () {
              try {
                this.el.classList.remove(this.classes.open);
                this.isOpen = false;
                // Ensure that the model is not marked as selected
                if (this.model) {
                  this.model.set('selected', false)
                }
              }
              catch (error) {
                console.log(
                  'There was an error closing the LayerDetailsView' +
                  '. Error details: ' + error
                );
              }
            },

            /**
             * Updates the MapAsset model set on the view then re-renders the view and
             * displays information about the new model.
             * @param {MapAsset|null} newModel the new MapAsset model to use to render the
             * view. If set to null, then the view will be rendered without any layer
             * information.
             */
            updateModel: function (newModel) {
              try {
                // Remove listeners from sub-views
                this.renderedSections.forEach(function (section) {
                  if (
                    section.renderedView &&
                    typeof section.renderedView.onClose === 'function'
                  ) {
                    section.renderedView.onClose()
                  }
                })
                this.model = newModel;
                this.render()
              }
              catch (error) {
                console.log(
                  'There was an error updating the MapAsset model in a LayerDetailsView' +
                  '. Error details: ' + error
                );
              }
            },

          }
        );

        return LayerDetailsView;

      }
    );