package wow.doge.mygame.state


import scala.concurrent.duration.DurationInt

import com.jme3.input.InputManager
import com.jme3.input.KeyInput
import com.jme3.input.controls.ActionListener
import com.jme3.input.controls.KeyTrigger
import com.jme3.math.Vector3f

import akka.actor.typed.scaladsl.ActorContext
import akka.actor.typed.scaladsl.Behaviors
import akka.actor.typed.Behavior
import akka.actor.typed.ActorRef
import com.jme3.scene.Geometry
import akka.actor.typed.scaladsl.TimerScheduler

import wow.doge.mygame.implicits._

class PlayerMovementState2(
    movementActor: ActorRef[MovementActor.Command],
    movementActorTimer: ActorRef[MovementActorTimer.Command],
    imMovementActor: ActorRef[ImMovementActor.Command],
    geom: Geometry
) extends MyBaseState
    with ActionListener {

  protected lazy val mat = MyMaterial(
    assetManager = assetManager,
    path = "Common/MatDefs/Misc/Unshaded.j3md"
  )

  override protected[state] def onEnable(): Unit = {}

  override protected[state] def onDisable(): Unit = {}

  override protected def init(): Unit = {

    setupKeys(inputManager)
    geom.setMaterial(mat)
    rootNode.attachChild(geom)
    // movementActorTimer ! MovementActorTimer.Start(geom, cam)
    // movementActorTimer ! MovementActorTimer.Start
  }

//   def system =
//     simpleApp.getStateManager.getState(classOf[ActorSystemState]).system
//   def player = system.actorSelection("/user/player") //.resolveOne(1.second)

  var lastDir: Vector3f = Vector3f.UNIT_X

  override def update(tpf: Float) = {
    // val direction = new Vector3f()
    // direction.multLocal(10 * tpf)
    // if (direction.length() > 0f) {
    //   //   player ! PlayerMove(direction)
    //   lastDir = direction.normalize
    // }
    // if (shoot) {
    //   shoot = false
    //   //   player ! Shoot(lastDir)
    // }
    // movementActor ! MovementActor.Tick(tpf, geom, cam)
    imMovementActor ! ImMovementActor.Tick(tpf)
    // movementActorTimer ! MovementActorTimer.Update(tpf)
  }

  def setupKeys(inputManager: InputManager) = {

    inputManager
      .withMapping(
        "Left",
        // new KeyTrigger(KeyInput.KEY_A),
        new KeyTrigger(KeyInput.KEY_LEFT)
      )
      .withMapping(
        "Right",
        // new KeyTrigger(KeyInput.KEY_D),
        new KeyTrigger(KeyInput.KEY_RIGHT)
      )
    inputManager.addMapping(
      "Up",
      // new KeyTrigger(KeyInput.KEY_W),
      new KeyTrigger(KeyInput.KEY_UP)
    )
    inputManager.addMapping(
      "Down",
      // new KeyTrigger(KeyInput.KEY_S),
      new KeyTrigger(KeyInput.KEY_DOWN)
    )
    inputManager.addMapping(
      "Space",
      new KeyTrigger(KeyInput.KEY_SPACE),
      new KeyTrigger(KeyInput.KEY_H)
    )
    inputManager.addMapping(
      "Reset",
      new KeyTrigger(KeyInput.KEY_R),
      new KeyTrigger(KeyInput.KEY_RETURN)
    )
    inputManager
      .withListener(this, "Left")
      .withListener(this, "Right")
    inputManager.addListener(this, "Up")
    inputManager.addListener(this, "Down")
    inputManager.addListener(this, "Space")
    inputManager.addListener(this, "Reset")
  }

  def onAction(binding: String, value: Boolean, tpf: Float) =
    binding match {
      case "Left"  => imMovementActor ! ImMovementActor.MovedLeft(value)
      case "Right" => imMovementActor ! ImMovementActor.MovedRight(value)
      case "Up"    => imMovementActor ! ImMovementActor.MovedUp(value)
      case "Down"  => imMovementActor ! ImMovementActor.MovedDown(value)
      case "Space" =>
      case _       =>
    }

}

final case class CardinalDirection(
    left: Boolean = false,
    right: Boolean = false,
    up: Boolean = false,
    down: Boolean = false
)

object MovementActor {
  sealed trait Command
  // final case class Tick(tpf: Float, geom: Geometry, cam: Camera) extends 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 class Props(app: com.jme3.app.Application, geom: Geometry)

