import TouchEventsHandler from "../utils/touch-events-handler.js";
import MouseEventsHandler from "../utils/mouse-events-handler.js";
import GearVRMouseEventsHandler from "../utils/gearvr-mouse-events-handler.js";
import ActionEventHandler from "../utils/action-event-handler.js";

AFRAME.registerComponent("input-configurator", {
  schema: {
    cursorController: { type: "selector" },
    gazeTeleporter: { type: "selector" },
    camera: { type: "selector" },
    playerRig: { type: "selector" },
    leftController: { type: "selector" },
    rightController: { type: "selector" },
    leftControllerRayObject: { type: "string" },
    rightControllerRayObject: { type: "string" },
    gazeCursorRayObject: { type: "string" }
  },

  init() {
    this.inVR = this.el.sceneEl.is("vr-mode");
    this.isMobile = AFRAME.utils.device.isMobile();
    this.eventHandlers = [];
    this.controllerQueue = [];
    this.cursor = this.data.cursorController.components["cursor-controller"];
    this.gazeTeleporter = this.data.gazeTeleporter.components["teleport-controls"];
    this.cameraController = this.data.camera.components["pitch-yaw-rotator"];
    this.playerRig = this.data.playerRig;
    this.handedness = "right";

    this.onEnterVR = this.onEnterVR.bind(this);
    this.onExitVR = this.onExitVR.bind(this);
    this.handleControllerConnected = this.handleControllerConnected.bind(this);
    this.handleControllerDisconnected = this.handleControllerDisconnected.bind(this);

    this.configureInput();
  },

  play() {
    this.el.sceneEl.addEventListener("controllerconnected", this.handleControllerConnected);
    this.el.sceneEl.addEventListener("controllerdisconnected", this.handleControllerDisconnected);
    this.el.sceneEl.addEventListener("enter-vr", this.onEnterVR);
    this.el.sceneEl.addEventListener("exit-vr", this.onExitVR);
  },

  pause() {
    this.el.sceneEl.removeEventListener("controllerconnected", this.handleControllerConnected);
    this.el.sceneEl.removeEventListener("controllerdisconnected", this.handleControllerDisconnected);
    this.el.sceneEl.removeEventListener("enter-vr", this.onEnterVR);
    this.el.sceneEl.removeEventListener("exit-vr", this.onExitVR);
  },

  onEnterVR() {
    this.inVR = true;
    this.tearDown();
    this.configureInput();
    this.updateController();
  },

  onExitVR() {
    this.inVR = false;
    this.tearDown();
    this.configureInput();
    this.updateController();
  },

  tearDown() {
    this.eventHandlers.forEach(h => h.tearDown());
    this.eventHandlers = [];
    this.actionEventHandler = null;
    if (this.lookOnMobile) {
      this.lookOnMobile.el.removeAttribute("look-on-mobile");
      this.lookOnMobile = null;
    }
    this.cursorRequiresManagement = false;
  },

  addLookOnMobile() {
    const onAdded = e => {
      if (e.detail.name !== "look-on-mobile") return;
      this.lookOnMobile = this.el.sceneEl.components["look-on-mobile"];
    };
    this.el.sceneEl.addEventListener("componentinitialized", onAdded);
    // This adds look-on-mobile to the scene
    this.el.sceneEl.setAttribute("look-on-mobile", "camera", this.data.camera);
  },

  configureInput() {
    this.actionEventHandler = new ActionEventHandler(this.el.sceneEl, this.cursor);
    this.eventHandlers.push(this.actionEventHandler);

    if (this.inVR) {
      this.cameraController.pause();
      this.cursorRequiresManagement = true;
      this.cursor.el.setAttribute("cursor-controller", "near", 0);
      if (this.isMobile) {
        this.eventHandlers.push(new GearVRMouseEventsHandler(this.cursor, this.gazeTeleporter));
      } else {
        this.eventHandlers.push(new MouseEventsHandler(this.cursor, this.cameraController));
      }
    } else {
      this.cameraController.play();
      if (this.isMobile) {
        this.cursor.setCursorVisibility(false);
        this.eventHandlers.push(new TouchEventsHandler(this.cursor, this.cameraController, this.cursor.el));
        this.addLookOnMobile();
      } else {
        this.eventHandlers.push(new MouseEventsHandler(this.cursor, this.cameraController));
        this.cursor.el.setAttribute("cursor-controller", "near", 0.3);
      }
    }
  },

  tick() {
    if (this.cursorRequiresManagement && this.controller) {
      this.actionEventHandler.manageCursorEnabled();
    }
  },

  handleControllerConnected: function(e) {
    const data = {
      controller: e.target,
      handedness: e.detail.component.data.hand
    };

    if (data.handedness === this.handedness) {
      this.controllerQueue.unshift(data);
    } else {
      this.controllerQueue.push(data);
    }

    this.updateController();
  },

  handleControllerDisconnected: function(e) {
    for (let i = 0; i < this.controllerQueue.length; i++) {
      if (e.target === this.controllerQueue[i].controller) {
        this.controllerQueue.splice(i, 1);
        this.updateController();
        return;
      }
    }
  },

  updateController: function() {
    this.cursor.setCursorVisibility(true);
    const controllerData = this.controllerQueue.length ? this.controllerQueue[0] : null;

    if (controllerData) {
      this.controller = controllerData.controller;
      this.actionEventHandler.setHandThatAlsoDrivesCursor(this.controller);
    } else {
      this.controller = null;
      this.actionEventHandler.setHandThatAlsoDrivesCursor(null);
    }

    let rayObject;
    let drawLine;
    if (controllerData && this.inVR) {
      rayObject =
        controllerData.handedness === "left" ? this.data.leftControllerRayObject : this.data.rightControllerRayObject;
      drawLine = true;
    } else if (this.inVR) {
      rayObject = this.data.gazeCursorRayObject;
      drawLine = false;
    } else {
      rayObject = null;
      drawLine = false;
    }
    this.cursor.el.setAttribute("cursor-controller", { rayObject, drawLine });
  }
});