package core.engine.toolbox;

import core.engine.Engine;
import core.engine.entity.Camera;
import core.engine.terrain.Terrain;
import core.engine.terrain.TerrainTile;
import org.lwjgl.system.windows.MOUSEINPUT;
import utils.MatrixGraphicUtils;
import utils.vectors.*;

public class MousePicker {

    private Vector3f currentRay = new Vector3f();

    private Matrix4f projectionMatrix;
    private Matrix4f viewMatrix;
    private Camera camera;

    private Terrain terrain;
    private Vector3f currentTerrainPoint;
    private TerrainTile currentTerrainTile;

    public MousePicker(Camera cam, Matrix4f projection, Terrain terrain) {
        camera = cam;
        projectionMatrix = projection;
        viewMatrix = MatrixGraphicUtils.createViewMatrix(camera);
        this.terrain = terrain;
    }

    public Vector3f getCurrentTerrainPoint() {
        return currentTerrainPoint;
    }

    public Vector3f getCurrentRay() {
        return currentRay;
    }

    public void update(float mouseX, float mouseY) {
        viewMatrix = MatrixGraphicUtils.createViewMatrix(camera);
        currentRay = calculateMouseRay(mouseX, mouseY);
        currentTerrainPoint = calcCurrentTerrainIntersection();
        currentTerrainTile = calcIntersectingTerrainTile();
        if(currentTerrainTile != null) {
            currentTerrainTile.setColor(new Vector3f(1f, 1f, 1f));
        }

    }

    private Vector3f calculateMouseRay(float mouseX, float mouseY) {
        Vector2f normalizedCoords = getNormalisedDeviceCoordinates(mouseX, mouseY);
        Vector4f clipCoords = new Vector4f(normalizedCoords.x, normalizedCoords.y, -1.0f, 1.0f);

        // Transform the clip coordinates into eye coordinates using the projection matrix
        Vector4f eyeCoords = toEyeCoords(clipCoords);

        // Transform the eye coordinates into world coordinates using the view matrix
        return toWorldCoords(eyeCoords);
    }

    private Vector3f toWorldCoords(Vector4f eyeCoords) {
        Matrix4f invertedView = Matrix4f.invert(viewMatrix, null);
        Vector4f rayWorld = Matrix4f.transform(invertedView, eyeCoords, null);
        Vector3f mouseRay = new Vector3f(rayWorld.x, rayWorld.y, rayWorld.z);
        return mouseRay;
    }

    private Vector4f toEyeCoords(Vector4f clipCoords) {
        Matrix4f invertedProjection = Matrix4f.invert(projectionMatrix, null);
        Vector4f eyeCoords = Matrix4f.transform(invertedProjection, clipCoords, null);
        return new Vector4f(eyeCoords.x, eyeCoords.y, -1f, 0f);
    }

    private Vector2f getNormalisedDeviceCoordinates(float mouseX, float mouseY) {
        float x = (2.0f * mouseX) / Engine.WINDOW_WIDTH - 1f;
        float y = (2.0f * mouseY) / Engine.WINDOW_HEIGHT - 1f;
        return new Vector2f(x, y);
    }

    //**********************************************************

    private Vector3f calcCurrentTerrainIntersection() {
        Vector3f camPos = camera.getPosition();
        float rayFactor = -camPos.y / currentRay.y;

        float terrainX = camPos.x + rayFactor * currentRay.x;
        float terrainY = camPos.y + rayFactor * currentRay.y;
        float terrainZ = camPos.z + rayFactor * currentRay.z;

        return new Vector3f(terrainX, terrainY, terrainZ);
    }

    //**********************************************************
    public TerrainTile calcIntersectingTerrainTile() {

        for(TerrainTile terrainTile : terrain.getTerrainTiles()) {
            float distanceX = (float) Math.pow(currentTerrainPoint.x - terrainTile.getPosition().x, 2);
            float distanceZ = (float) Math.pow(-currentTerrainPoint.z - terrainTile.getPosition().z, 2);

            float radius = (float) Math.pow(1, 2);

            if(distanceX + distanceZ < radius) {
                if(!terrainTile.getColor().equals(new Vector3f(1f, 1f, 1f))) {
                    System.out.println("Center: " + terrainTile.getPosition());
                    System.out.println("TerrainPoint: " + currentTerrainPoint);
                }
                return terrainTile;
            }
        }
        return null;
    }

}