  /**
    *  Internal state of the actor
    *
    * @param cardinalDir Immutable, can be shared as is
    * @param walkDirection scratch space to avoid allocations on every tick. Do not share outside the actor
    */
  final case class State(
      cardinalDir: CardinalDirection = CardinalDirection(),
      walkDirection: Vector3f = Vector3f.UNIT_X
  )

  def apply(props: Props): Behavior[Command] =
    Behaviors.setup(ctx => new MovementActor(ctx, props).receive(State()))

}
class MovementActor(
    ctx: ActorContext[MovementActor.Command],
    props: MovementActor.Props
) {
  import MovementActor._
  import com.softwaremill.quicklens._
  def receive(state: MovementActor.State): Behavior[Command] =
    Behaviors.receiveMessage { msg =>
      msg match {
        case m: Movement =>
          m match {
            case MovedLeft(pressed) =>
              receive(state = state.modify(_.cardinalDir.left).setTo(pressed))
            case MovedUp(pressed) =>
              receive(state = state.modify(_.cardinalDir.up).setTo(pressed))
            case MovedRight(pressed) =>
              receive(state = state.modify(_.cardinalDir.right).setTo(pressed))
            case MovedDown(pressed) =>
              receive(state = state.modify(_.cardinalDir.down).setTo(pressed))
          }

        case Tick =>
          val camDir =
            props.app.getCamera.getDirection().clone().multLocal(0.6f)
          val camLeft = props.app.getCamera.getLeft().clone().multLocal(0.4f)
          val walkDir = state.walkDirection.set(0, 0, 0)
          // val walkDir = new Vector3f
          val dir = state.cardinalDir
          if (dir.up) {
            ctx.log.debug("up")
            // ctx.log.debug(Thread.currentThread().getName())
            // walkDir.addLocal(0, 0, -1)
            walkDir += camDir
          }
          if (dir.left) {
            ctx.log.debug("left")
            // walkDir.addLocal(-1, 0, 0)
            walkDir.addLocal(camLeft)
          }
          if (dir.right) {
            ctx.log.debug("right")
            // walkDir.addLocal(1, 0, 0)
            walkDir.addLocal(camLeft.negateLocal())
          }
          if (dir.down) {
            ctx.log.debug("down")
            walkDir.addLocal(camDir.negateLocal())
            // walkDir.addLocal(0, 0, 1)
          }
          // (dir.up, dir.down, dir.left, dir.right) match {
          //   case (true, false, true, false) =>
          //   case _                          =>
          // }

          walkDir.multLocal(2f)

          // walkDir.multLocal(100f)
          // .multLocal(tpf)

          // val v = props.geom.getLocalTranslation()
          // props.geom.setLocalTranslation(
          //   (v += walkDir)
          // )
          props.app.enqueue(new Runnable {
            override def run(): Unit = {
              // geom.setLocalTranslation(walkDir)

              val v = props.geom.getLocalTranslation()
              props.geom.setLocalTranslation(
                (v += walkDir)
              )
            }
          })
          Behaviors.same
        // receive(state = state.modify(_.walkDirection).setTo(walkDir))

      }
    }
}

object MovementActorTimer {
  sealed trait Command
  final case object Start extends Command
  final case object Update extends Command
  private case object Send extends Command
  case object TimerKey

  final case class Props(
      timers: TimerScheduler[MovementActorTimer.Command],
      target: ActorRef[MovementActor.Command]
  )
  final case class State()
  def apply(target: ActorRef[MovementActor.Command]) =
    Behaviors.withTimers[Command] { timers =>
      new MovementActorTimer(Props(timers, target)).idle()
    }
}
class MovementActorTimer(
    props: MovementActorTimer.Props
) {
  import MovementActorTimer._
  // import com.softwaremill.quicklens._

  def idle(): Behavior[Command] =
    Behaviors.receiveMessage { msg =>
      msg match {
        case Start =>
          props.timers.startTimerWithFixedDelay(
            Send,
            10.millis
          )
          active()
        case _ => Behaviors.unhandled
      }
    }

  def active(): Behavior[Command] =
    Behaviors.receiveMessage { msg =>
      msg match {
        case Update => active()
        case Send =>
          props.target ! MovementActor.Tick
          Behaviors.same
        case _ => Behaviors.unhandled

      }
    }
}
