package electrosphere.client.scene;

import org.joml.Vector3d;
import org.joml.Vector3f;
import org.joml.Vector3i;

import electrosphere.server.terrain.manager.ServerTerrainChunk;

/**
 * Client's data on the world
 */
public class ClientWorldData {

    
    
    /*
    
                       world max
    +---------------------+
    |                     |
    |                     |
    |                     |
    |                     |
    |                     |
    +---------------------+
 world min
    
    
    basically we're saying what the maximum and minimum x and z something can occupy are
    
    FOR THE TIME BEING DOES NOT ACCOUNT FOR Y
    */
    Vector3f worldMinPoint;
    Vector3f worldMaxPoint;
    
    
    int worldDiscreteSize;
    

    public ClientWorldData(
        Vector3f worldMinPoint,
        Vector3f worldMaxPoint,
        int worldDiscreteSize
    ) {
        this.worldMinPoint = worldMinPoint;
        this.worldMaxPoint = worldMaxPoint;
        this.worldDiscreteSize = worldDiscreteSize;
    }
    
    
    
    
    public Vector3f getWorldBoundMin(){
        return worldMinPoint;
    }
    
    public Vector3f getWorldBoundMax(){
        return worldMaxPoint;
    }

    public int getWorldDiscreteSize() {
        return worldDiscreteSize;
    }
    
    
    public int convertRealToChunkSpace(double real){
        return (int)Math.floor(real / ServerTerrainChunk.CHUNK_DIMENSION);
    }

    public float convertChunkToRealSpace(int chunk){
        return chunk * ServerTerrainChunk.CHUNK_DIMENSION;
    }

    public int convertRealToWorld(double real){
        return convertRealToChunkSpace(real);
    }

    public double convertWorldToReal(int world){
        return convertChunkToRealSpace(world);
    }

    /**
     * Checks if a world position is in bounds or not
     * @param worldPos The world position
     * @return true if is in bounds, false otherwise
     */
    public boolean worldPosInBounds(Vector3i worldPos){
        return worldPos.x >= convertRealToWorld(worldMinPoint.x) &&
        worldPos.x < convertRealToWorld(worldMaxPoint.x) &&
        worldPos.y >= convertRealToWorld(worldMinPoint.y) &&
        worldPos.y < convertRealToWorld(worldMaxPoint.y) &&
        worldPos.z >= convertRealToWorld(worldMinPoint.z) &&
        worldPos.z < convertRealToWorld(worldMaxPoint.z)
        ;
    }

    /**
     * Converts a real space position to its world space equivalent
     * @param position The real space position
     * @return The world space position (ie the chunk containing the real space position)
     */
    public Vector3i convertRealToWorldSpace(Vector3d position){
        return new Vector3i(
            convertRealToChunkSpace(position.x),
            convertRealToChunkSpace(position.y),
            convertRealToChunkSpace(position.z)
        );
    }

    /**
     * Converts a real space position to its absolute voxel space equivalent
     * @param position The real space position
     * @return The absolute voxel space position ie the voxel-aligned position not clamped to the current chunk
     */
    public Vector3i convertRealToAbsoluteVoxelSpace(Vector3d position){
        return new Vector3i(
            (int)Math.floor(position.x),
            (int)Math.floor(position.y),
            (int)Math.floor(position.z)
        );
    }

    /**
     * Converts a absolute voxel position to its relative voxel space equivalent
     * @param position The real space position
     * @return The relative voxel space position ie the voxel-aligned position not clamped to the current chunk
     */
    public Vector3i convertAbsoluteVoxelToRelativeVoxelSpace(Vector3i position){
        return new Vector3i(
            position.x % ServerTerrainChunk.CHUNK_DIMENSION,
            position.y % ServerTerrainChunk.CHUNK_DIMENSION,
            position.z % ServerTerrainChunk.CHUNK_DIMENSION
        );
    }

    /**
     * Converts a absolute voxel position to its world space equivalent
     * @param position The real space position
     * @return The world space position ie the voxel-aligned position not clamped to the current chunk
     */
    public Vector3i convertAbsoluteVoxelToWorldSpace(Vector3i position){
        return new Vector3i(
            position.x / ServerTerrainChunk.CHUNK_DIMENSION,
            position.y / ServerTerrainChunk.CHUNK_DIMENSION,
            position.z / ServerTerrainChunk.CHUNK_DIMENSION
        );
    }


    /**
     * Converts a relative voxel position to its absolute voxel equivalent
     * @param voxelPos The relative voxel position
     * @param worldPos The position of the chunk
     * @return The absolute voxel position ie the voxel-aligned position not clamped to the current chunk
     */
    public Vector3i convertRelativeVoxelToAbsoluteVoxelSpace(Vector3i voxelPos, Vector3i worldPos){
        return new Vector3i(
            worldPos.x * ServerTerrainChunk.CHUNK_DIMENSION + voxelPos.x,
            worldPos.y * ServerTerrainChunk.CHUNK_DIMENSION + voxelPos.y,
            worldPos.z * ServerTerrainChunk.CHUNK_DIMENSION + voxelPos.z
        );
    }

    /**
     * Converts a relative voxel position to its absolute voxel equivalent
     * @param voxelPos The relative voxel position
     * @param worldPos The position of the chunk
     * @return The absolute voxel position ie the voxel-aligned position not clamped to the current chunk
     */
    public int convertRelativeVoxelToAbsoluteVoxelSpace(int voxelPos, int worldPos){
        return worldPos * ServerTerrainChunk.CHUNK_DIMENSION + voxelPos;
    }

    /**
     * Converts a world space vector to a real space vector
     * @param position The world space vector
     * @return The real space vector
     */
    public Vector3d convertWorldToRealSpace(Vector3i position){
        return new Vector3d(
            convertWorldToReal(position.x),
            convertWorldToReal(position.y),
            convertWorldToReal(position.z)
        );
    }

    public Vector3i convertRealToVoxelSpace(Vector3d position){
        return new Vector3i(
            (int)Math.floor(position.x - convertChunkToRealSpace(convertRealToChunkSpace(position.x))),
            (int)Math.floor(position.y - convertChunkToRealSpace(convertRealToChunkSpace(position.y))),
            (int)Math.floor(position.z - convertChunkToRealSpace(convertRealToChunkSpace(position.z)))
        );
    }

}
