package ca.recrown.islandsurvivalcraft.world.generation;

import java.util.Random;

import org.bukkit.Chunk;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Biome;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.world.ChunkLoadEvent;
import org.bukkit.generator.ChunkGenerator;

import ca.recrown.islandsurvivalcraft.Utilities;
import ca.recrown.islandsurvivalcraft.caching.Cache;
import ca.recrown.islandsurvivalcraft.datatypes.Point2;
import ca.recrown.islandsurvivalcraft.world.BiomeSelector;
import ca.recrown.islandsurvivalcraft.world.IslandWorldMapper;
import ca.recrown.islandsurvivalcraft.world.shaders.WorldHeightShader;

public class IslandWorldChunkGenerator extends ChunkGenerator implements Listener {
    private final Cache<Point2, Double> blockValueCache = new Cache<>(102400);
    private final Cache<Point2, Biome[]> biomeCache = new Cache<>(102400);
    private final Cache<Point2, Boolean> chunkExistenceCache = new Cache<>(16384);
    private volatile World currentWorld;

    @Override
    public ChunkData generateChunkData(World world, Random random, int x, int z, BiomeGrid biome) {
        this.currentWorld = world;
        IslandWorldMapper mapper = new IslandWorldMapper(random, blockValueCache);
        TemperatureMapGenerator temperatureMapGenerator = new TemperatureMapGenerator(world.getSeed());
        BiomeSelector biomeSelector = new BiomeSelector(random);
        biomeSelector.initialize();
        WorldHeightShader heightShader = new WorldHeightShader(world.getSeed(), mapper, world.getSeaLevel(), world.getMaxHeight(), 3);
        BiomeGenerator biomeGenerator = new UniBiomeIslandGenerator();

        int maxHeight = world.getMaxHeight();
        ChunkData chunkData = createChunkData(world);

        Biome[][] biomes = new Biome[Utilities.CHUNK_SIZE][Utilities.CHUNK_SIZE];
        for (int localX = 0; localX < Utilities.CHUNK_SIZE; localX++) {
            for (int localZ = 0; localZ < Utilities.CHUNK_SIZE; localZ++) {
                if (biomes[localX][localZ] == null) {
                    biomeGenerator.generateBiomeColumn(biomes, world, x, z, localX, localZ, mapper, biomeSelector, temperatureMapGenerator, biomeCache, chunkExistenceCache);
                }
                if (biomes[localX][localZ] == null) throw new IllegalStateException("Biome was null.");
                for (int y = 0; y < maxHeight; y++) {
                    biome.setBiome(x, y, z, biomes[localX][localZ]);
                }
                int worldX = 16 * x + localX;
                int worldZ = 16 * z + localZ;
                int height = heightShader.getAltitude(worldX, worldZ, biomes[localX][localZ]);
                chunkData.setRegion(localX, 1, localZ, localX+1, height, localZ+1, Material.DIAMOND_BLOCK);
            }
        }
        chunkData.setRegion(0, 0, 0, 16, 1, 16, Material.BEDROCK);

        return chunkData;
    }

    @EventHandler(priority = EventPriority.MONITOR)
    public void onChunkLoaded(ChunkLoadEvent event) {
        if (event.isNewChunk()) {
            if (currentWorld == event.getWorld()) {
                Chunk chunk = event.getChunk();
                
                chunkExistenceCache.set(new Point2(chunk.getX(), chunk.getZ()), true);
            }
        }
    }
}