// Copyright 2017-2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only

/* eslint-disable */

// This file was taken from Backbone and then modified. It does not conform to this
//   project's standards.

(function () {
  'use strict';

  // Note: this is all the code required to customize Backbone's trigger() method to make
  //   it resilient to exceptions thrown by event handlers. Indentation and code styles
  //   were kept inline with the Backbone implementation for easier diffs.

  // The changes are:
  //   1. added 'name' parameter to triggerEvents to give it access to the
  //      current event name
  //   2. added try/catch handlers to triggerEvents with error logging inside
  //      every while loop

  // And of course, we update the protoypes of Backbone.Model/Backbone.View as well as
  //   Backbone.Events itself

  const arr = [];

  const slice = arr.slice;

  // Regular expression used to split event strings.
  const eventSplitter = /\s+/;

  // Implement fancy features of the Events API such as multiple event
  // names `"change blur"` and jQuery-style event maps `{change: action}`
  // in terms of the existing API.
  const eventsApi = function (obj, action, name, rest) {
    if (!name) return true;

    // Handle event maps.
    if (typeof name === 'object') {
      for (const key in name) {
        obj[action](...[key, name[key]].concat(rest));
      }
      return false;
    }

    // Handle space separated event names.
    if (eventSplitter.test(name)) {
      const names = name.split(eventSplitter);
      for (let i = 0, l = names.length; i < l; i++) {
        obj[action](...[names[i]].concat(rest));
      }
      return false;
    }

    return true;
  };

  // A difficult-to-believe, but optimized internal dispatch function for
  // triggering events. Tries to keep the usual cases speedy (most internal
  // Backbone events have 3 arguments).
  const triggerEvents = function (events, name, args) {
    let ev,
      i = -1,
      l = events.length,
      a1 = args[0],
      a2 = args[1],
      a3 = args[2];
    const logError = function (error) {
      window.SignalContext.log.error(
        'Model caught error triggering',
        name,
        'event:',
        error && error.stack ? error.stack : error
      );
    };
    switch (args.length) {
      case 0:
        while (++i < l) {
          try {
            (ev = events[i]).callback.call(ev.ctx);
          } catch (error) {
            logError(error);
          }
        }
        return;
      case 1:
        while (++i < l) {
          try {
            (ev = events[i]).callback.call(ev.ctx, a1);
          } catch (error) {
            logError(error);
          }
        }
        return;
      case 2:
        while (++i < l) {
          try {
            (ev = events[i]).callback.call(ev.ctx, a1, a2);
          } catch (error) {
            logError(error);
          }
        }
        return;
      case 3:
        while (++i < l) {
          try {
            (ev = events[i]).callback.call(ev.ctx, a1, a2, a3);
          } catch (error) {
            logError(error);
          }
        }
        return;
      default:
        while (++i < l) {
          try {
            (ev = events[i]).callback.apply(ev.ctx, args);
          } catch (error) {
            logError(error);
          }
        }
    }
  };

  // Trigger one or many events, firing all bound callbacks. Callbacks are
  // passed the same arguments as `trigger` is, apart from the event name
  // (unless you're listening on `"all"`, which will cause your callback to
  // receive the true name of the event as the first argument).
  function trigger(name) {
    if (!this._events) return this;
    const args = slice.call(arguments, 1);
    if (!eventsApi(this, 'trigger', name, args)) return this;
    const events = this._events[name];
    const allEvents = this._events.all;
    if (events) triggerEvents(events, name, args);
    if (allEvents) triggerEvents(allEvents, name, arguments);
    return this;
  }

  Backbone.Model.prototype.trigger =
    Backbone.View.prototype.trigger =
    Backbone.Collection.prototype.trigger =
    Backbone.Events.trigger =
      trigger;
})();
