package projectzombie.world.layer.layergen;

import java.util.Random;

import gl_engine.MathHelpers;
import gl_engine.vec.Vec2d;
import gl_engine.vec.Vec2i;
import gl_engine.vec.Vec3d;
import projectzombie.Main;
import projectzombie.entity.Entity;
import projectzombie.entity.EntityZombie;
import projectzombie.init.Tiles;
import projectzombie.tiles.Tile;
import projectzombie.util.ColorRange;
import projectzombie.util.TileState;
import projectzombie.util.math.random.NoiseGenerator;
import projectzombie.util.math.random.NoiseGeneratorSimplex;
import projectzombie.util.math.random.RandomHelpers;
import projectzombie.world.World;
import projectzombie.world.chunk.Chunk;
import projectzombie.world.layer.Layer;

public class LayerGenCaves extends LayerGen
{
	private static final TileState[] ores = new TileState[]
	{
		new TileState(Tiles.ROCK, 0), // Stone
		new TileState(Tiles.ROCK, 0), // Stone
		new TileState(Tiles.ROCK, 0), // Stone
		new TileState(Tiles.ROCK, 0), // Stone
		new TileState(Tiles.ROCK, 0), // Stone
		new TileState(Tiles.ORE, 0), // Iron
		new TileState(Tiles.ORE, 0), // Iron
		new TileState(Tiles.ORE, 0), // Iron
		new TileState(Tiles.ORE, 0), // Iron
		new TileState(Tiles.ORE, 1), // Copper
		new TileState(Tiles.ORE, 1), // Copper
		new TileState(Tiles.ORE, 1), // Copper
		new TileState(Tiles.ORE, 2), // Tin
		new TileState(Tiles.ORE, 2), // Tin
		new TileState(Tiles.ORE, 3), // Gold
	};
	
	public double getTemperatureStatic(Layer layer, Vec2d pos)
	{
		// Get the noise generator
		NoiseGenerator terrain_noise = layer.noise_gens[0];
		return MathHelpers.map(terrain_noise.eval(pos.x / World.BIOME_SIZE, pos.y / World.BIOME_SIZE), -1, 1, 0, 0.6);
	}
	
	@Override
	public double getTemperature(Layer layer, Vec2d pos) {
		return layer.getTemperature(pos.toInt());
	}
	
	public double getHumidity(Layer layer, Vec2d pos)
	{
		// Get the noise generator
		NoiseGenerator terrain_noise = layer.noise_gens[1];
		return MathHelpers.map(terrain_noise.eval(pos.x / World.BIOME_SIZE, pos.y / World.BIOME_SIZE), -1, 1, 0, 0.8);
	}
	
	@Override
	public void init(Layer layer) {
		super.init(layer);
		
		Random rand = new Random(layer.seed);
		Random lrand = new Random(layer.lseed);
		
		layer.noise_gens = new NoiseGenerator[] {
				new NoiseGeneratorSimplex(rand, 64),  	// Temperature
				new NoiseGeneratorSimplex(rand, 64), 	// Humidity
				
				new NoiseGeneratorSimplex(lrand, 4), 	// Cave structure
				new NoiseGeneratorSimplex(lrand, 4), 	// Caverns
				new NoiseGeneratorSimplex(lrand, 4),	// Wall width
		};
	}
	
