package electrosphere.server.datacell.physics;

import electrosphere.client.terrain.cache.ChunkData;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.types.terrain.TerrainChunk;
import electrosphere.server.datacell.Realm;
import electrosphere.server.terrain.manager.ServerTerrainChunk;
import electrosphere.server.terrain.manager.ServerTerrainManager;

import org.joml.Vector3d;
import org.joml.Vector3i;
import org.ode4j.ode.DBody;

/**
 * An entity which contains physics for terrain for a given chunk on the server
 */
public class PhysicsDataCell {

    Vector3i worldPos;
    
    Entity physicsEntity;
    
    DBody physicsObject;

    Realm realm;
    ServerTerrainManager serverTerrainManager;

    float[][][] weights = new float[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE];
    int[][][] types = new int[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE];
    
    
    PhysicsDataCell(){
        
    }
    
    /**
     * Creates a physics cell
     * @param realm The realm to create it in
     * @param worldPos The world position of the cell
     * @return The cell
     */
    public static PhysicsDataCell createPhysicsCell(
        Realm realm,
        Vector3i worldPos
    ){
        PhysicsDataCell rVal = new PhysicsDataCell();
        rVal.realm = realm;
        rVal.serverTerrainManager = realm.getServerWorldData().getServerTerrainManager();
        rVal.worldPos = worldPos;
        return rVal;
    }
    
    /**
     * Retires a physics data cell
     */
    public void retireCell(){
        EntityUtils.cleanUpEntity(physicsEntity);
    }
    
