package wow.doge.mygame.utils.wrappers.jme import cats.effect.Sync import cats.syntax.eq._ import com.jme3.light.Light import com.jme3.{scene => jmes} import monix.bio.IO import monix.bio.Task import monix.bio.UIO import monix.execution.annotations.UnsafeBecauseImpure import monix.reactive.Observable import wow.doge.mygame.implicits._ abstract class NodeWrapper[F[_]: Sync] protected (node: jmes.Node) { def name = node.getName() def children: Observable[jmes.Spatial] = node.observableChildren def attachChild(n: jmes.Spatial): F[Unit] = Sync[F].delay(node.attachChild(n)) def add(wn: Node[F]): F[Unit] = Sync[F].delay(node.attachChild(wn.unsafeDelegate)) def remove(n: jmes.Spatial): F[Unit] = Sync[F].delay(node.detachChild(n)) def remove(wn: Node[F]): F[Unit] = Sync[F].delay(node.detachChild(wn.unsafeDelegate)) def addLight(light: Light) = Sync[F].delay { node.addLight(light) } def removeLight(light: Light) = Sync[F].delay { node.removeLight(light) } def asSpatial: F[jmes.Spatial] = Sync[F].delay(node) } object NodeWrapper { implicit class NodeOps[F[_]](private val nw: NodeWrapper[F]) extends AnyVal { def +=(n: jmes.Spatial) = nw.attachChild(n) def +=(n: Node[F]) = nw.add(n) def -=(n: jmes.Spatial) = nw.remove(n) def -=(wn: Node[F]) = nw.remove(wn) def +=(light: Light) = nw.addLight(light) def -=(light: Light) = nw.removeLight(light) } } final class Node[F[_]: Sync] private (node: jmes.Node) extends NodeWrapper[F](node) { /** * Get the underlying wrapped value */ @UnsafeBecauseImpure def unsafeDelegate = node } object Node { def apply[F[_]: Sync](name: String) = new Node[F](new jmes.Node(name)) def apply[F[_]: Sync](n: jmes.Node) = new Node[F](n) } final class AppNode[F[_]: Sync] private (node: jmes.Node) extends NodeWrapper[F](node) object AppNode { def apply[F[_]: Sync](name: String) = new AppNode[F](new jmes.Node(name)) def apply[F[_]: Sync](n: jmes.Node) = new AppNode[F](n) } abstract class NodeWrapper2 protected (node: jmes.Node) { import NodeWrapper2._ def name = node.getName() def children: Observable[jmes.Spatial] = node.observableChildren def attachChild(n: jmes.Spatial): IO[AddNodeToItselfError.type, Unit] = IO { node.attachChild(n); () }.onErrorHandleWith { case ex: IllegalArgumentException => if (ex.getMessage === "Cannot add child to itself") IO.raiseError(AddNodeToItselfError) else IO.unit } def add(wn: Node2): IO[AddNodeToItselfError.type, Unit] = IO { node.attachChild(wn.unsafeDelegate); () }.onErrorHandleWith { case ex: IllegalArgumentException => if (ex.getMessage === "Cannot add child to itself") IO.raiseError(AddNodeToItselfError) else IO.unit } def remove(n: jmes.Spatial) = UIO(node.detachChild(n)) def remove(wn: Node2) = UIO(node.detachChild(wn.unsafeDelegate)) def addLight(light: Light) = UIO { node.addLight(light) } def removeLight(light: Light) = UIO { node.removeLight(light) } def asSpatial: Task[jmes.Spatial] = UIO(node) } object NodeWrapper2 { sealed trait Error case object AddNodeToItselfError extends Error implicit class NodeOps[F[_]](private val nw: NodeWrapper2) extends AnyVal { def +=(n: jmes.Spatial) = nw.attachChild(n) def +=(n: Node2) = nw.add(n) def -=(n: jmes.Spatial) = nw.remove(n) def -=(wn: Node2) = nw.remove(wn) def +=(light: Light) = nw.addLight(light) def -=(light: Light) = nw.removeLight(light) } } final class Node2 private (node: jmes.Node) extends NodeWrapper2(node) { /** * Get the underlying wrapped value */ @UnsafeBecauseImpure def unsafeDelegate = node } object Node2 { def apply(name: String) = new Node2(new jmes.Node(name)) def apply(n: jmes.Node) = new Node2(n) } final class AppNode2 private (node: jmes.Node) extends NodeWrapper2(node) object AppNode2 { // sealed trait Error extends NodeWrapper2.Error def apply(name: String) = new AppNode2(new jmes.Node(name)) def apply(n: jmes.Node) = new AppNode2(n) }