package wow.doge.mygame.game

import scala.collection.immutable.Queue

import com.jme3.app.SimpleApplication
import com.jme3.app.state.AppState
import monix.bio.Task
import monix.execution.CancelableFuture
import monix.execution.Scheduler
import monix.execution.atomic.Atomic
import monix.execution.{CancelablePromise => Promise}
import monix.reactive.MulticastStrategy
import monix.reactive.Observable
import monix.reactive.subjects.ConcurrentSubject
import wow.doge.mygame.executors.GUIExecutorService
import wow.doge.mygame.executors.Schedulers

class SimpleAppExt(
    schedulers: Schedulers,
    appStates: AppState*
) extends SimpleApplication(appStates: _*) {
  import SimpleAppExt._

  /**
    * A non blocking synchronized queue using an immutable scala queue and monix's atomic class
    */
  private lazy val taskQueue2 = Atomic(Queue.empty[MyTask[_]])

  private val tickSubject =
    ConcurrentSubject[Float](multicast = MulticastStrategy.publish)(
      schedulers.async
    )

  def tickObservable: Observable[Float] = tickSubject

  override def simpleInitApp(): Unit = {}

  override def simpleUpdate(tpf: Float): Unit = {
    tickSubject.onNext(tpf)
  }

  override def stop(): Unit = {
    tickSubject.onComplete()
    super.stop()
  }

  def enqueueScala[T](cb: () => T): CancelableFuture[T] = {
    val p = Promise[T]()
    taskQueue2.transform(_ :+ MyTask(p, cb))
    p.future
  }

  def enqueueL[T](cb: () => T): Task[T] =
    Task.deferFuture(enqueueScala(cb))

  override protected def runQueuedTasks(): Unit = {
    taskQueue2.transform { current =>
      current.dequeueOption.fold(current) {
        case (MyTask(p, cb), updated) =>
          p.success(cb())
          updated
      }
    }

    super.runQueuedTasks()
  }

  object JMEExecutorService extends GUIExecutorService {
    override def execute(command: Runnable): Unit =
      enqueue(command)
  }

  lazy val scheduler = Scheduler(JMEExecutorService)
}
object SimpleAppExt {
  private[game] case class MyTask[T](p: Promise[T], cb: () => T)
}

// val ship = ed.createEntity()
// val mbState = stateManager().state[EntityDataState]().map(_.getEntityData())
// val mbState = Try(
//   stateManager()
//     .state[TestAppState]()
//     .entity
// ).toOption.flatten.toRight(println("empty"))
// // .flatMap(_.entity)
// val x = mbState.flatMap(
//   _.query
//     .filter[TestComponent]("name", new Object())
//     // .filterOr[TestEntity](
//     //   Filters
//     //     .fieldEquals(classOf[TestEntity], "", null)
//     // )
//     .component[Tag]()
//     .component[TestComponent]()
//     .result
//     .toRight(println("failed"))
// )

// rootNode
//   .child(geom)
//   .child(geom)
//   .child(geom)
//   .child(geom)
//   .child(geom)
//   .child(geom)
//   .child(geom)
//   .child(geom)
// Future(println("hello"))(jmeEC(this))
// val wbActor: Future[ActorRef[Greeter.Greet]] = actorSystem.ask(
//   SpawnProtocol.Spawn(
//     behavior = Greeter(),
//     name = "listener",
//     DispatcherSelector.fromConfig("jme-dispatcher"),
//     _
//   )
// )

// wbActor.map(a => a.ask(Greeter.Greet("hello", _)).map(println))

// Task(Promise[T]()).flatMap { p =>
//   Task(taskQueue2.transform(_ :+ MyTask(p, cb))) >>
//     Task.fromCancelablePromise(p)
// }
// Task.fromCancelablePromise {
//   val p = Promise[T]()
//   taskQueue2.transform(_ :+ MyTask(p, cb))
//   p
// }
