AFRAME.registerComponent("super-spawner", {
  schema: {
    template: { default: "" },
    useCustomSpawnPosition: { default: false },
    spawnPosition: { type: "vec3" },
    events: { default: ["cursor-grab", "action_grab"] },
    spawnCooldown: { default: 1 }
  },

  init: function() {
    this.entities = new Map();
    this.timeout = null;
    this.defaultScale = this.el.getAttribute("scale").clone();
  },

  play: function() {
    this.handleGrabStart = this._handleGrabStart.bind(this);
    this.el.addEventListener("grab-start", this.handleGrabStart);
  },

  pause: function() {
    this.el.removeEventListener("grab-start", this.handleGrabStart);

    clearTimeout(this.timeout);
    this.timeout = null;
    this.el.setAttribute("visible", true);
    this.el.setAttribute("scale", this.defaultScale);
  },

  remove: function() {
    for (const entity of this.entities.keys()) {
      const data = this.entities.get(entity);
      entity.removeEventListener("componentinitialized", data.componentinInitializedListener);
      entity.removeEventListener("body-loaded", data.bodyLoadedListener);
    }

    this.entities.clear();
  },

  _handleGrabStart: function(e) {
    if (this.timeout) {
      return;
    }
    const hand = e.detail.hand;
    const entity = document.createElement("a-entity");

    entity.setAttribute("networked", "template:" + this.data.template);

    const componentinInitializedListener = this._handleComponentInitialzed.bind(this, entity);
    const bodyLoadedListener = this._handleBodyLoaded.bind(this, entity);
    this.entities.set(entity, {
      hand: hand,
      componentInitialized: false,
      bodyLoaded: false,
      componentinInitializedListener: componentinInitializedListener,
      bodyLoadedListener: bodyLoadedListener
    });

    entity.addEventListener("componentinitialized", componentinInitializedListener);
    entity.addEventListener("body-loaded", bodyLoadedListener);

    const pos = this.data.useCustomSpawnPosition ? this.data.spawnPosition : this.el.getAttribute("position");
    entity.setAttribute("position", pos);
    this.el.sceneEl.appendChild(entity);

    if (this.data.spawnCooldown > 0) {
      this.el.setAttribute("visible", false);
      this.el.setAttribute("scale", { x: 0.0001, y: 0.0001, z: 0.0001 });
      this.timeout = setTimeout(() => {
        this.el.setAttribute("visible", true);
        this.el.setAttribute("scale", this.defaultScale);
        this.timeout = null;
      }, this.data.spawnCooldown * 1000);
    }
  },

  _handleComponentInitialzed: function(entity, e) {
    if (e.detail.name === "grabbable") {
      this.entities.get(entity).componentInitialized = true;
      this._emitEvents.call(this, entity);
    }
  },

  _handleBodyLoaded: function(entity) {
    this.entities.get(entity).bodyLoaded = true;
    this._emitEvents.call(this, entity);
  },

  _emitEvents: function(entity) {
    const data = this.entities.get(entity);
    if (data.componentInitialized && data.bodyLoaded) {
      for (let i = 0; i < this.data.events.length; i++) {
        data.hand.emit(this.data.events[i], { targetEntity: entity });
      }

      entity.removeEventListener("componentinitialized", data.componentinInitializedListener);
      entity.removeEventListener("body-loaded", data.bodyLoadedListener);

      this.entities.delete(entity);
    }
  }
});