package wow.doge.mygame.game.subsystems.movement

import com.jme3.bullet.control.BetterCharacterControl
import com.jme3.math.FastMath
import com.jme3.math.Quaternion
import com.jme3.math.Vector3f
import com.jme3.scene.Spatial
import com.typesafe.scalalogging.LazyLogging
import wow.doge.mygame.implicits._
import wow.doge.mygame.math.ImVector3f
import wow.doge.mygame.subsystems.movement.RotateDir

trait CanMove[-A] {
  // def getDirection(cam: Camera, cardinalDir: CardinalDirection): ImVector3f
  def move(inst: A, direction: ImVector3f, speedFactor: Float): Unit
  def location(inst: A): ImVector3f
  def jump(inst: A): Unit
  def stop(inst: A): Unit
  def rotate(inst: A, rotateDir: RotateDir): Unit
}

object CanMove {
  implicit val implCanMoveForBetterCharacterControl =
    new CanMove[BetterCharacterControl] {
      override def move(
          inst: BetterCharacterControl,
          direction: ImVector3f,
          speedFactor: Float
      ): Unit = {
        val dir = direction.mutable.normalizeLocal()
        inst.setViewDirection(dir.negate())
        inst.setWalkDirection(dir.mult(speedFactor))
      }
      override def location(inst: BetterCharacterControl) =
        inst.getSpatial().getLocalTranslation().immutable
      override def jump(inst: BetterCharacterControl): Unit = inst.jump()
      override def rotate(
          inst: BetterCharacterControl,
          rotateDir: RotateDir
      ): Unit = {
        val q =
          rotateDir match {
            case RotateDir.Left =>
              new Quaternion()
                .fromAngleNormalAxis(5 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y)
            case RotateDir.Right =>
              new Quaternion()
                .fromAngleAxis(-5 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y)
          }

        val tmp = new Vector3f()
        inst.getViewDirection(tmp)
        inst.setViewDirection(q.mult(tmp))
      }
      override def stop(inst: BetterCharacterControl) =
        inst.setWalkDirection(Vector3f.ZERO)
    }

  implicit val implCanMoveForSpatial = new CanMove[Spatial] with LazyLogging {
    override def move(
        inst: Spatial,
        direction: ImVector3f,
        speedFactor: Float = 1f
    ): Unit =
      inst.move(direction.mutable multLocal speedFactor)
    override def location(inst: Spatial) =
      inst.getLocalTranslation().immutable
    override def jump(inst: Spatial): Unit =
      logger.warn("`Jump` is not implemented for type `Spatial`")
    override def rotate(inst: Spatial, rotateDir: RotateDir): Unit =
      rotateDir match {
        case RotateDir.Left  => inst.rotate(0, -0.01f, 0)
        case RotateDir.Right => inst.rotate(0, 0.01f, 0)
      }
    override def stop(inst: Spatial) = { /*not required*/ }
  }
}
