/*
 * Copyright (C) 2018 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

import K from './constants'
import $ from 'jquery'
import {throttle, extend, uniqueId} from 'underscore'

// # An event tracker installs code to capture quiz events and submits them for
// # delivery.
// #
// # You should subclass this to define logic for capturing different types of
// # quiz events.
export default class EventTracker {
  constructor(options = {}) {
    // @property {String} eventType
    //
    // The type of events recorded by this tracker, like "question_answered" or
    // "page_focused".
    //
    // THIS IS REQUIRED AND MUST BE UNIQUE PER EVENT TRACKER.
    //
    // See ./constants.coffee for the defined event types.
    if (!this.eventType) this.eventType = null

    // @property {Number} priority
    //
    // Priority of the events generated by this tracker. This parameter is used
    // for delivery purposes and is not understood by the backend.
    //
    // Higher priority indicates that events should be delivered ASAP.
    //
    // Possible values: see ./constants.coffee
    if (!this.priority) this.priority = K.EVT_PRIORITY_LOW

    this._options = extend({}, this.options, options)
    this.uid = `${this.eventType}_${uniqueId()}`
  }

  // Start capturing events. This is where we get to install window event
  // listeners and setup any necessary tracker context.
  //
  // @param {Function} deliveryCallback
  // A callback to invoke when the tracker has an event ready for delivery.
  install(deliveryCallback) {
    throw new Error('Not implemented!')
  }

  getEventType() {
    return this.eventType
  }

  getDeliveryPriority() {
    return this.priority
  }

  getOption(name) {
    return this._options[name]
  }

  // Teardown
  //
  // Call super() if you're overriding this and have used the #bind() helper.
  uninstall() {
    if (this._bindings) {
      this._bindings.forEach(binding => $(binding.selector).off(binding.eventName))

      return (this._bindings = null)
    }
  }

  // A helper for doing $.fn.bind() with 2 extra things:
  //
  // - scopes the event to this tracker so it won't conflict with other jQuery
  //   event handlers we have installed (e.g, $(window).on('click.me'))
  //
  // - automatically unbinds when the tracker is uninstalled (assuming you'd
  //   call super() if you override it)
  //
  // @param {String|HTMLElement|jQuery} selector
  //
  // @param {String} eventName
  //   This would be the Event / jQuery event name, like "click" or "blur".
  //
  // @param {Function} callback
  //
  // @param {Object} [options={}]
  //   More nice stuff.
  //
  // @param {Number} [options.throttle=undefined]
  //   If you set this to a number, the callback will be called at most once
  //   every @throttle ms.
  bind(selector, eventName, callback, options = {}) {
    if (!this._bindings) this._bindings = []
    this._bindings.push({selector, eventName})

    const throttleMs = parseInt(options.throttle || 0, 10)

    if (throttleMs > 0) {
      callback = throttle(callback, throttleMs, {
        leading: true,
        trailing: false
      })
    }

    $(selector).on(`${eventName}.${this.eventType}`, callback)
  }
}
