package wow.doge.mygame.launcher

import cats.effect.Resource
import cats.effect.concurrent.Deferred
import cats.kernel.Eq
import javafx.application.Platform
import monix.bio.Task
import monix.bio.UIO
import monix.catnap.CancelableF
import monix.execution.CancelablePromise
import monix.reactive.Observable
import monix.{eval => me}
import scalafx.Includes._
import scalafx.application.JFXApp
import scalafx.application.JFXApp.PrimaryStage
import scalafx.scene.control.Button
import scalafx.stage.StageStyle
import wow.doge.mygame.executors.Schedulers.FxScheduler
import wow.doge.mygame.implicits.JavaFXMonixObservables._
import wow.doge.mygame.utils.IOUtils._
object Launcher {
  sealed trait LauncherResult
  object LauncherResult {
    case object LaunchGame extends LauncherResult
    case object Exit extends LauncherResult

    implicit val eqForLR = Eq.fromUniversalEquals[LauncherResult]
  }

  class Props(
      // val schedulers: Schedulers,
      val fxScheduler: FxScheduler,
      val signal: Deferred[Task, LauncherResult]
  ) {
    val create = UIO(new Launcher(this))
  }
}
class Launcher private (props: Launcher.Props) {
  import Launcher._

  private lazy val launchButton = new Button {
    text = "Launch"
  }

  private lazy val launchAction =
    launchButton
      .observableAction()
      .doOnNext(_ => toTask(props.signal.complete(LauncherResult.LaunchGame)))

  private lazy val exitButton = new Button {
    text = "Exit"
  }

  private lazy val exitAction =
    exitButton
      .observableAction()
      .doOnNext(_ => toTask(props.signal.complete(LauncherResult.Exit)))

  private lazy val _scene =
    DefaultUI.scene(launchButton, exitButton)

  private lazy val _stage = new PrimaryStage {
    scene = _scene
  }

  private def internal(startSignal: CancelablePromise[Unit]) =
    new JFXApp {
      stage = _stage
      stage.initStyle(StageStyle.Undecorated)
      // ResizeHelper.addResizeListener(stage)
      startSignal.success(())
    }

  private lazy val sceneDragObservable = {
    val mpo = _scene.observableMousePressed()
    val mdo = _scene.observableMouseDragged()

    mpo.concatMap(pressEvent =>
      mdo.doOnNext(dragEvent =>
        me.Task(pprint.log("emitted")) >>
          me.Task(
            _stage.setX(dragEvent.screenX - pressEvent.sceneX)
          ) >>
          me.Task(
            _stage.setY(
              dragEvent.screenY - pressEvent.sceneY
            )
          )
      )
    )
  }

  def init =
    Resource.make(for {
      _ <- Task(Platform.setImplicitExit(false))
      startSignal <- Task(CancelablePromise[Unit]())
      delegate <- Task(internal(startSignal))
      combinedFib <-
        Task
          .parZip2(
            Task(delegate.main(Array.empty)),
            Task.fromCancelablePromise(startSignal) >> toIO(
              me.Task.parSequence(
                List(
                  sceneDragObservable.completedL,
                  Observable(launchAction, exitAction).merge
                    .doOnNext(_ =>
                      me.Task(delegate.stage.close())
                        .executeOn(props.fxScheduler.value)
                    )
                    .completedL
                )
              )
            )
          )
          .start
      _ <- Task.fromCancelablePromise(startSignal)
      c <- CancelableF[Task](combinedFib.cancel)
    } yield c)(_.cancel)

}
