# Source: src/js/views/MetricModalView.js

    /*global define */
    define([
      "jquery",
      "underscore",
      "backbone",
      "MetricsChart",
      "text!templates/metricModalTemplate.html",
      "collections/Citations",
      "views/CitationListView",
      "views/SignInView",
    ], function (
      $,
      _,
      Backbone,
      MetricsChart,
      MetricModalTemplate,
      Citations,
      CitationList,
      SignInView
    ) {
      "use strict";

      /**
       * @class MetricModalView
       * @classdesc A Bootstrap Modal that displays a DataONE dataset usage metric,
       * such as downloads, views, or citations.
       * @classcategory Views
       * @extends Backbone.View
       */
      var MetricModalView = Backbone.View.extend(
        /** @lends MetricModalView.prototype */ {
          /**
           * An ID for this view
           * @type {string}
           */
          id: "metric-modal",

          /**
           * Classes to add to the modal
           * @type {string}
           */
          className: "modal fade hide",

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

          /**
           * The model that contains the metrics data
           * @type {MetricsModel}
           */
          metricsModel: null,

          /**
           * A metric option is an object with properties that define how to display
           * a metric in the modal.
           * @typedef {Object} MetricOption
           * @property {string} name - The name of the metric, as it will be
           * displayed in the modal.
           * @property {string} icon - The font awesome icon class for the metric
           * @property {string} metricValue - The name of the property in the
           * metrics model that contains the value for this metric. This will be
           * displayed in the title of the modal.
           * @property {string} render - The name of the method within this view
           * that will render the metric. This method will be called after the
           * basic modal template has been rendered.
           */

          /**
           * The metrics to include in the modal, in the order they will be
           * displayed.
           * @type {MetricOption[]}
           */
          metrics: [
            {
              name: "Downloads",
              icon: "icon-cloud-download",
              metricValue: "totalDownloads",
              render: "drawMetricsChart",
            },
            {
              name: "Citations",
              icon: "icon-quote-left",
              metricValue: "totalCitations",
              render: "showCitations",
            },
            {
              name: "Views",
              icon: "icon-eye-open",
              metricValue: "totalViews",
              render: "drawMetricsChart",
            },
          ],

          /**
           * The name of the metric currently being displayed
           * @type {string}
           */
          metricName: null,

          /**
           * Views that are subviews of this view
           * @type {Backbone.View[]}
           */
          subviews: [],

          /**
           * The events this view will listen for. See
           * {@link https://backbonejs.org/#View-events}
           * @type {Object}
           */
          events: {
            hidden: "teardown",
            "click .left-modal-footer": "showPreviousMetricModal",
            "click .right-modal-footer": "showNextMetricModal",
            "click .register-citation": "showCitationForm",
            "click .login": "showSignInViewPopUp",
          },

          /**
           * Initialize a new MetricModalView
           * @param {Object} options - A literal object with options to pass to the
           * view. The options can include:
           * @param {string} options.metricName - The name of the metric to display
           * in the modal
           * @param {MetricsModel} options.metricsModel - The model that contains
           * the metrics data
           * @param {string} options.pid - The DataONE PID of the dataset that the
           * metrics are for
           */
          initialize: function (options) {
            _.bindAll(this, "show", "teardown", "render", "renderView");
            if (typeof options == "undefined") {
              var options = {};
            }

            this.metricName = options.metricName;
            this.metricsModel = options.metricsModel;
            this.pid = options.pid;
          },

          /**
           * Set a listener that will render the view when the modal is shown
           */
          render: function () {
            var thisView = this;

            this.$el.on("shown", function () {
              thisView.renderView();
              thisView.trigger("renderComplete");
            });

            this.$el.modal("show");

            return this;
          },

          /**
           * Render the view
           * @returns {MetricModalView} - Returns this view
           */
          renderView: function () {
            try {
              // Get the current metric name and associated options
              const metric = this.metricName || this.metrics[0].name;
              const metricOpts = this.metrics.find(
                (metric) => metric.name === this.metricName
              );

              // Get the name in the singular form in lower case.
              this.metricNameLemma = metric.slice(0, -1).toLowerCase();

              // Render the template
              this.el.innerHTML = this.template({
                metricName: this.metricName,
                metricNameLemma: this.metricNameLemma,
                nextMetric: this.getNextMetric() || "",
                prevMetric: this.getPreviousMetric() || "",
                metricIcon: metricOpts.icon,
                metricValue: this.metricsModel.get(metricOpts.metricValue),
              });

              // Call the specific render function for the metric
              if (typeof this[metricOpts.render] === "function") {
                this[metricOpts.render]();
              }
            } catch (e) {
              console.error("Failed to render the MetricModelView: ", e);
              MetacatUI.appView.showAlert({
                message:
                  `Something went wrong displaying the ${this.metricNameLemma}s ` +
                  `for this dataset.`,
                classes: "alert-info",
                container: this.$el,
                replaceContents: true,
                includeEmail: true,
              });
            } finally {
              this.$el.modal({ show: false }); // don't show modal on instantiation
            }
          },

          /**
           * Get the previous metric name in the circular queue
           * @returns {string} The name of the previous metric
           */
          getNextMetric: function () {
            return this.getMetricAtOffset(1);
          },

          /**
           * Get the next metric name in the circular queue
           * @returns {string} The name of the next metric
           */
          getPreviousMetric: function () {
            return this.getMetricAtOffset(-1);
          },

          /**
           * Get the metric name at the given offset from the current metric
           * @param {number} n - The offset from the current metric
           * @returns {string} The name of the metric at the given offset
           * @since 2.23.0
           */
          getMetricAtOffset: function (n) {
            const currentMetricName = this.metricName || this.metrics[0].name;
            const currentMetricIndex = this.metrics.findIndex(
              (metric) => metric.name === currentMetricName
            );
            let metricIndex = (currentMetricIndex + n) % this.metrics.length;
            if (metricIndex < 0) {
              metricIndex = this.metrics.length + metricIndex;
            }
            return this.metrics[metricIndex].name;
          },

          /**
           * Make the modal visible
           */
          show: function () {
            this.$el.modal("show");
          },

          /**
           * Show the previous metric in the modal
           */
          showPreviousMetricModal: function () {
            this.metricName = this.getPreviousMetric();
            this.renderView();
          },

          /**
           * Show the next metric in the modal
           */
          showNextMetricModal: function () {
            this.metricName = this.getNextMetric();
            this.renderView();
          },

          /**
           * Show the citations in the modal. Replace current content.
           */
          showCitations: function () {
            var resultDetails = this.metricsModel.get("resultDetails");
            let citationCollection;

            if (resultDetails) {
              citationCollection = new Citations(resultDetails["citations"], {
                parse: true,
              });
            } else {
              citationCollection = new Citations();
            }

            this.citationCollection = citationCollection;

            var modalBody = this.el.querySelector(".modal-body");

            // Checking if there are any citations available for the List display.
            if (this.metricsModel.get("totalCitations") == 0) {
              var citationList = new CitationList({
                citationsForDataCatalogView: true,
                pid: this.pid,
                el: modalBody,
              });
            } else {
              var citationList = new CitationList({
                citations: this.citationCollection,
                citationsForDataCatalogView: true,
                pid: this.pid,
                el: modalBody,
              });
            }
            citationList.render();
            this.citationList = citationList;
            this.subviews.push(citationList);
          },

          /**
           * Display the Citation registration form
           */
          showCitationForm: function () {
            var viewRef = this;

            // if the user is not currently signed in
            if (!MetacatUI.appUserModel.get("loggedIn")) {
              this.showSignIn();
            } else {
              // close the current modal
              this.teardown();

              require(["views/RegisterCitationView"], function (
                RegisterCitationView
              ) {
                // display a register citation modal
                var registerCitationView = new RegisterCitationView({
                  pid: viewRef.pid,
                });
                registerCitationView.render();
                registerCitationView.show();
              });
            }
          },

          /**
           * Show Sign In buttons
           */
          showSignIn: function () {
            var container = $(document.createElement("div")).addClass(
              "container center"
            );
            this.$el.html(container);

            //Create a SignInView
            let signInView = new SignInView();
            signInView.redirectQueryString = "registerCitation=true";

            //Get the Sign In buttons elements
            var signInButtons = signInView.render().el;
            this.signInButtons = signInButtons;

            //Add the elements to the page
            $(container).append(
              "<h1>Sign in to register citations</h1>",
              signInButtons
            );
          },

          /**
           * Handle the sign in click event
           */
          showSignInViewPopUp: function () {
            // close the current modal
            this.teardown();

            // display the pop up
            this.signInButtons.showSignInViewPopUp();
          },

          /**
           * Draw the metrics chart
           */
          drawMetricsChart: function () {
            // Create <div class='metric-chart'></div>
            const chartContainer = document.createElement("div");
            chartContainer.className = "metric-chart";
            // Prepend to modal-body
            this.$el.find(".modal-body").prepend(chartContainer);
            var metricCount = MetacatUI.appView.currentView.metricsModel.get(
              this.metricName.toLowerCase()
            );
            var metricMonths =
              MetacatUI.appView.currentView.metricsModel.get("months");
            var metricName = this.metricName;

            //Draw a metric chart
            var modalMetricChart = new MetricsChart({
              id: "metrics-chart",
              metricCount: metricCount,
              metricMonths: metricMonths,
              metricName: metricName,
              height: 370,
            });

            this.$(".metric-chart").html(modalMetricChart.el);
            modalMetricChart.render();

            this.subviews.push(modalMetricChart);
          },

          /**
           * Remove the modal from the DOM
           */
          teardown: function () {
            this.$el.modal("hide");
            this.$el.data("modal", null);

            _.invoke(this.subviews, "onClose");

            this.remove();
          },

          /**
           * Cleans up and removes all artifacts created for view
           */
          onClose: function () {
            this.teardown();
          },
        }
      );

      return MetricModalView;
    });