import RAPIER from '@dimforge/rapier3d-compat';
import * as RE from 'rogue-engine';
import * as THREE from 'three';
import RogueRapier from '../../Lib/RogueRapier';
import RapierBody from '../RapierBody.re';

export default abstract class RapierCollider extends RE.Component {
  initialized = false;
  collider: RAPIER.Collider;
  body: RAPIER.RigidBody;
  bodyComponent: RapierBody;

  localPos: THREE.Vector3 = new THREE.Vector3();
  worldPos = new THREE.Vector3();

  localRot = new THREE.Quaternion();
  worldQuaternion = new THREE.Quaternion();

  @RE.props.checkbox() isSensor = false;
  @RE.props.checkbox() collisionEvents = false;

  static findByShape(shape: RAPIER.Collider) {
    let shapeComponent: undefined | RapierCollider;

    RE.traverseComponents(component => {
      if (shapeComponent) return;

      if (component instanceof RapierCollider && component.collider === shape) {
        shapeComponent = component;
      }
    });

    return shapeComponent;
  }

  init() {
    this.bodyComponent = this.getBodyComponent(this.object3d) as RapierBody;

    if (!this.bodyComponent) return;
    if (!this.bodyComponent.body) return;

    this.body = this.bodyComponent.body;

    this.createShape();

    this.collider.setSensor(this.isSensor);
    this.collisionEvents && this.collider.setActiveEvents(RAPIER.ActiveEvents.COLLISION_EVENTS);

    this.setColliderPos();
    this.setColliderRot();

    this.initialized = true;
  }

  setColliderPos() {
    this.object3d.updateWorldMatrix(true, true);
    this.object3d.getWorldPosition(this.worldPos);

    this.collider.setTranslation(this.worldPos);
  }

  setColliderRot() {
    this.object3d.updateWorldMatrix(true, true);
    this.object3d.getWorldQuaternion(this.worldQuaternion);

    this.collider.setRotation(this.worldQuaternion);
  }

  beforeUpdate(): void {
    if (!RogueRapier.initialized) return;
    if (!this.initialized) this.init();
    if (!this.collider) return;

    this.setColliderPos();
    this.setColliderRot();
  }

  onDisabled() {
    RogueRapier.world.removeCollider(this.collider, false);
  }

  onBeforeObjectRemoved() {
    RogueRapier.world.removeCollider(this.collider, false);
  }

  getBodyComponent(object3d: THREE.Object3D): RapierBody | undefined {
    const bodyComponent = RE.getComponent(RapierBody, object3d);

    if (bodyComponent) {
      return bodyComponent;
    }

    if (!object3d.parent) return;

    return this.getBodyComponent(object3d.parent as THREE.Object3D);
  }

  protected createShape(): void {};
}
