/src/actions/gesture.js

const utils = require('../utils');

function init (scope) {
  const {
    actions,
    InteractEvent,
    Interactable,
    Interaction,
    defaults,
  } = scope;

  const gesture = {
    defaults: {
    },

    checker: function (pointer, event, interactable, element, interaction) {
      if (interaction.pointerIds.length >= 2) {
        return { name: 'gesture' };
      }

      return null;
    },

    getCursor: function () {
      return '';
    },
  };

  InteractEvent.signals.on('new', function ({ iEvent, interaction }) {
    if (iEvent.type !== 'gesturestart') { return; }
    iEvent.ds = 0;

    interaction.gesture.startDistance = interaction.gesture.prevDistance = iEvent.distance;
    interaction.gesture.startAngle = interaction.gesture.prevAngle = iEvent.angle;
    interaction.gesture.scale = 1;
  });

  InteractEvent.signals.on('new', function ({ iEvent, interaction }) {
    if (iEvent.type !== 'gesturemove') { return; }

    iEvent.ds = iEvent.scale - interaction.gesture.scale;

    interaction.target.fire(iEvent);

    interaction.gesture.prevAngle = iEvent.angle;
    interaction.gesture.prevDistance = iEvent.distance;

    if (iEvent.scale !== Infinity
        && iEvent.scale !== null
        && iEvent.scale !== undefined
        && !isNaN(iEvent.scale)) {

      interaction.gesture.scale = iEvent.scale;
    }
  });

  /**
   * ```js
   * interact(element).gesturable({
   *     onstart: function (event) {},
   *     onmove : function (event) {},
   *     onend  : function (event) {},
   *
   *     // limit multiple gestures.
   *     // See the explanation in {@link Interactable.draggable} example
   *     max: Infinity,
   *     maxPerElement: 1,
   * });
   *
   * var isGestureable = interact(element).gesturable();
   * ```
   *
   * Gets or sets whether multitouch gestures can be performed on the target
   *
   * @param {boolean | object} [options] true/false or An object with event
   * listeners to be fired on gesture events (makes the Interactable gesturable)
   * @return {boolean | Interactable} A boolean indicating if this can be the
   * target of gesture events, or this Interactable
   */
  Interactable.prototype.gesturable = function (options) {
    if (utils.is.object(options)) {
      this.options.gesture.enabled = options.enabled === false? false: true;
      this.setPerAction('gesture', options);
      this.setOnEvents('gesture', options);

      return this;
    }

    if (utils.is.bool(options)) {
      this.options.gesture.enabled = options;

      if (!options) {
        this.ongesturestart = this.ongesturestart = this.ongestureend = null;
      }

      return this;
    }

    return this.options.gesture;
  };

  InteractEvent.signals.on('set-delta', function ({ interaction, iEvent, action, event, starting, ending, deltaSource }) {
    if (action !== 'gesture') { return; }

    const pointers = interaction.pointers;

    iEvent.touches = [pointers[0], pointers[1]];

    if (starting) {
      iEvent.distance = utils.pointer.touchDistance(pointers, deltaSource);
      iEvent.box      = utils.pointer.touchBBox(pointers);
      iEvent.scale    = 1;
      iEvent.ds       = 0;
      iEvent.angle    = utils.pointer.touchAngle(pointers, undefined, deltaSource);
      iEvent.da       = 0;
    }
    else if (ending || event instanceof InteractEvent) {
      iEvent.distance = interaction.prevEvent.distance;
      iEvent.box      = interaction.prevEvent.box;
      iEvent.scale    = interaction.prevEvent.scale;
      iEvent.ds       = iEvent.scale - 1;
      iEvent.angle    = interaction.prevEvent.angle;
      iEvent.da       = iEvent.angle - interaction.gesture.startAngle;
    }
    else {
      iEvent.distance = utils.pointer.touchDistance(pointers, deltaSource);
      iEvent.box      = utils.pointer.touchBBox(pointers);
      iEvent.scale    = iEvent.distance / interaction.gesture.startDistance;
      iEvent.angle    = utils.pointer.touchAngle(pointers, interaction.gesture.prevAngle, deltaSource);

      iEvent.ds = iEvent.scale - interaction.gesture.prevScale;
      iEvent.da = iEvent.angle - interaction.gesture.prevAngle;
    }
  });

  Interaction.signals.on('new', function (interaction) {
    interaction.gesture = {
      start: { x: 0, y: 0 },

      startDistance: 0,   // distance between two touches of touchStart
      prevDistance : 0,
      distance     : 0,

      scale: 1,           // gesture.distance / gesture.startDistance

      startAngle: 0,      // angle of line joining two touches
      prevAngle : 0,      // angle of the previous gesture event
    };
  });

  actions.gesture = gesture;
  actions.names.push('gesture');
  utils.arr.merge(Interactable.eventTypes, [
    'gesturestart',
    'gesturemove',
    'gestureend',
  ]);
  actions.methodDict.gesture = 'gesturable';

  defaults.gesture = gesture.defaults;
}

module.exports = { init };