text: var registerComponent = require('../core/component').registerComponent;
  var bind = require('../utils/bind');
  var trackedControlsUtils = require('../utils/tracked-controls');
  var checkControllerPresentAndSetup = trackedControlsUtils.checkControllerPresentAndSetup;
  var emitIfAxesChanged = trackedControlsUtils.emitIfAxesChanged;
  var onButtonEvent = trackedControlsUtils.onButtonEvent;
  
  var GAMEPAD_ID_PREFIX = 'Oculus Go';
  
  var OCULUS_GO_CONTROLLER_MODEL_URL = 'https://cdn.aframe.io/controllers/oculus/go/oculus-go-controller.gltf';
  
  /**
   * Oculus Go controls.
   * Interface with Oculus Go controller and map Gamepad events to
   * controller buttons: trackpad, trigger
   * Load a controller model and highlight the pressed buttons.
   */
  module.exports.Component = registerComponent('oculus-go-controls', {
    schema: {
      hand: {default: ''},  // This informs the degenerate arm model.
      buttonColor: {type: 'color', default: '#FFFFFF'},
      buttonTouchedColor: {type: 'color', default: '#BBBBBB'},
      buttonHighlightColor: {type: 'color', default: '#7A7A7A'},
      model: {default: true},
      rotationOffset: {default: 0},
      armModel: {default: true}
    },
  
    /**
     * Button IDs:
     * 0 - trackpad
     * 1 - trigger
     */
    mapping: {
      axes: {trackpad: [0, 1]},
      buttons: ['trackpad', 'trigger']
    },
  
    bindMethods: function () {
      this.onModelLoaded = bind(this.onModelLoaded, this);
      this.onControllersUpdate = bind(this.onControllersUpdate, this);
      this.checkIfControllerPresent = bind(this.checkIfControllerPresent, this);
      this.removeControllersUpdateListener = bind(this.removeControllersUpdateListener, this);
      this.onAxisMoved = bind(this.onAxisMoved, this);
    },
  
    init: function () {
      var self = this;
      this.animationActive = 'pointing';
      this.onButtonChanged = bind(this.onButtonChanged, this);
      this.onButtonDown = function (evt) { onButtonEvent(evt.detail.id, 'down', self); };
      this.onButtonUp = function (evt) { onButtonEvent(evt.detail.id, 'up', self); };
      this.onButtonTouchStart = function (evt) { onButtonEvent(evt.detail.id, 'touchstart', self); };
      this.onButtonTouchEnd = function (evt) { onButtonEvent(evt.detail.id, 'touchend', self); };
      this.onAxisMoved = bind(this.onAxisMoved, this);
      this.controllerPresent = false;
      this.lastControllerCheck = 0;
      this.rendererSystem = this.el.sceneEl.systems.renderer;
      this.bindMethods();
      this.checkControllerPresentAndSetup = checkControllerPresentAndSetup;  // To allow mock.
      this.emitIfAxesChanged = emitIfAxesChanged;  // To allow mock.
    },
  
    addEventListeners: function () {
      var el = this.el;
      el.addEventListener('buttonchanged', this.onButtonChanged);
      el.addEventListener('buttondown', this.onButtonDown);
      el.addEventListener('buttonup', this.onButtonUp);
      el.addEventListener('touchstart', this.onButtonTouchStart);
      el.addEventListener('touchend', this.onButtonTouchEnd);
      el.addEventListener('model-loaded', this.onModelLoaded);
      el.addEventListener('axismove', this.onAxisMoved);
      this.controllerEventsActive = true;
    },
  
    removeEventListeners: function () {
      var el = this.el;
      el.removeEventListener('buttonchanged', this.onButtonChanged);
      el.removeEventListener('buttondown', this.onButtonDown);
      el.removeEventListener('buttonup', this.onButtonUp);
      el.removeEventListener('touchstart', this.onButtonTouchStart);
      el.removeEventListener('touchend', this.onButtonTouchEnd);
      el.removeEventListener('model-loaded', this.onModelLoaded);
      el.removeEventListener('axismove', this.onAxisMoved);
      this.controllerEventsActive = false;
    },
  
    checkIfControllerPresent: function () {
      this.checkControllerPresentAndSetup(this, GAMEPAD_ID_PREFIX,
                                          this.data.hand ? {hand: this.data.hand} : {});
    },
  
    play: function () {
      this.checkIfControllerPresent();
      this.addControllersUpdateListener();
    },
  
    pause: function () {
      this.removeEventListeners();
      this.removeControllersUpdateListener();
    },
  
    injectTrackedControls: function () {
      var el = this.el;
      var data = this.data;
      el.setAttribute('tracked-controls', {
        armModel: data.armModel,
        idPrefix: GAMEPAD_ID_PREFIX,
        rotationOffset: data.rotationOffset
      });
      if (!this.data.model) { return; }
      this.el.setAttribute('gltf-model', OCULUS_GO_CONTROLLER_MODEL_URL);
    },
  
    addControllersUpdateListener: function () {
      this.el.sceneEl.addEventListener('controllersupdated', this.onControllersUpdate, false);
    },
  
    removeControllersUpdateListener: function () {
      this.el.sceneEl.removeEventListener('controllersupdated', this.onControllersUpdate, false);
    },
  
    onControllersUpdate: function () {
      this.checkIfControllerPresent();
    },
  
    // No need for onButtonChanged, since Oculus Go controller has no analog buttons.
  
    onModelLoaded: function (evt) {
      var controllerObject3D = evt.detail.model;
      var buttonMeshes;
  
      if (!this.data.model) { return; }
      buttonMeshes = this.buttonMeshes = {};
      buttonMeshes.trigger = controllerObject3D.getObjectByName('oculus_go_button_trigger');
      buttonMeshes.trackpad = controllerObject3D.getObjectByName('oculus_go_touchpad');
    },
  
    onButtonChanged: function (evt) {
      var button = this.mapping.buttons[evt.detail.id];
      if (!button) return;
      // Pass along changed event with button state, using button mapping for convenience.
      this.el.emit(button + 'changed', evt.detail.state);
    },
  
    onAxisMoved: function (evt) {
      this.emitIfAxesChanged(this, this.mapping.axes, evt);
    },
  
    updateModel: function (buttonName, evtName) {
      if (!this.data.model) { return; }
      this.updateButtonModel(buttonName, evtName);
    },
  
    updateButtonModel: function (buttonName, state) {
      var buttonMeshes = this.buttonMeshes;
      if (!buttonMeshes || !buttonMeshes[buttonName]) { return; }
      var color;
      var button;
      switch (state) {
        case 'down':
          color = this.data.buttonHighlightColor;
          break;
        case 'touchstart':
          color = this.data.buttonTouchedColor;
          break;
        default:
          color = this.data.buttonColor;
      }
      button = buttonMeshes[buttonName];
      button.material.color.set(color);
      this.rendererSystem.applyColorCorrection(button.material.color);
    }
  });
  