package wtf.beatrice.retrorender.engine;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.VertexAttributes;
import com.badlogic.gdx.graphics.g3d.*;
import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
import com.badlogic.gdx.graphics.g3d.attributes.TextureAttribute;
import com.badlogic.gdx.graphics.g3d.utils.MeshPartBuilder;
import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder;
import com.badlogic.gdx.math.Vector3;

import java.util.ArrayList;
import java.util.List;

public class World3D {

    // textures
    private final Texture groundTexture;

    // models (base mesh)
    private final Model groundModel;
    private final Model cubeModel;

    // instances
    private final ModelInstance groundInstance;
    private final List<ModelInstance> cubeInstances = new ArrayList<>();

    public World3D() {
        ModelBuilder modelBuilder = new ModelBuilder();

        // --- load floor texture
        groundTexture = new Texture(Gdx.files.internal("textures/paving.png"));
        groundTexture.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest);
        groundTexture.setWrap(Texture.TextureWrap.Repeat, Texture.TextureWrap.Repeat);

        // --- create ground material
        Material groundMat = new Material(
            ColorAttribute.createDiffuse(1f, 1f, 1f, 1f),
            TextureAttribute.createDiffuse(groundTexture)
        );

        // plane size (halfSize in X/Z)
        float halfSize = 32f;

        // times the texture repeats across the whole plane
        float tileScale = 16f;

        modelBuilder.begin();

        MeshPartBuilder mpb = modelBuilder.part(
            "ground",
            GL20.GL_TRIANGLES,
            VertexAttributes.Usage.Position
                | VertexAttributes.Usage.Normal
                | VertexAttributes.Usage.TextureCoordinates,  // 👈 important
            groundMat
        );

        // set UV range so texture repeats tileScale times
        mpb.setUVRange(0f, 0f, tileScale, tileScale);

        // build the quad
        mpb.rect(
            -halfSize, 0f,  halfSize,   // top-left
            halfSize, 0f,  halfSize,   // top-right
            halfSize, 0f, -halfSize,   // bottom-right
            -halfSize, 0f, -halfSize,   // bottom-left
            0f, 1f,  0f                // normal up
        );

        groundModel = modelBuilder.end();
        groundInstance = new ModelInstance(groundModel);

        // --- create reusable cube model
        cubeModel = modelBuilder.createBox(
            2f, 2f, 2f,
            new Material(ColorAttribute.createDiffuse(1f, 1f, 1f, 1f)),
            VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal
        );

        // --- place random cubes in the world
        // center
        cubeInstances.add(new ModelInstance(cubeModel)); // at origin

        // four around
        cubeInstances.add(new ModelInstance(cubeModel));
        cubeInstances.get(1).transform.setToTranslation(8f, 1f, 8f);

        cubeInstances.add(new ModelInstance(cubeModel));
        cubeInstances.get(2).transform.setToTranslation(-8f, 1f, 8f);

        cubeInstances.add(new ModelInstance(cubeModel));
        cubeInstances.get(3).transform.setToTranslation(8f, 1f, -8f);

        cubeInstances.add(new ModelInstance(cubeModel));
        cubeInstances.get(4).transform.setToTranslation(-8f, 1f, -8f);

        // one tall pillar in front of you
        ModelInstance pillar = new ModelInstance(cubeModel);
        pillar.transform
            .setToScaling(1f, 3f, 1f)
            .translate(0f, 3f, -10f);
        cubeInstances.add(pillar);
    }

    public void update(float delta) {
        // rotate cubes around y axis
        for (ModelInstance instance : cubeInstances) {
            instance.transform.rotate(Vector3.Y, 20f * delta);
        }
    }

    public void render(ModelBatch batch, Environment environment) {
        // draw ground
        batch.render(groundInstance, environment);
        // draw cubes
        for (ModelInstance instance : cubeInstances) {
            batch.render(instance, environment);
        }
    }
    /**
     * Returns the ground height (y) at a given XZ position.
     * Starts from baseHeight (e.g. 0) and checks if you're standing on top of any cube.
     */
    public float getGroundHeightAt(float x, float z, float baseHeight) {
        float maxY = baseHeight; // ground plane at y = 0

        // a cube is 2 units tall, from y = 0 to y = 2
        float cubeHalfSize = 1f;
        float cubeTopY = 2f;

         Vector3 tmp = new Vector3();
        for (ModelInstance cube : cubeInstances) {
            cube.transform.getTranslation(tmp);

            float minX = tmp.x - cubeHalfSize;
            float maxX = tmp.x + cubeHalfSize;
            float minZ = tmp.z - cubeHalfSize;
            float maxZ = tmp.z + cubeHalfSize;

            if (x >= minX && x <= maxX && z >= minZ && z <= maxZ) {
                if (cubeTopY > maxY) {
                    maxY = cubeTopY;
                }
            }
        }

        return maxY;
    }

    /**
     * Simple horizontal collision check between a player and cube sides.
     * Player is approximated as a capsule: radius in XZ, height = eyeHeight.
     */
    public boolean collidesAt(float x, float y, float z, float radius, float eyeHeight) {
        float feetY = y - eyeHeight;
        float headY = y;

        float cubeHalfSize = 1f;
        float cubeBottomY = 0f;
        float cubeTopY = 2f;

        Vector3 tmp = new Vector3();
        for (ModelInstance cube : cubeInstances) {
            cube.transform.getTranslation(tmp);

            float minX = tmp.x - cubeHalfSize;
            float maxX = tmp.x + cubeHalfSize;
            float minZ = tmp.z - cubeHalfSize;
            float maxZ = tmp.z + cubeHalfSize;

            // no vertical overlap -> ignore
            if (headY <= cubeBottomY || feetY >= cubeTopY) {
                continue;
            }

            // circle (player) vs AABB (cube) in XZ
            boolean overlapX = (x + radius > minX) && (x - radius < maxX);
            boolean overlapZ = (z + radius > minZ) && (z - radius < maxZ);

            if (overlapX && overlapZ) {
                return true;
            }
        }

        return false;
    }

    public void dispose() {
        groundModel.dispose();
        cubeModel.dispose();
        groundTexture.dispose();
    }
}