	@Override
	public void generateChunk(Chunk chunk, Layer layer, Random rand, Vec2i c_pos)
	{
		// Is there going to be a portal up in this chunk
		boolean portal = PortalSpawnrates.WorldCavePortal(rand);
		Vec2i portal_pos = null;
		if(portal) portal_pos = new Vec2i(
				RandomHelpers.randrange(rand, Chunk.CHUNK_SIZE.mx),
				RandomHelpers.randrange(rand, Chunk.CHUNK_SIZE.my));
		
		// Is there going to be a portal down in this chunk
		boolean portal_down = PortalSpawnrates.CaveLavaCavePortal(rand);
		Vec2i portal_down_pos = null;
		if(portal_down) portal_down_pos = new Vec2i(
				RandomHelpers.randrange(rand, Chunk.CHUNK_SIZE.mx),
				RandomHelpers.randrange(rand, Chunk.CHUNK_SIZE.my));
		
		// Loop over the chunk
		for(int x=0;x<Chunk.CHUNK_SIZE.mx;x++) {
			for(int y=0;y<Chunk.CHUNK_SIZE.my;y++)
			{
				// Get the x and y in the world
				int cx = x + c_pos.x * Chunk.CHUNK_SIZE.mx;
				int cy = y + c_pos.y * Chunk.CHUNK_SIZE.my;
				
				Vec2i pos = new Vec2i(x, y);
				
				double temperature = getTemperatureStatic(layer, new Vec2d(cx, cy));
				double humidity = getHumidity(layer, new Vec2d(cx, cy));
				
				chunk.setTemperature(pos, temperature);
				chunk.setHumidity(pos, humidity);
				
				// Get the noise value and the position vector
				double noise_n = 100 - MathHelpers.positive( layer.noise_gens[2].eval(cx/16.0, cy/16.0) * 100 );
				double noise_c = layer.noise_gens[3].eval(cx/32.0, cy/32.0);
				double noise_w = layer.noise_gens[4].eval(cx/32.0, cy/32.0) * 30 + 90;
				boolean cavern = false;
				
				Tile floor = Tiles.STONE;
				Tile wall = Tiles.WALL;
				
				if(temperature < 0.25)
				{
					floor = Tiles.ICE;
					wall = Tiles.ICE_WALL;
					cavern = noise_c < -0.5 && temperature < 0.15;
				}
				
				if(temperature > 0.35 && humidity < 0.4)
				{
					floor = Tiles.SANDSTONE;
					wall = Tiles.SANDSTONE_WALL;
					cavern = noise_c < -0.5 && temperature > 0.45;
				}
				
				if(noise_n > noise_w || cavern)
				{
					chunk.setBackTile(floor.getDefaultState(), pos);
					
					if((floor == Tiles.STONE || floor == Tiles.SANDSTONE) && rand.nextDouble() > 0.99) {
						chunk.setFrontTile(ores[(int)(rand.nextDouble() * ores.length)], pos);
					}
				}
				
				else {
					chunk.setBackTile(wall.getDefaultState(), pos);
				}
			}
		}
		
		if(portal) {
			chunk.setFrontTile(new TileState(Tiles.LADDER_UP, (short)0), portal_pos);
		}
		
		if(portal_down) {
			chunk.setBackTile(new TileState(Tiles.PORTAL_DOWN, (short)2), portal_down_pos);
			chunk.setFrontTile(Tiles.LADDER.getDefaultState(), portal_down_pos);
		}
		
		// Spawn entities
		Entity zombie = new EntityZombie(new Vec3d(
				chunk.c_pos.x * 16 + RandomHelpers.randrange(rand, 0, 16), 0,
				chunk.c_pos.y * 16 + RandomHelpers.randrange(rand, 0, 16)),
				new Vec3d(0, 0, 0));
		
		Vec2i zpos = zombie.getPos().xz().toInt();
		
		if(
			chunk.getBackTile(zpos).tile == getTileDestroyed(layer, zpos).tile
		) {
			chunk.spawnEntity(zombie);
		}
	}

	@Override
	public void spawnEntities(Layer layer, Random rand)
	{
		if(rand.nextDouble() > 0.9)
		{
			Vec3d ppos = Main.player.getPos();
			
			Entity zombie = new EntityZombie(new Vec3d(
					RandomHelpers.randrange(rand, (int)ppos.x - 128, (int)ppos.x + 128), 0,
					RandomHelpers.randrange(rand, (int)ppos.z - 128, (int)ppos.z + 128)),
					new Vec3d(0, 0, 0));
			
			Vec3d zpos = zombie.getPos();
			boolean exists = false;
			
			for(Entity e : layer.getChunk(zpos.xz()).entities) {
				if(e.getClass() == zombie.getClass()) {
					exists = true;
					break;
				}
			}
			
			Vec2i ztpos = zombie.getPos().xz().toInt();
			
			if(
				!exists && layer.getBackTile(ztpos).tile == getTileDestroyed(layer, ztpos).tile &&
				zpos.squareDistance(ppos) > 32
			) {
				layer.spawnEntity(zombie);
			}
		}
	}
	
	@Override
	public TileState getTileDestroyed(Layer layer, Vec2i pos)
	{
		double temperature = getTemperatureStatic(layer, pos.toDouble());
		double humidity = getHumidity(layer, pos.toDouble());
		
		if(temperature < 0.25) return Tiles.ICE.getDefaultState();
		if(temperature > 0.35 && humidity < 0.4) return Tiles.SANDSTONE.getDefaultState();
		
		return Tiles.STONE.getDefaultState();
	}
	
	@Override
	public ColorRange getLightLevel() {
		return new ColorRange(new Vec3d(0, 0, 0), new Vec3d(0, 0, 0));
	}
	
	

}
