package wow.doge.mygame.launcher

import cats.effect.Resource
import cats.effect.concurrent.Deferred
import cats.kernel.Eq
import javafx.application.Platform
import javafx.beans.value.ObservableValue
import monix.bio.Task
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.beans.property.StringProperty
import scalafx.scene.control.Button
import scalafx.stage.StageStyle
import wow.doge.mygame.executors.Schedulers
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 signal: Deferred[Task, LauncherResult]
  ) {
    val create = Task(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)))

  def testChangeObs(
      obs: Observable[(ObservableValue[_ <: String], String, String)]
  ) =
    obs
      .doOnNext {
        case (x, y, z) => monix.eval.Task.unit
      }
  // .subscribe()

  private lazy val exitButton = new Button {
    text = "Exit"
    // text <-- testChangeObs
  }

  // exitButton.text.bind
  StringProperty("") addListener ((_, _, _) => ())

  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
            )
          )
      )
    )
  }

  // import cats.syntax.all._

  // def init(delay: FiniteDuration = 2000.millis) =
  //   for {
  //     _ <- Task(Platform.setImplicitExit(false))
  //     x <- (Task.unit.start, Task.unit.start).parTupled
  //     fxAppStartFib <- Task(internal.main(Array.empty)).start
  //     _ <- Task.sleep(500.millis)
  //     sceneDragFib <- toIO(sceneDragObservable.completedL).start
  //     buttonActionsComposedFib <- toIO(
  //       Observable(launchAction, exitAction).merge
  //         .doOnNext(_ =>
  //           me.Task(internal.stage.close()).executeOn(props.schedulers.fx)
  //         )
  //         .completedL
  //     ).start
  //     c <- CancelableF[Task](
  //       fxAppStartFib.cancel >> buttonActionsComposedFib.cancel >> sceneDragFib.cancel
  //     )
  //   } yield (c)

  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.schedulers.fx)
                    )
                    .completedL
                )
              )
            )
          )
          .start
      c <- CancelableF[Task](
        // Task(println("Cancelling")) >>
        //   combinedFib.cancel >>
        //   fxAppStartFib.cancel
        // Task.unit
        combinedFib.cancel
      )
    } yield c)(_.cancel)

}
