package xyz.r0r5chach.dermy_app.map

import com.badlogic.gdx.Gdx
import com.badlogic.gdx.graphics.PerspectiveCamera
import com.badlogic.gdx.graphics.g3d.ModelInstance
import com.badlogic.gdx.graphics.g3d.model.MeshPart
import com.badlogic.gdx.graphics.g3d.utils.CameraInputController
import com.badlogic.gdx.math.Intersector
import com.badlogic.gdx.math.Vector3
import xyz.r0r5chach.dermy_app.map.locations.BodyPart
import xyz.r0r5chach.dermy_app.map.locations.Location
import xyz.r0r5chach.dermy_app.map.locations.Side
import kotlin.math.abs
import kotlin.properties.Delegates

class MapCameraController(
    camera: PerspectiveCamera?,
    var modelInstance: ModelInstance?
) : CameraInputController(camera) {
    private var initialX: Int = 0
    private var initialY: Int = 0
    private var selectedLocation: Location by Delegates.observable(Location(Side.NONE, BodyPart.NONE)) { _, _, newValue ->
        notifyObservers(newValue)
    }
    private val observers = mutableListOf<(Location) -> Unit>()

    init {
        this.pinchZoomFactor = 25f
    }

    fun addObserver(observer: (Location) -> Unit) {
        observers.add(observer)
    }

    private fun notifyObservers(newValue: Location) {
        for (observer in observers) {
            observer(newValue)
        }
    }

    override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
        initialX = screenX
        initialY = screenY
        return super.touchDown(screenX, screenY, pointer, button)
    }

    override fun touchUp(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
        if (abs((initialX - screenX).toDouble()) < 10 && abs((initialY - screenY).toDouble()) < 10) {
            handleClick()
        }
        return super.touchUp(screenX, screenY, pointer, button)
    }

    private fun handleClick() {
        if (modelInstance != null) {
            val selectionRay = camera.getPickRay(Gdx.input.x.toFloat(), Gdx.input.y.toFloat())
            var closestDistance = Float.MAX_VALUE
            var closestPart: MeshPart? = null
            val meshes = modelInstance!!.model.meshParts

            for (part in meshes) {
                val bounds = part.mesh.calculateBoundingBox()
                bounds.mul(modelInstance!!.transform)
                val intersection = Vector3()

                if (Intersector.intersectRayBounds(selectionRay, bounds, intersection)) {
                    val distance = selectionRay.origin.dst(intersection)
                    if (distance < closestDistance) {
                        closestDistance = distance
                        closestPart = part
                    }
                }
            }
            val id = closestPart?.id
            val side = when (id?.get(0)) {
                'L' -> Side.LEFT
                'R' -> Side.RIGHT
                else -> Side.LEFT
            }
            val bodyPart = BodyPart.valueOf(id!!.substring(1).uppercase())
            selectedLocation = Location(side, bodyPart)
        }
    }
}
