package thebetweenlands.common.world.gen.biome.feature;

import java.util.Random;

import net.minecraft.block.Block;
import net.minecraft.init.Blocks;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.chunk.ChunkPrimer;
import net.minecraft.world.gen.NoiseGeneratorPerlin;
import thebetweenlands.common.world.gen.ChunkGeneratorBetweenlands;
import thebetweenlands.common.world.gen.biome.BiomeWeights;
import thebetweenlands.common.world.gen.biome.generator.BiomeGenerator.EnumGeneratorPass;

/**
 * Takes all base terrain below the water height, moves it above the water and creates small rivers
 */
public class FlatLandFeature extends BiomeFeature {
	private final int waterHeight;
	private final int terrainOffset;

	private NoiseGeneratorPerlin landNoiseGen;
	private double[] landNoise = new double[256];

	private NoiseGeneratorPerlin riverNoiseGen;
	private double[] riverNoise = new double[256];

	public FlatLandFeature(int waterHeight, int terrainOffset) {
		this.waterHeight = waterHeight;
		this.terrainOffset = terrainOffset;
	}

	@Override
	public void initializeGenerators(long seed, Biome biome) {
		Random rng = new Random(seed);
		this.landNoiseGen = new NoiseGeneratorPerlin(rng, 4);
		this.riverNoiseGen = new NoiseGeneratorPerlin(rng, 2);
	}

	@Override
	public void generateNoise(int chunkX, int chunkZ, Biome biome) {
		this.landNoise = this.landNoiseGen.getRegion(this.landNoise, (double) (chunkX * 16), (double) (chunkZ * 16), 16, 16, 0.06D, 0.06D, 1.0D);
		this.riverNoise = this.riverNoiseGen.getRegion(this.riverNoise, (double) (chunkX * 16), (double) (chunkZ * 16), 16, 16, 0.032D, 0.032D, 1.0D);
	}

	@Override
	public void replaceStackBlocks(int x, int z, double baseBlockNoise, ChunkPrimer chunkPrimer,
			ChunkGeneratorBetweenlands chunkGenerator, Biome[] biomesForGeneration, Biome biome, BiomeWeights biomeWeights,
			EnumGeneratorPass pass) {
		if(pass == EnumGeneratorPass.PRE_REPLACE_BIOME_BLOCKS) {
			float biomeWeight = biomeWeights.get(x, z, 0, 10);
			//Flatten terrain
			int lowestBlock = 0;
			for(int yOff = 0; yOff < this.waterHeight; yOff++) {
				int y = this.waterHeight - yOff;
				Block currentBlock = chunkPrimer.getBlockState(x, y, z).getBlock();
				if(currentBlock != chunkGenerator.layerBlock && currentBlock != Blocks.AIR) {
					lowestBlock = y;
					break;
				}
			}
			double noise = this.landNoise[x * 16 + z] / 18.0f;
			double riverNoise = Math.abs(this.riverNoise[x * 16 + z]) * 4.0D;
			riverNoise *= riverNoise * riverNoise * riverNoise * riverNoise;
			riverNoise *= 25.0D;
			int terrainHeight = (int)Math.ceil(Math.abs(noise * (this.waterHeight - lowestBlock + this.terrainOffset)));
			float riverThreshold = 6.0f * (terrainHeight + 2);
			double riverPercentage = 1.0D - (riverNoise / riverThreshold);
			float weight = (Math.min(biomeWeight + 0.5F, 1.0F) - 0.5F) * 2.0F;
			if(riverNoise < riverThreshold) {
				for(int y = lowestBlock; y < lerp((this.waterHeight + terrainHeight - riverPercentage * (terrainHeight + ((riverThreshold - riverNoise) / 16.0D))), lowestBlock, weight); y++) {
					chunkPrimer.setBlockState(x, y, z, chunkGenerator.baseBlockState);
				}
			} else {
				for(int y = lowestBlock; y < lerp((this.waterHeight + terrainHeight), lowestBlock, weight); y++) {
					chunkPrimer.setBlockState(x, y, z, chunkGenerator.baseBlockState);
				}
			}
		}
	}
}
