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

export default class RapierMovementController extends RE.Component {
  initialized = false
  characterController: RAPIER.KinematicCharacterController
  rapierConfig: RapierConfig

  @RE.props.num() speed: number = 1
  @RE.props.num() rotationSpeed: number = 1
  // @RE.props.num() jumpStrength: number = 10
  @RE.props.checkbox() useCameraForward: boolean = true
  @RE.props.checkbox() useObjectForward: boolean = false
  cameraVectorCalculator: GetForwardVector
  objectVectorCalculator: GetForwardVector
  bodyComponent: RapierBody
  velocity: THREE.Vector3 = new THREE.Vector3(0,0,0)

  @RE.props.vector3() drag: THREE.Vector3 = new THREE.Vector3(0.002, 0.002, 0.002)



  // private jumpVector: RAPIER.Vector3 = new RAPIER.Vector3(0,1,0)
  private scaledVelocity: THREE.Vector3 = new THREE.Vector3(0,0,0)
  private rapierScaledVelocity: RAPIER.Vector3 = new RAPIER.Vector3(0,0,0)

  elapsed = 0

  @RE.props.checkbox() thrust = false
  @RE.props.checkbox() brake = false
  @RE.props.checkbox() strafeLeft = false
  @RE.props.checkbox() strafeRight = false
  @RE.props.checkbox() rotateLeft = false
  @RE.props.checkbox() rotateRight = false

  awake() {
    this.bodyComponent = RE.getComponent(RapierBody, this.object3d) as RapierBody
  }

  start() {
    this.cameraVectorCalculator = new GetForwardVector(RE.Runtime.camera)
    this.objectVectorCalculator = new GetForwardVector(this.object3d)
  }

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

  init() {
    this.characterController = RogueRapier.world.createCharacterController(0.1)
    // this.characterController.enableAutostep(this.autostepMaxHeight, this.autostepMinWidth, this.autostepIncludeDynamicBodies)
    // this.characterController.enableSnapToGround(this.snapToGroundDistance)
    this.characterController.setCharacterMass(100)
    this.characterController.setApplyImpulsesToDynamicBodies(true)
    // this.characterController.setSlideEnabled(this.slideEnabled)
  }

  update() {
    if(!this.bodyComponent.body) {
      return
    }
    this.elapsed += RE.Runtime.deltaTime

    // if(direction.x != 0) {
    // this.moveForward(direction.x * this.speed)
    // }
    // if(direction.y != 0) {
    // this.moveRight(direction.y * this.speed)
    // }

    // if(direction.z != 0) {
    //   const floorCheckComponent = RE.getComponent(FloorCheckComponent, this.object3d)
    //   if(floorCheckComponent) {
    //     if(floorCheckComponent.isOnFloor) {
    //       this.jumpVector.y = this.jumpStrength
    //       this.bodyComponent.body.applyImpulse(this.jumpVector, true)
    //     }
    //   }
    // }

    
    if(this.thrust) {
      this.moveForward(1 * this.speed)
    }
    if(this.brake) {
      this.moveForward(-1 * this.speed)
    }

    if(this.rotateLeft) {
      this.rotate(1 * this.rotationSpeed)
    }

    if(this.rotateRight) {
      this.rotate(-1 * this.rotationSpeed)
    }

    if(this.strafeLeft) {
      this.moveRight(-1 * this.speed)
    }

    if(this.strafeRight) {
      this.moveRight(1 * this.speed)
    }

    const fixedStep = RE.Runtime.deltaTime
    const nextPosition = this.bodyComponent.body.translation()
    const nextVelocity = this.convertRapierToThreeVec3(this.bodyComponent.body.linvel())

    nextVelocity.x *= (1 - this.drag.x)
    nextVelocity.y *= (1 - this.drag.y)
    nextVelocity.z *= (1 - this.drag.z)

    this.velocity.x *= (1 - this.drag.x)
    this.velocity.y *= (1 - this.drag.x)
    this.velocity.z *= (1 - this.drag.x)

    nextVelocity.x += this.velocity.x
    nextVelocity.y += this.velocity.y
    nextVelocity.z += this.velocity.z
    
   

    this.characterController.computeColliderMovement(
      this.bodyComponent.body.collider(0),
      nextVelocity.clone().multiplyScalar(fixedStep),
    )

    const characterMovement = this.characterController.computedMovement()

    nextPosition.x += characterMovement.x
    nextPosition.y += characterMovement.y
    // nextPosition.y = 0
    nextPosition.z += characterMovement.z

    this.bodyComponent.body.setNextKinematicTranslation(nextPosition)

    this.thrust = false
    this.brake = false
    this.strafeLeft = false
    this.strafeRight = false
    this.rotateLeft = false
    this.rotateRight = false
  }

