package wtf.beatrice.retrorender.engine.world;

import com.badlogic.gdx.graphics.g3d.Environment;
import com.badlogic.gdx.graphics.g3d.ModelBatch;
import com.badlogic.gdx.graphics.g3d.ModelInstance;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Vector3;

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

public class World3D {

    private final ModelLibrary models;

    private final WorldObject ground;
    private final List<WorldObject> objects = new ArrayList<>();

    private final TownFactory townFactory;

    private final Vector3 tmpWorld   = new Vector3();
    private final Vector3 tmpFeet    = new Vector3();
    private final Vector3 tmpHead    = new Vector3();
    private final Vector3 tmpLocalFeet = new Vector3();
    private final Vector3 tmpLocalHead = new Vector3();

    private final Matrix4 tmpMat   = new Matrix4();
    private final Matrix4 tmpInv   = new Matrix4();

    private final Vector3 tmp = new Vector3();

    public World3D(ModelLibrary models) {
        this.models = models;
        this.townFactory = new TownFactory(models);

        // --- ground ---
        ground = new WorldObject(
            "ground",
            new ModelInstance(models.groundModel),
            Collider.none()
        );
        ground.staticObject = true;

        // --- build a tiny town layout instead of random cubes ---
        buildTestTown();
    }

    private void addObject(WorldObject obj) {
        objects.add(obj);
    }

    private void buildTestTown() {
        float tileSize = 2f; // world units per “map tile”

        // Simple 7x7 layout:
        // H = house, P = path, . = empty
        String[] layout = {
            ".............",
            "..H.C.....H..",
            "..P.......P..",
            "..P.......P..",
            "..P.......P..",
            "..P.......P..",
            "..P.......P..",
            "..P.......P..",
            "..H.......H..",
            "............."
        };

        int rows = layout.length;
        int cols = layout[0].length();

        float offsetX = -(cols - 1) * tileSize * 0.5f;
        float offsetZ = -(rows - 1) * tileSize * 0.5f;

        for (int row = 0; row < rows; row++) {
            String line = layout[row];
            for (int col = 0; col < cols; col++) {
                char c = line.charAt(col);

                float x = offsetX + col * tileSize;
                float z = offsetZ + row * tileSize;

                switch (c) {
                    case 'H':
                        addObject(townFactory.createHouse("house_" + row + "_" + col, x, z));
                        break;
                    case 'P':
                        addObject(townFactory.createPathTile("path_" + row + "_" + col, x, z));
                        break;
                    case 'C':
                        addObject(townFactory.createCrate("crate_" + row + "_" + col, x, z));
                    default:
                        break;
                }
            }
        }

        // maybe put a crate in the middle

    }


    public void update(float delta) {
        // here you can update objects that animate / move
        for (WorldObject obj : objects) {
            obj.update(delta);
        }
    }

    public void render(ModelBatch batch, Environment environment) {
        // draw ground
        batch.render(ground.instance, environment);

        // draw objects
        for (WorldObject obj : objects) {
            batch.render(obj.instance, environment);
        }
    }

    /**
     * Returns the ground height (y) for a capsule with given radius whose center
     * is at (x, z) in world space. Starts from baseHeight (e.g. 0) and checks
     * walkable BOX colliders using a circle-vs-AABB test in XZ.
     */
    public float getCapsuleGroundHeight(float x, float z, float radius, float baseHeight) {
        float maxY = baseHeight; // plane at y = baseHeight

        for (WorldObject obj : objects) {
            if (!obj.walkableSurface) continue;
            if (obj.collider.type != Collider.Type.BOX) continue;

            Collider col = obj.collider;

            // world → local
            tmpInv.set(obj.instance.transform);
            tmpInv.inv();

            // capsule center projected to XZ in local space
            tmpWorld.set(x, 0f, z);
            tmpWorld.mul(tmpInv);

            float cx = tmpWorld.x;
            float cz = tmpWorld.z;

            float boxMinX = col.center.x - col.halfExtents.x;
            float boxMaxX = col.center.x + col.halfExtents.x;
            float boxMinZ = col.center.z - col.halfExtents.z;
            float boxMaxZ = col.center.z + col.halfExtents.z;

            // circle (capsule footprint) vs AABB in local XZ
            float closestX = Math.max(boxMinX, Math.min(cx, boxMaxX));
            float closestZ = Math.max(boxMinZ, Math.min(cz, boxMaxZ));

            float dx = cx - closestX;
            float dz = cz - closestZ;

            if (dx * dx + dz * dz > radius * radius) {
                // no overlap in XZ with this walkable surface
                continue;
            }

            // Local top Y of the box
            float localTopY = col.center.y + col.halfExtents.y;

            // Convert that top point back to world space
            tmpWorld.set(col.center.x, localTopY, col.center.z);
            tmpWorld.mul(obj.instance.transform);

            if (tmpWorld.y > maxY) {
                maxY = tmpWorld.y;
            }
        }

        return maxY;
    }

    /**
     * Backwards-compatible point-sample version (radius = 0).
     */
    public float getGroundHeightAt(float x, float z, float baseHeight) {
        return getCapsuleGroundHeight(x, z, 0f, baseHeight);
    }

    /**
     * Simple horizontal collision check between a player and BOX colliders.
     * 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;

        final float EPS_TOP    = 0.05f;
        final float EPS_BOTTOM = 0.01f;

        for (WorldObject obj : objects) {
            if (!obj.collidable) continue;
            if (obj.collider.type != Collider.Type.BOX) continue;

            Collider col = obj.collider;

            tmpInv.set(obj.instance.transform);
            tmpInv.inv();

            tmpFeet.set(x, feetY, z).mul(tmpInv);
            tmpHead.set(x, headY, z).mul(tmpInv);

            float boxMinY = col.center.y - col.halfExtents.y;
            float boxMaxY = col.center.y + col.halfExtents.y;

            if (tmpFeet.y >= boxMaxY - EPS_TOP) {
                // on / just above top → let groundHeight handle it
                continue;
            }

            if (tmpHead.y <= boxMinY + EPS_BOTTOM || tmpFeet.y >= boxMaxY) {
                continue;
            }

            float px = 0.5f * (tmpFeet.x + tmpHead.x);
            float pz = 0.5f * (tmpFeet.z + tmpHead.z);

            float minX = col.center.x - col.halfExtents.x;
            float maxX = col.center.x + col.halfExtents.x;
            float minZ = col.center.z - col.halfExtents.z;
            float maxZ = col.center.z + col.halfExtents.z;

            float closestX = Math.max(minX, Math.min(px, maxX));
            float closestZ = Math.max(minZ, Math.min(pz, maxZ));

            float dx = px - closestX;
            float dz = pz - closestZ;

            if (dx * dx + dz * dz <= radius * radius) {
                return true;
            }
        }

        return false;
    }

    public void dispose() {
        models.dispose(); // or let GameScreen own/dispose the library
    }
}
