package wow.doge.mygame.game.entities.player

import scala.concurrent.duration._

import akka.actor.typed.ActorRef
import akka.actor.typed.Behavior
import akka.actor.typed.LogOptions
import akka.actor.typed.PostStop
import akka.actor.typed.SupervisorStrategy
import akka.actor.typed.scaladsl.ActorContext
import akka.actor.typed.scaladsl.Behaviors
import com.typesafe.scalalogging.Logger
import org.slf4j.event.Level
import wow.doge.mygame.Dispatchers
import wow.doge.mygame.game.entities.PlayerCameraActor
import wow.doge.mygame.game.entities.PlayerCameraEventListener
import wow.doge.mygame.game.entities.PlayerMovementEventListener
import wow.doge.mygame.implicits._
import wow.doge.mygame.subsystems.events.EventBus
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
import wow.doge.mygame.subsystems.events.PlayerEvent
import wow.doge.mygame.subsystems.events.TickEvent
import wow.doge.mygame.subsystems.events.TickEvent.RenderTick
import wow.doge.mygame.subsystems.movement.ImMovementActor

object PlayerActorSupervisor2 {
  sealed trait Command
  case object Tick extends Command
  sealed trait Movement extends Command
  final case class MoveLeft(pressed: Boolean) extends Movement
  final case class MoveUp(pressed: Boolean) extends Movement
  final case class MoveRight(pressed: Boolean) extends Movement
  final case class MoveDown(pressed: Boolean) extends Movement
  case object Jump extends Movement
  sealed trait Camera
  case object RotateLeft extends Camera
  case object RotateRight extends Camera
  case object RotateUp extends Camera
  case object RotateDown extends Camera
  class Props(
      val playerEventBus: GameEventBus[PlayerEvent],
      val tickEventBus: GameEventBus[TickEvent],
      val imMovementActorBehavior: Behavior[ImMovementActor.Command],
      val playerCameraActorBehavior: Behavior[PlayerCameraActor.Command]
  ) {
    def behavior =
      Behaviors.logMessages(
        LogOptions()
          .withLevel(Level.TRACE)
          .withLogger(
            Logger[PlayerActorSupervisor2].underlying
          ),
        Behaviors
          .setup[Command] { ctx =>
            ctx.log.infoP("Starting PlayerActor")

            // spawn children actors
            val movementActor =
              ctx.spawn(
                Behaviors
                  .supervise(imMovementActorBehavior)
                  .onFailure[Exception](
                    SupervisorStrategy.restart.withLimit(2, 100.millis)
                  ),
                "playerMovementActor",
                Dispatchers.jmeDispatcher
              )

            val playerCameraActor =
              ctx.spawn(
                playerCameraActorBehavior,
                "playerCameraActor",
                Dispatchers.jmeDispatcher
              )

            val playerCameraEl = ctx.spawn(
              PlayerCameraEventListener(playerCameraActor),
              "playerCameraActorEl"
            )

            val playerMovementEl = ctx.spawn(
              Behaviors
                .supervise(PlayerMovementEventListener(movementActor))
                .onFailure[Exception](
                  SupervisorStrategy.restart.withLimit(2, 100.millis)
                ),
              "playerMovementEventHandler"
            )

            val renderTickEl = {
              val behavior: Behavior[RenderTick.type] =
                Behaviors.setup(ctx =>
                  Behaviors
                    .receiveMessage[RenderTick.type] {
                      case RenderTick =>
                        movementActor ! ImMovementActor.Tick
                        // playerCameraActor ! PlayerCameraActor.Tick
                        Behaviors.same
                    }
                    .receiveSignal {
                      case (_, PostStop) =>
                        ctx.log.infoP("stopped")
                        Behaviors.same
                    }
                )
              ctx.spawn(behavior, "playerMovementTickListener")
            }

            //init listeners
            playerEventBus ! EventBus.Subscribe(playerMovementEl)
            tickEventBus ! EventBus.Subscribe(renderTickEl)
            playerEventBus ! EventBus.Subscribe(playerCameraEl)

            new PlayerActorSupervisor2(
              ctx,
              this,
              Children(movementActor)
            ).receive
          }
      )

  }

  case class Children(
      movementActor: ActorRef[ImMovementActor.Command]
  )
}
class PlayerActorSupervisor2(
    ctx: ActorContext[PlayerActorSupervisor2.Command],
    props: PlayerActorSupervisor2.Props,
    children: PlayerActorSupervisor2.Children
) {
  import PlayerActorSupervisor2._
  def receive =
    Behaviors
      .receiveMessage[Command] {
        case m @ MoveDown(pressed) =>
          //   children.movementActor ! m
          Behaviors.same
        case _ =>
          // children.movementActor ! ImMovementActor.MovedDown(true)
          Behaviors.same
      }
      .receiveSignal {
        case (_, PostStop) =>
          ctx.log.infoP("stopped")
          Behaviors.same
      }
}
