package wow.doge.mygame.utils.wrappers.jme
import com.jme3.bullet.control.PhysicsControl
import com.jme3.bullet.joints.PhysicsJoint
import com.jme3.{bullet => jmeb}
import com.jme3.{scene => jmes}
import monix.bio.UIO
import wow.doge.mygame.implicits._

final class PhysicsSpace(space: jmeb.PhysicsSpace) {
  def add[T](addable: T)(implicit P: PhysicsSpaceAddable[T]) =
    UIO(P.addToSpace(addable, space))

  def remove(anyObject: Any) = UIO(space.remove(anyObject))

  def addAll(spatial: jmes.Spatial) = UIO(space.addAll(spatial))

  def removeAll(spatial: jmes.Spatial) = UIO(space.removeAll(spatial))

  def collisionObservable = space.collisionObservable()

  // space.enqueue(() => ())

  def physicsTickObservable = space.physicsTickObservable()

  def prePhysicsTickObservable = space.prePhysicsTickObservable()
}
object PhysicsSpace {
  implicit final class PhysicsSpaceOps(private val space: PhysicsSpace)
      extends AnyVal {
    def +=[T: PhysicsSpaceAddable](addable: T) = space.add(addable)

    def :+[T: PhysicsSpaceAddable](addable: T) =
      for {
        _ <- space.add(addable)
      } yield space
    def -(anyObject: Any) =
      for {
        _ <- space.remove(anyObject)
      } yield space

    def -=(anyObject: Any) = space.remove(anyObject)

    def +=(spatial: jmes.Spatial) = space.addAll(spatial)

    // def :+(spatial: jmes.Spatial) = {
    //   space.addAll(spatial)
    //   space
    // }

    // def -(spatial: jmes.Spatial) = {
    //   space.removeAll(spatial)
    //   space
    // }

    def -=(spatial: jmes.Spatial) = space.removeAll(spatial)
  }
}

trait PhysicsSpaceAddable[-T] {
  def addToSpace(inst: T, space: jmeb.PhysicsSpace): Unit
}

object PhysicsSpaceAddable {
  implicit val physicsSpaceAddableForPhysControl =
    new PhysicsSpaceAddable[PhysicsControl] {

      override def addToSpace(
          inst: PhysicsControl,
          space: jmeb.PhysicsSpace
      ): Unit = space.add(inst)

    }
  implicit val physicsSpaceAddableForSpatial =
    new PhysicsSpaceAddable[jmes.Spatial] {

      override def addToSpace(
          inst: jmes.Spatial,
          space: jmeb.PhysicsSpace
      ): Unit = space.add(inst)

    }
  implicit val physicsSpaceAddableForPhysJoint =
    new PhysicsSpaceAddable[PhysicsJoint] {

      override def addToSpace(
          inst: PhysicsJoint,
          space: jmeb.PhysicsSpace
      ): Unit = space.add(inst)

    }
}
