const invaderPos = new AFRAME.THREE.Vector3();
const bubblePos = new AFRAME.THREE.Vector3();

AFRAME.registerSystem("personal-space-bubble", {
  schema: {
    debug: { default: false }
  },

  init() {
    this.invaders = [];
    this.bubbles = [];
  },

  registerBubble(bubble) {
    this.bubbles.push(bubble);
  },

  unregisterBubble(bubble) {
    const index = this.bubbles.indexOf(bubble);

    if (index !== -1) {
      this.bubbles.splice(index, 1);
    }
  },

  registerInvader(invader) {
    NAF.utils.getNetworkedEntity(invader.el).then(networkedEl => {
      const owner = NAF.utils.getNetworkOwner(networkedEl);

      if (owner !== NAF.clientId) {
        this.invaders.push(invader);
      }
    });
  },

  unregisterInvader(invader) {
    const index = this.invaders.indexOf(invader);

    if (index !== -1) {
      this.invaders.splice(index, 1);
    }
  },

  tick() {
    // Update matrix positions once for each space bubble and space invader
    for (let i = 0; i < this.bubbles.length; i++) {
      this.bubbles[i].el.object3D.updateMatrixWorld(true);
    }

    for (let i = 0; i < this.invaders.length; i++) {
      this.invaders[i].el.object3D.updateMatrixWorld(true);
      this.invaders[i].setInvading(false);
    }

    // Loop through all of the space bubbles (usually one)
    for (let i = 0; i < this.bubbles.length; i++) {
      const bubble = this.bubbles[i];

      bubblePos.setFromMatrixPosition(bubble.el.object3D.matrixWorld);

      // Hide the invader if inside the bubble
      for (let j = 0; j < this.invaders.length; j++) {
        const invader = this.invaders[j];

        invaderPos.setFromMatrixPosition(invader.el.object3D.matrixWorld);

        const distanceSquared = bubblePos.distanceToSquared(invaderPos);
        const radiusSum = bubble.data.radius + invader.data.radius;
        if (distanceSquared < radiusSum * radiusSum) {
          invader.setInvading(true);
        }
      }
    }
  }
});

function createSphereGizmo(radius) {
  const geometry = new THREE.SphereBufferGeometry(radius, 10, 10);
  const wireframe = new THREE.WireframeGeometry(geometry);
  const line = new THREE.LineSegments(wireframe);
  line.material.opacity = 0.5;
  line.material.transparent = true;
  return line;
}

// TODO: we need to come up with a more generic way of doing this as this is very specific to our avatars.
AFRAME.registerComponent("space-invader-mesh", {
  schema: {
    meshSelector: { type: "string" }
  },
  init() {
    this.targetMesh = this.el.querySelector(this.data.meshSelector).object3DMap.skinnedmesh;
    console.log("target", this.targetMesh);
  }
});

function findInvderMesh(entity) {
  while (entity && !(entity.components && entity.components["space-invader-mesh"])) {
    entity = entity.parentNode;
  }
  return entity && entity.components["space-invader-mesh"].targetMesh;
}

AFRAME.registerComponent("personal-space-invader", {
  schema: {
    radius: { type: "number", default: 0.1 },
    useMaterial: { default: false },
    debug: { default: false }
  },
  init() {
    const system = this.el.sceneEl.systems["personal-space-bubble"];
    system.registerInvader(this);
    if (system.data.debug || this.data.debug) {
      this.el.object3D.add(createSphereGizmo(this.data.radius));
    }
    if (this.data.useMaterial) {
      const mesh = findInvderMesh(this.el);
      if (mesh) {
        this.targetMaterial = mesh.material;
      }
    }
    this.invading = false;
  },

  update() {
    this.radiusSquared = this.data.radius * this.data.radius;
  },

  remove() {
    this.el.sceneEl.systems["personal-space-bubble"].unregisterInvader(this);
  },

  setInvading(invading) {
    if (this.targetMaterial) {
      this.targetMaterial.opacity = invading ? 0.3 : 1;
      this.targetMaterial.transparent = invading;
    } else {
      this.el.object3D.visible = !invading;
    }
    this.invading = invading;
  }
});

AFRAME.registerComponent("personal-space-bubble", {
  schema: {
    radius: { type: "number", default: 0.8 },
    debug: { default: false }
  },
  init() {
    this.system.registerBubble(this);
    if (this.system.data.debug || this.data.debug) {
      this.el.object3D.add(createSphereGizmo(this.data.radius));
    }
  },

  update() {
    this.radiusSquared = this.data.radius * this.data.radius;
  },

  remove() {
    this.system.unregisterBubble(this);
  }
});