package wow.doge.mygame.game

import scala.concurrent.duration._

import akka.actor.typed.ActorRef
import akka.actor.typed.PostStop
import akka.actor.typed.SpawnProtocol
import akka.actor.typed.SupervisorStrategy
import akka.actor.typed.scaladsl.Behaviors
import monix.execution.CancelableFuture
import monix.execution.CancelablePromise
import wow.doge.mygame.game.TickGenerator.Send
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.TickEvent
import wow.doge.mygame.utils.GenericTimerActor

object GameAppActor {

  sealed trait Command
  case object Start extends Command
  case object Pause extends Command
  case object Ping extends Command
  case class Stop(stopSignal: ActorRef[CancelableFuture[Unit]]) extends Command
  case class GetSpawnProtocol(
      replyTo: ActorRef[ActorRef[SpawnProtocol.Command]]
  ) extends Command

  case class Props(tickEventBus: GameEventBus[TickEvent]) {
    def behavior =
      Behaviors.setup[Command] { ctx =>
        ctx.log.infoP("Hello from GameAppActor")
        val renderTickGenerator =
          ctx.spawnN(
            Behaviors
              .supervise(renderTickGeneratorBehavior)
              .onFailure[Exception](
                SupervisorStrategy.restart.withLimit(2, 100.millis)
              )
          )

        val tickGeneratorTimer = ctx.spawnN(
          GenericTimerActor
            .Props(renderTickGenerator, TickGenerator.Send, 10.millis)
            .behavior
        )

        val sp = ctx.spawn(SpawnProtocol(), "gameSpawnProtocol")

        ctx.spawn(
          GenericTimerActor
            .Props(ctx.self, Ping, 1000.millis)
            .behavior,
          "pingTimer"
        ) ! GenericTimerActor.Start

        val stopPromise = CancelablePromise[Unit]()

        Behaviors
          .receiveMessage[Command] {
            case Start =>
              tickGeneratorTimer ! GenericTimerActor.Start
              Behaviors.same
            case Pause =>
              tickGeneratorTimer ! GenericTimerActor.Stop
              Behaviors.same
            case Stop(replyTo) =>
              ctx.log.infoP("Received stop")
              tickGeneratorTimer ! GenericTimerActor.Stop
              replyTo ! stopPromise.future
              Behaviors.stopped
            case Ping =>
              ctx.log.debugP("ping")
              Behaviors.same
            case GetSpawnProtocol(replyTo) =>
              replyTo ! sp
              Behaviors.same

          }
          .receiveSignal {
            case (_, PostStop) =>
              stopPromise.success(())
              Behaviors.same
          }
      }

    val renderTickGeneratorBehavior =
      Behaviors.receiveMessage[TickGenerator.Command] {
        case Send =>
          tickEventBus ! EventBus.Publish(
            TickEvent.RenderTick,
            "tickGeneratorActor"
          )
          Behaviors.same
      }
  }
}

object TickGenerator {
  sealed trait Command
  case object Send extends Command
}
