package wow.doge.mygame.subsystems.movement

import akka.actor.typed.Behavior
import akka.actor.typed.scaladsl.ActorContext
import akka.actor.typed.scaladsl.Behaviors
import com.jme3.math.Vector3f
import com.jme3.renderer.Camera
import com.softwaremill.quicklens._
import wow.doge.mygame.game.subsystems.movement.CanMove
import wow.doge.mygame.implicits._
import wow.doge.mygame.math.ImVector3f
import wow.doge.mygame.state.CardinalDirection

sealed trait RotateDir
object RotateDir {
  case object Left extends RotateDir
  case object Right extends RotateDir
}

object ImMovementActor {
  sealed trait Command
  // final case class Tick(tpf: Float) extends Command
  final case object Tick extends Command

  sealed trait Movement extends Command
  final case class MovedLeft(pressed: Boolean) extends Movement
  final case class MovedUp(pressed: Boolean) extends Movement
  final case class MovedRight(pressed: Boolean) extends Movement
  final case class MovedDown(pressed: Boolean) extends Movement
  final case object Jump extends Movement
  // final case object RotateRight extends Movement
  // final case object RotateLeft extends Movement

  final class Props[T: CanMove](
      val enqueueR: Function1[() => Unit, Unit],
      val movable: T,
      // playerMovementEventBus: ActorRef[
      //   EventBus.Command[PlayerMovementEvent]
      // ]
      val camera: Camera
  ) {
    def behavior: Behavior[Command] =
      Behaviors.setup(ctx => new ImMovementActor(ctx, this).receive(State()))
  }

  /**
    *  Internal state of the actor
    *
    * @param cardinalDir The four directions the character can move
    */
  final case class State(cardinalDir: CardinalDirection = CardinalDirection())

}

class ImMovementActor[T](
    ctx: ActorContext[ImMovementActor.Command],
    props: ImMovementActor.Props[T]
) {
  import ImMovementActor._
  import Methods._

  def receive(
      state: ImMovementActor.State
  )(implicit cm: CanMove[T]): Behavior[Command] =
    Behaviors.receiveMessage {
      case m: Movement =>
        m match {
          case MovedLeft(pressed) =>
            props.enqueueR(() => stopIfNotPressed(pressed, props.movable))
            receive(state = state.modify(_.cardinalDir.left).setTo(pressed))
          case MovedUp(pressed) =>
            props.enqueueR(() => stopIfNotPressed(pressed, props.movable))
            receive(state = state.modify(_.cardinalDir.up).setTo(pressed))
          case MovedRight(pressed) =>
            props.enqueueR(() => stopIfNotPressed(pressed, props.movable))
            receive(state = state.modify(_.cardinalDir.right).setTo(pressed))
          case MovedDown(pressed) =>
            props.enqueueR(() => stopIfNotPressed(pressed, props.movable))
            receive(state = state.modify(_.cardinalDir.down).setTo(pressed))
          case Jump =>
            props.enqueueR(() => cm.jump(props.movable))
            Behaviors.same
        }

      case Tick =>
        val walkDir =
          getDirection2(state.cardinalDir, ctx.log.trace)
        // if (walkDir != ImVector3f.ZERO) {
        val tmp = walkDir * 25f * (1f / 144)
        props.enqueueR { () =>
          cm.move(props.movable, tmp)
        }
        // }
        Behaviors.same
    }

  def getDirection2(cardinalDir: CardinalDirection, debug: String => Unit) = {
    val camDir =
      props.camera.getDirection().clone().normalizeLocal.multLocal(0.6f)
    val camLeft = props.camera.getLeft().clone().normalizeLocal.multLocal(0.4f)
    val dir = cardinalDir
    val walkDir = {
      val mutWalkDir = new Vector3f()
      if (dir.up) {
        debug("up")
        mutWalkDir += camDir
      }
      if (dir.left) {
        debug("left")
        mutWalkDir += camLeft
      }
      if (dir.right) {
        debug("right")
        mutWalkDir += -camLeft
      }
      if (dir.down) {
        debug("down")
        mutWalkDir += -camDir
      }
      mutWalkDir.immutable
    }
    walkDir
  }
}
object Methods {
  def getDirection(cardinalDir: CardinalDirection, trace: String => Unit) = {
    val zero = ImVector3f.ZERO
    val dir = cardinalDir
    val walkDir = {
      val mutWalkDir = new Vector3f()
      if (dir.left) {
        trace("left")
        mutWalkDir += zero +=: new Vector3f(-1, 0, 0)
      }
      if (dir.right) {
        trace("right")
        mutWalkDir += zero +=: new Vector3f(1, 0, 0)
      }
      if (dir.up) {
        trace("up")
        mutWalkDir += zero +=: new Vector3f(0, 0, -1)
      }
      if (dir.down) {
        trace("down")
        mutWalkDir += zero +=: new Vector3f(0, 0, 1)
      }
      mutWalkDir.immutable
    }
    walkDir
  }

  def stopIfNotPressed[T](pressed: Boolean, movable: T)(implicit
      cm: CanMove[T]
  ) =
    if (!pressed) cm.stop(movable)
}

// props.app
//   .enqueueScala { () =>
//     1
//   }
//   .map(println)(scala.concurrent.ExecutionContext.global)
// monix.eval.Task
//   .fromFuture(
//     props.app
//       .enqueueScala { () =>
//         1
//       }
//   )
//   .flatMap(i => monix.eval.Task(println(i)))
//   .runToFuture(monix.execution.Scheduler.Implicits.global)
