package wow.doge.mygame.game.subsystems.level

import cats.effect.Resource
import com.jme3.bullet.control.RigidBodyControl
import com.jme3.light.AmbientLight
import com.jme3.light.DirectionalLight
import com.jme3.scene.Node
import com.jme3.scene.Spatial
import com.softwaremill.tagging._
import monix.bio.IO
import monix.bio.UIO
import wow.doge.mygame.AppError
import wow.doge.mygame.game.GameAppTags
import wow.doge.mygame.utils.wrappers.jme.AppNode2
import wow.doge.mygame.utils.wrappers.jme.CollisionShapeFactory
import wow.doge.mygame.utils.wrappers.jme.PhysicsSpace

class GameLevel(
    val model: Spatial,
    val physicsControl: RigidBodyControl,
    val ambientLight: AmbientLight,
    val directionalLight: DirectionalLight
) {
  def addToGame(
      rootNode: AppNode2 @@ GameAppTags.RootNode,
      physicsSpace: PhysicsSpace
  ) =
    (for {
      _ <- rootNode += model
      _ <- rootNode += ambientLight
      _ <- rootNode += directionalLight
      _ <- physicsSpace += model
      _ <- physicsSpace += physicsControl
    } yield ()).mapError(AppError.AppNodeError)
  def removeFromGame(
      rootNode: AppNode2 @@ GameAppTags.RootNode,
      physicsSpace: PhysicsSpace
  ) =
    for {
      _ <- rootNode -= model
      _ <- rootNode -= ambientLight
      _ <- rootNode -= directionalLight
      _ <- physicsSpace -= model
      _ <- physicsSpace -= physicsControl
    } yield ()

  def resource(
      rootNode: AppNode2 @@ GameAppTags.RootNode,
      physicsSpace: PhysicsSpace
  ) =
    Resource.make(this.addToGame(rootNode, physicsSpace))(_ =>
      this.removeFromGame(rootNode, physicsSpace)
    )
}

object GameLevel {

  def apply(
      modelPath: os.RelPath,
      al: AmbientLight,
      dl: DirectionalLight
  )(implicit
      assetManager: wow.doge.mygame.utils.wrappers.jme.AssetManager
  ): IO[AppError, GameLevel] =
    for {
      sceneModel <-
        assetManager
          .loadModelAs[Node](modelPath)
          .mapError(AppError.AssetManagerError)
      sceneShape <-
        CollisionShapeFactory
          .createMeshShape(sceneModel)
          .mapError(AppError.CollisionShapeCreationFailed)
      landscape <- UIO(new RigidBodyControl(sceneShape, 0))

    } yield new GameLevel(
      model = sceneModel,
      physicsControl = landscape,
      ambientLight = al,
      directionalLight = dl
    )
}
