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.time.GameTimer;
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 LayerGenEarth extends LayerGen
{
	@Override
	public Vec3d getWind(Layer layer, Vec2d pos) {
		double t = GameTimer.getTime() / 1000.0;
		return new Vec3d(
				layer.noise_gens[2].eval(pos.x / 64.0, pos.y / 64.0, t) * 0.02, 0,
				layer.noise_gens[3].eval(pos.x / 64.0, pos.y / 64.0, t) * 0.02);
	}

	public double getTemperatureStatic(Layer layer, Vec2d pos)
	{
		// Get the noise generator
		NoiseGenerator terrain_noise = layer.noise_gens[0];
		return terrain_noise.eval(pos.x / World.BIOME_SIZE, pos.y / World.BIOME_SIZE) * 0.5 + 0.5;
	}
	
	@Override
	public double getTemperature(Layer layer, Vec2d pos)
	{
		// Get the noise generator
		double humidity = getHumidity(layer, pos);
		double light = MathHelpers.map(
				MathHelpers.sin(MathHelpers.map(GameTimer.getTime() % 720000, 0, 720000, 0, MathHelpers.TWO_PI)),
				-1, 1, 0.2 * humidity, 0.2);
		return layer.getTemperature(pos.toInt()) * (0.5 + light);
	}
	
	public double getHumidity(Layer layer, Vec2d pos)
	{
		// Get the noise generator
		NoiseGenerator terrain_noise = layer.noise_gens[1];
		return terrain_noise.eval(pos.x / World.BIOME_SIZE, pos.y / World.BIOME_SIZE) * 0.5 + 0.5;
	}
	
	@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), 		// Wind
				new NoiseGeneratorSimplex(lrand), 		// Wind
				new NoiseGeneratorSimplex(lrand, 4), 	// Water
		};
	}
	
	@Override
	public void generateChunk(Chunk chunk, Layer layer, Random rand, Vec2i c_pos)
	{
		// Is there going to be a portal 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));
		
		// Loop over the layer dimensions and create land
		for(int x=0;x<Chunk.CHUNK_SIZE.mx;x++) {
			for(int y=0;y<Chunk.CHUNK_SIZE.my;y++)
			{
				Vec2d pos = new Vec2d(c_pos.x * 16 + x, c_pos.y * 16 + y);
				Vec2i tpos = pos.toInt();
				
				double temperature = getTemperatureStatic(layer, pos);
				double humidity = getHumidity(layer, pos);
				boolean rockIsOre = rand.nextDouble() > 0.95;
				
				chunk.setTemperature(tpos, temperature);
				chunk.setHumidity(tpos, humidity);
				
				if(layer.noise_gens[4].eval(pos.x / 64.0, pos.y / 64.0) > (1 - humidity) * 1.5) {
					chunk.setFrontTile(Tiles.WATER.getDefaultState(), tpos);
				}
				
				else
				{
					byte rock_type = 0;
					boolean placed_stone = false;
					
					if(temperature < 0.35)
					{
						chunk.setBackTile(Tiles.SNOW.getDefaultState(), tpos);
						rock_type = (byte)1;
						
						if(rand.nextDouble() > MathHelpers.map(humidity, 0, 1, 0.99, 0.8)) {
							chunk.setFrontTile(new TileState(Tiles.TREE, (byte)1), tpos);
						}
					}
					
					else if(temperature > 0.65 && humidity < 0.5)
					{
						chunk.setBackTile(Tiles.SAND.getDefaultState(), tpos);
						rock_type = (byte)2;
						
						if(rand.nextDouble() > MathHelpers.map(humidity, 0, 0.5, 0.98, 0.95)) {
							chunk.setFrontTile(Tiles.CACTUS.getDefaultState(), tpos);
						}
					}
					
					else
					{
						chunk.setBackTile(Tiles.GRASS.getDefaultState(), tpos);
						
						if(rand.nextDouble() > MathHelpers.map(humidity, 0, 1, 0.99, 0.9)) {
							chunk.setFrontTile(Tiles.TREE.getDefaultState(), tpos);
						}
						
						else if(rand.nextDouble() > 0.9) {
							chunk.setFrontTile(Tiles.TALL_GRASS.getDefaultState(), tpos);
						}
						
						else if(temperature > 0.7 && humidity > 0.7 && Math.random() > 0.9998) {
							chunk.setFrontTile(Tiles.HEMP.getDefaultState(), tpos);
							while(Math.random() < 0.8) {
								chunk.setFrontTile(Tiles.HEMP.getDefaultState(),
										tpos.add(new Vec2d(
										MathHelpers.map(Math.random(), 0, 1, -2, 3),
										MathHelpers.map(Math.random(), 0, 1, -2, 3)).toInt()));
							}
						}
					}
					
					if(!placed_stone && rand.nextDouble() > 0.98)
					{
						if((rock_type == 0 || rock_type == 2) && rockIsOre)
						{
							// Set coal ore
							chunk.setFrontTile(Tiles.COAL.getDefaultState(), tpos);
						}
						
						else {
							chunk.setFrontTile(new TileState(Tiles.ROCK, rock_type), tpos);
						}
					}
				}
			}
		}
		
		if(portal) {
			chunk.setBackTile(new TileState(Tiles.PORTAL_DOWN, (short)1), portal_pos);
			chunk.setFrontTile(Tiles.LADDER.getDefaultState(), portal_pos);
			//chunk.setFrontTile(Tiles.BOSS_PORTAL.getDefaultState(), portal_pos);
		}
	}

	@Override
	public void spawnEntities(Layer layer, Random rand)
	{
		if(rand.nextDouble() > 0.98 && getEarthLight() < 0.4)
		{
			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.y - 128, (int)ppos.y + 128)),
					new Vec3d(0, 0, 0));
			
			boolean exists = false;
			
			for(Entity e : layer.getChunk(zombie.getPos().xz()).entities) {
				if(e.getClass() == zombie.getClass()) {
					exists = true;
					break;
				}
			}
			
			if(!exists && zombie.getPos().squareDistance(ppos) > 64) {
				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.3) return Tiles.SNOW.getDefaultState();
		if(temperature > 0.7 && humidity < 0.5) return Tiles.SAND.getDefaultState();
		
		return Tiles.DIRT.getDefaultState();
	}
	
	private double getEarthLight() {
		return MathHelpers.sin(Math.toRadians(getSunPosition())) * 0.5 + 0.5;
	}
	
	@Override
	public boolean hasSun() {
		return true;
	}
	
	@Override
	public double getSunPosition()
	{
		int timer = (int)((GameTimer.getTime() + 9000) % 72000);
		
		if(timer < 48000) {
			return MathHelpers.map(timer, 0, 48000, 0, 180);
		}
		
		else {
			return 270;
		}
	}
	
	@Override
	public ColorRange getLightLevel()
	{
		double light = getEarthLight() * 2 - 1;
		
		ColorRange daylightRange = new ColorRange(
				new Vec3d(-255/255.0, -244/255.0, -208/255.0),
				new Vec3d(205/255.0, 191/255.0, 162/255.0));
		
		Vec3d daylight = daylightRange.getColor(MathHelpers.sin(light * MathHelpers.HALF_PI) * 0.5 + 0.5);
		return new ColorRange(daylight.multiply(5/6.0), daylight);
	}
}
