package electrosphere.server.fluid.simulator.cellularautomata;

import electrosphere.server.fluid.manager.ServerFluidChunk;
import electrosphere.server.fluid.simulator.ServerFluidSimulator;
import electrosphere.server.terrain.manager.ServerTerrainChunk;

/**
 * Simulates server fluid chunks via cellular automata
 */
public class FluidCellularAutomataSimulator implements ServerFluidSimulator {

    static final float MAX_WEIGHT = 1.0f;

    static final float GRAVITY_DIFF = 0.04f;

    @Override
    public boolean simulate(ServerFluidChunk fluidChunk, ServerTerrainChunk terrainChunk, int worldX, int worldY, int worldZ) {
        
        float[][][] weights = fluidChunk.getWeights();
        float[][][] terrainWeights = terrainChunk.getWeights();

        //if true, alerts the server data cell to broadcast a new update message to all clients within it
        boolean update = false;

        for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){
            for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){
                for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){
                    if(weights[x][y][z] <= 0){
                        continue;
                    } else {
                        if(y > 0 && weights[x][y-1][z] < MAX_WEIGHT){
                            update = true;
                            weights[x][y][z] -= GRAVITY_DIFF;
                            weights[x][y-1][z] += GRAVITY_DIFF;
                        } else {
                            //propagate sideways
                            int[] offsetX = new int[]{-1,1,0,0};
                            int[] offsetZ = new int[]{0,0,-1,1};
                            for(int i = 0; i < 4; i++){
                                int realX = x + offsetX[i];
                                int realZ = z + offsetZ[i];
                                if(realX > 0 && realX < ServerTerrainChunk.CHUNK_DIMENSION - 1 &&
                                    realZ > 0 && realZ < ServerTerrainChunk.CHUNK_DIMENSION - 1
                                ){
                                    if(
                                        weights[realX][y][realZ] < weights[x][y][z] && 
                                        terrainWeights[realX][y][realZ] < MAX_WEIGHT
                                    ){
                                        update = true;
                                        weights[x][y][z] -= GRAVITY_DIFF;
                                        weights[realX][y][realZ] += GRAVITY_DIFF;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        return update;
    }
    

}