  moveForward(distance) {

    let forwardVector;
    if(this.useCameraForward) {
      forwardVector = this.cameraVectorCalculator.getForward()      
      let euler = new THREE.Euler(0,0,0, 'XZY')
      euler.setFromQuaternion(RE.Runtime.camera.quaternion.normalize())
      euler.x = 0
      euler.z = 0
      let newRotation = new THREE.Quaternion()
      newRotation.setFromEuler(euler)
      const nextRotation = this.convertRapierToThreeQuat(this.bodyComponent.body.rotation())
      nextRotation.slerp(newRotation, this.rotationSpeed * RE.Runtime.deltaTime)
      this.bodyComponent.body.setNextKinematicRotation(nextRotation)
    } else {
      forwardVector = this.objectVectorCalculator.getForward()
    }
    
    this.scaledVelocity.set(0,0,0)
    this.scaledVelocity.addScaledVector(forwardVector, distance)

    this.velocity.x += this.scaledVelocity.x
    this.velocity.y += this.scaledVelocity.y
    this.velocity.z += this.scaledVelocity.z
  }

  moveRight(distance) {
    let rightVector;
    if(this.useCameraForward) {
      rightVector = this.cameraVectorCalculator.getRight()

      let euler = new THREE.Euler(0,0,0, 'XZY')
      euler.setFromQuaternion(RE.Runtime.camera.quaternion.normalize())
      euler.x = 0
      euler.z = 0
      let newRotation = new THREE.Quaternion()
      newRotation.setFromEuler(euler)
      const nextRotation = this.convertRapierToThreeQuat(this.bodyComponent.body.rotation())
      nextRotation.slerp(newRotation, this.rotationSpeed * RE.Runtime.deltaTime)
      this.bodyComponent.body.setNextKinematicRotation(nextRotation)

    } else {
      rightVector = this.objectVectorCalculator.getRight()
    }

    this.scaledVelocity.set(0,0,0)
      this.scaledVelocity.addScaledVector(rightVector, distance)
      // this.rapierScaledVelocity.x = this.scaledVelocity.x
      // this.rapierScaledVelocity.y = this.scaledVelocity.y
      // this.rapierScaledVelocity.z = this.scaledVelocity.z
      // this.bodyComponent.body.applyImpulse(this.rapierScaledVelocity, true)
      this.velocity.x += this.scaledVelocity.x
      this.velocity.y += this.scaledVelocity.y
      this.velocity.z += this.scaledVelocity.z
    
  }

  rotate(amount) {
      const nextRotation = this.convertRapierToThreeQuat(this.bodyComponent.body.rotation())
      const newRotation = this.quaternionFromAxisAngle(this.object3d.up, amount)
      nextRotation.multiply(newRotation)
      this.bodyComponent.body.setNextKinematicRotation(nextRotation)

  }

  
  convertRapierToThreeQuat(oldQuaternion: RAPIER.Quaternion) {
    return new THREE.Quaternion(oldQuaternion.x, oldQuaternion.y, oldQuaternion.z, oldQuaternion.w)
  }

  convertRapierToThreeVec3(oldVector: RAPIER.Vector3) {
    return new THREE.Vector3(oldVector.x, oldVector.y, oldVector.z)
  }

  quaternionFromAxisAngle(axis, rotationRadians) {
    let factor = Math.sin(rotationRadians / 2)
    let x = axis.x * factor
    let y = axis.y * factor
    let z = axis.z * factor
    let w = Math.cos(rotationRadians / 2)
    let normalized = new THREE.Quaternion(x, y, z, w).normalize()
    return new THREE.Quaternion(normalized.x, normalized.y, normalized.z, normalized.w)
  }
}



RE.registerComponent(RapierMovementController);
