package wow.doge.mygame.game.controls

import scala.concurrent.Future

import com.jme3.math.FastMath
import com.jme3.math.Quaternion
import com.jme3.math.Vector3f
import com.jme3.renderer.RenderManager
import com.jme3.renderer.ViewPort
import com.jme3.scene.Node
import com.jme3.scene.Spatial
import com.jme3.scene.control.AbstractControl
import monix.execution.Ack
import monix.execution.Cancelable
import monix.execution.Scheduler
import monix.reactive.Observable
import monix.reactive.Observer
import wow.doge.mygame.game.subsystems.input.PlayerCameraInput

/**
  * A very low level (and error prone) camera movement control implementation.
  * Not used currently
  *
  * @param rotationBuf
  * @param obs
  * @param rotateFn
  * @param s
  */
class CameraMovementControl(
    rotationBuf: Quaternion,
    obs: Observable[PlayerCameraInput],
    rotateFn: Quaternion => Unit
)(implicit s: Scheduler)
    extends AbstractControl {
  private var _event: PlayerCameraInput = null
  private var _subscriptionToken: Cancelable = null

  private val sink = new Observer[PlayerCameraInput] {

    override def onNext(event: PlayerCameraInput): Future[Ack] = {
      _event = event
      Ack.Continue
    }

    override def onError(ex: Throwable): Unit = {}

    override def onComplete(): Unit = {}

  }

  override def controlUpdate(tpf: Float): Unit =
    if (_event != null) {
      _event match {
        case PlayerCameraInput.CameraRotateLeft =>
          val rot = rotationBuf
            .fromAngleAxis(1 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y)
          rotateFn(rot)
        case PlayerCameraInput.CameraRotateRight =>
          val rot = rotationBuf
            .fromAngleAxis(-1 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y)
          rotateFn(rot)
        case PlayerCameraInput.CameraRotateUp =>
          val rot = rotationBuf
            .fromAngleAxis(-1 * FastMath.DEG_TO_RAD, Vector3f.UNIT_X)
          rotateFn(rot)
        case PlayerCameraInput.CameraRotateDown =>
          val rot = rotationBuf
            .fromAngleAxis(1 * FastMath.DEG_TO_RAD, Vector3f.UNIT_X)
          rotateFn(rot)
      }
      _event = null
    }

  override def controlRender(
      x$1: RenderManager,
      x$2: ViewPort
  ): Unit = {}
  override def setSpatial(spatial: Spatial): Unit = {
    super.setSpatial(spatial)
    if (this.spatial != null)
      _subscriptionToken = obs.subscribe(sink)
    else {
      _subscriptionToken.cancel()
      _subscriptionToken = null
    }
  }

}

class FollowControl(playerNode: Node) extends AbstractControl {
  override def controlUpdate(tpf: Float): Unit =
    this.spatial.setLocalTranslation(playerNode.getLocalTranslation())
  override def controlRender(
      rm: RenderManager,
      vp: ViewPort
  ): Unit = {}
}