    /**
     * Generates the physics entity for this chunk
     */
    public void generatePhysics(){
        //if the entity hasn't already been created for some reason, need to create it
        if(physicsEntity == null){
            
            //
            //fill in weights and types maps
            //
            fillInData();

            Vector3d realPos = new Vector3d(
                worldPos.x * ServerTerrainChunk.CHUNK_DIMENSION,
                worldPos.y * ServerTerrainChunk.CHUNK_DIMENSION,
                worldPos.z * ServerTerrainChunk.CHUNK_DIMENSION
            );
            physicsEntity = TerrainChunk.serverCreateTerrainChunkEntity(realm, realPos, weights, types);
            physicsEntity.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true);
        }
        // //then actually perform the attach
        // physicsObject = PhysicsUtils.attachTerrainRigidBody(physicsEntity,heightmap,true);
        // Realm realm = Globals.realmManager.getEntityRealm(physicsEntity);
        // realm.getCollisionEngine().registerPhysicsEntity(physicsEntity);
    }
    
    /**
     * Destroys the physics for this data cell
     */
    public void destroyPhysics(){
        Realm realm = Globals.realmManager.getEntityRealm(physicsEntity);
        realm.getCollisionEngine().destroyPhysics(physicsEntity);
    }

    /**
     * Fills in the internal arrays of data for generate terrain models
     */
    private void fillInData(){
        //
        //fill in data
        //
        //main chunk
        ServerTerrainChunk currentChunk = serverTerrainManager.getChunk(worldPos.x, worldPos.y, worldPos.z);
        for(int x = 0; x < ChunkData.CHUNK_SIZE; x++){
            for(int y = 0; y < ChunkData.CHUNK_SIZE; y++){
                for(int z = 0; z < ChunkData.CHUNK_SIZE; z++){
                    weights[x][y][z] = currentChunk.getWeight(x,y,z);
                    types[x][y][z] = currentChunk.getType(x,y,z);
                }
            }
        }
        //face X
        if(worldPos.x + 1 < realm.getServerWorldData().getWorldSizeDiscrete()){
            currentChunk = serverTerrainManager.getChunk(worldPos.x + 1, worldPos.y, worldPos.z);
            for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
                for(int j = 0; j < ChunkData.CHUNK_SIZE; j++){
                    weights[ChunkData.CHUNK_SIZE][i][j] = currentChunk.getWeight(0, i, j);
                    types[ChunkData.CHUNK_SIZE][i][j] = currentChunk.getType(0, i, j);
                }
            }
        } else {
            for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
                for(int j = 0; j < ChunkData.CHUNK_SIZE; j++){
                    weights[ChunkData.CHUNK_SIZE][i][j] = 0;
                    types[ChunkData.CHUNK_SIZE][i][j] = 0;
                }
            }
        }
        //face Y
        if(worldPos.y + 1 < realm.getServerWorldData().getWorldSizeDiscrete()){
            currentChunk = serverTerrainManager.getChunk(worldPos.x, worldPos.y + 1, worldPos.z);
            for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
                for(int j = 0; j < ChunkData.CHUNK_SIZE; j++){
                    weights[i][ChunkData.CHUNK_SIZE][j] = currentChunk.getWeight(i, 0, j);
                    types[i][ChunkData.CHUNK_SIZE][j] = currentChunk.getType(i, 0, j);
                }
            }
        } else {
            for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
                for(int j = 0; j < ChunkData.CHUNK_SIZE; j++){
                    weights[i][ChunkData.CHUNK_SIZE][j] = 0;
                    types[i][ChunkData.CHUNK_SIZE][j] = 0;
                }
            }
        }
        //face Z
        if(worldPos.z + 1 < realm.getServerWorldData().getWorldSizeDiscrete()){
            currentChunk = serverTerrainManager.getChunk(worldPos.x, worldPos.y, worldPos.z + 1);
            for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
                for(int j = 0; j < ChunkData.CHUNK_SIZE; j++){
                    weights[i][j][ChunkData.CHUNK_SIZE] = currentChunk.getWeight(i, j, 0);
                    types[i][j][ChunkData.CHUNK_SIZE] = currentChunk.getType(i, j, 0);
                }
            }
        } else {
            for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
                for(int j = 0; j < ChunkData.CHUNK_SIZE; j++){
                    weights[i][j][ChunkData.CHUNK_SIZE] = 0;
                    types[i][j][ChunkData.CHUNK_SIZE] = 0;
                }
            }
        }
        //edge X-Y
        if(
            worldPos.x + 1 < realm.getServerWorldData().getWorldSizeDiscrete() &&
            worldPos.y + 1 < realm.getServerWorldData().getWorldSizeDiscrete()
        ){
            currentChunk = serverTerrainManager.getChunk(worldPos.x + 1, worldPos.y + 1, worldPos.z);
            for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
                weights[ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE][i] = currentChunk.getWeight(0, 0, i);
                types  [ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE][i] = currentChunk.getType(0, 0, i);
            }
        } else {
            for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
                weights[ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE][i] = 0;
                types  [ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE][i] = 0;
            }
        }
        //edge X-Z
        if(
            worldPos.x + 1 < realm.getServerWorldData().getWorldSizeDiscrete() &&
            worldPos.z + 1 < realm.getServerWorldData().getWorldSizeDiscrete()
        ){
            currentChunk = serverTerrainManager.getChunk(worldPos.x + 1, worldPos.y, worldPos.z + 1);
            for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
                weights[ChunkData.CHUNK_SIZE][i][ChunkData.CHUNK_SIZE] = currentChunk.getWeight(0, i, 0);
                types  [ChunkData.CHUNK_SIZE][i][ChunkData.CHUNK_SIZE] = currentChunk.getType(0, i, 0);
            }
        } else {
            for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
                weights[ChunkData.CHUNK_SIZE][i][ChunkData.CHUNK_SIZE] = 0;
                types  [ChunkData.CHUNK_SIZE][i][ChunkData.CHUNK_SIZE] = 0;
            }
        }
        //edge Y-Z
        if(
            worldPos.y + 1 < realm.getServerWorldData().getWorldSizeDiscrete() &&
            worldPos.z + 1 < realm.getServerWorldData().getWorldSizeDiscrete()
        ){
            currentChunk = serverTerrainManager.getChunk(worldPos.x, worldPos.y + 1, worldPos.z + 1);
            for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
                weights[i][ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE] = currentChunk.getWeight(i, 0, 0);
                types  [i][ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE] = currentChunk.getType(i, 0, 0);
            }
        } else {
            for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
                weights[i][ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE] = 0;
                types  [i][ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE] = 0;
            }
        }
        if(
            worldPos.z + 1 < realm.getServerWorldData().getWorldSizeDiscrete() &&
            worldPos.y + 1 < realm.getServerWorldData().getWorldSizeDiscrete() &&
            worldPos.z + 1 < realm.getServerWorldData().getWorldSizeDiscrete()
        ){
            currentChunk = serverTerrainManager.getChunk(worldPos.x + 1, worldPos.y + 1, worldPos.z + 1);
            weights[ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE] = currentChunk.getWeight(0, 0, 0);
            types[ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE] = currentChunk.getType(0, 0, 0);
        } else {
            weights[ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE] = 0;
            types[ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE] = 0;
        }
    }
    
}
