package wow.doge.mygame.utils.wrappers.jme
import scala.reflect.ClassTag

import cats.Show
import cats.kernel.Eq
import com.jme3.asset.AssetLoadException
import com.jme3.asset.AssetLocator
import com.jme3.asset.AssetNotFoundException
import com.jme3.scene.Spatial
import com.jme3.{asset => jmea}
import monix.bio.IO
import monix.bio.UIO

class AssetManager(assetManager: jmea.AssetManager) {
  import AssetManager._
  def loadModel(path: os.RelPath): IO[Error, Spatial] =
    IO(assetManager.loadModel(path.toString)).onErrorHandleWith {
      case ex: AssetNotFoundException =>
        IO.raiseError(AssetNotFound(ex.getMessage))
      case ex: AssetLoadException =>
        IO.raiseError(AssetLoadError(ex.getMessage))
    }
  def loadModelAs[T <: Spatial](
      path: os.RelPath
  )(implicit ct: ClassTag[T]): IO[Error, T] =
    loadModel(path).flatMap(model =>
      if (model.getClass == ct.runtimeClass)
        UIO(model.asInstanceOf[T])
      else IO.raiseError(CouldNotCastError)
    )
  def loadAssetAs[T](path: os.RelPath)(implicit ct: ClassTag[T]): IO[Error, T] =
    IO(assetManager.loadAsset(path.toString))
      .onErrorHandleWith {
        case ex: AssetNotFoundException =>
          IO.raiseError(AssetNotFound(ex.getMessage))
        case ex: AssetLoadException =>
          IO.raiseError(AssetLoadError(ex.getMessage))
      }
      .flatMap(asset =>
        if (asset.getClass == ct.runtimeClass)
          UIO(asset.asInstanceOf[T])
        else IO.raiseError(CouldNotCastError)
      )
  def registerLocator(path: os.RelPath, locator: Class[_ <: AssetLocator]) =
    UIO(assetManager.registerLocator(path.toString, locator))

}
object AssetManager {
  sealed trait Error
  case class AssetNotFound(message: String) extends Error
  case class AssetLoadError(message: String) extends Error
  case object CouldNotCastError extends Error
  object Error {
    implicit val show = Show.fromToString[Error]
    implicit val eq = Eq.fromUniversalEquals[Error]
  }
}
