package dangerzone.biomes;

import java.util.Random;

import dangerzone.Chunk;
import dangerzone.DangerZone;
import dangerzone.World;

/*
 * This code is copyright Richard H. Clark, TheyCallMeDanger, OreSpawn, 2015-2020.
 * You may use this code for reference for modding the DangerZone game program,
 * and are perfectly welcome to cut'n'paste portions for your mod as well.
 * DO NOT USE THIS CODE FOR ANY PURPOSE OTHER THAN MODDING FOR THE DANGERZONE GAME.
 * DO NOT REDISTRIBUTE THIS CODE. 
 * 
 * This copyright remains in effect until January 1st, 2021. 
 * At that time, this code becomes public domain.
 * 
 * WARNING: There are bugs. Big bugs. Little bugs. Every size in-between bugs.
 * This code is NOT suitable for use in anything other than this particular game. 
 * NO GUARANTEES of any sort are given, either express or implied, and Richard H. Clark, 
 * TheyCallMeDanger, OreSpawn are not responsible for any damages, direct, indirect, or otherwise. 
 * You should have made backups. It's your own fault for not making them.
 * 
 * NO ATTEMPT AT SECURITY IS MADE. This code is USE AT YOUR OWN RISK.
 * Regardless of what you may think, the reality is, that the moment you 
 * connected your computer to the Internet, Uncle Sam, among many others, hacked it.
 * DO NOT KEEP VALUABLE INFORMATION ON INTERNET-CONNECTED COMPUTERS.
 * Or your phone...
 * 
 */


public class BiomeManager {
	
	public Biome biomes[];
	public static final int max_biomes = 128;
	public Random rand = new Random();
	public int nregistered = 0;
	float distsx[][] = new float[16][16];
	float distsz[][] = new float[16][16];
	float distsxz[][] = new float[16][16];

	
	public BiomeManager(){
		int i, j;
		float ti, tj;
		biomes = new Biome[max_biomes];
		for(i=0;i<max_biomes;i++)biomes[i] = null;
		
		//Make a little distance-from-center map
		for(i=-8;i<8;i++){
			for(j=-8;j<8;j++){
				ti = i+0.5f;
				tj = j+0.5f;
				distsxz[i+8][j+8] = (float)Math.sqrt(ti*ti + tj*tj);
				distsx[i+8][j+8] = (float)Math.sqrt(ti*ti);
				distsz[i+8][j+8] = (float)Math.sqrt(tj*tj);
			}
		}
	}
	
	public int registerBiome(Biome b){		
		int i;
		for(i=0;i<max_biomes;i++){
			if(biomes[i]==null)break;
		}
		if(i<0 || i>=max_biomes)return -1;
		biomes[i] = b;
		nregistered++;
		return i;
	}
	
	//chunk "random" value is average of all those around it too!
	//this determines the general size of biomes
	//TODO FIXME - highly dependent on NUMBER of biomes... need to fix... less than 10 works best for now...
	//TODO FIXME - could probably also pre-calculate the chunks in the area and keep it in a sliding map...
	//				even just a small cache of about 64 would be a lot faster...
	//TODO FIXME - the rarityfactor is highly dependent on # biomes too...
	public float getValueForChunk(long bioseed, int x, int y, int z){
		float retval = 0;
		int biomesizefactor = 7;
		
		for(int i=-biomesizefactor;i<=biomesizefactor;i++){
			for(int j=-biomesizefactor;j<=biomesizefactor;j++){
				rand.setSeed((bioseed*(long)((x>>4)+i)*(long)((z>>4)+j))+DangerZone.server_world.worldseed);
				retval += rand.nextFloat();
			}
		}		
		retval /= ((biomesizefactor*2) + 1)*((biomesizefactor*2) + 1);
		return retval;
	}

	
	//highest value from all the biomes gets it!
	public Biome getBiomeForChunk(Chunk c, int d, int x, int y, int z){
		if(nregistered == 0)return null;
		if(nregistered == 1)return biomes[0];
		
		//Save ourselves some work and see if the chunk remembers its biome!
		if(c != null && c.mybiome != null)return c.mybiome;
		
		//Save ourselves some work and see if the chunk remembers its biome!
		Chunk t = null;
		if(c == null){
			t = DangerZone.server_chunk_cache.getChunk(null, d, x, y, z);
			if(t != null){
				if(t.mybiome != null)return t.mybiome;
			}
		}
		
		//calculate which biome this chunk should be!
		int highest = 0;
		float highestval = -1;
		float thisval = 0;
		for(int i=0;i<nregistered;i++){
			thisval = getValueForChunk(biomes[i].bioRand, x, y, z) * biomes[i].rarityFactor;
			//System.out.printf("iiter %d bio %s,  seed %d, val %f\n", i, biomes[i].uniquename, biomes[i].bioRand+(x>>4)+(z>>4), thisval);
			if(thisval > highestval){
				highest = i;
				highestval = thisval;
			}
		}
		//System.out.printf("Returning %s with %f from %d, %d\n", biomes[highest].uniquename, highestval, x, z);
		if(c != null)c.mybiome = biomes[highest];
		if(t != null)t.mybiome = biomes[highest];
		return biomes[highest];
	}
	
	//Kind of meh, but gets (most of? some of?) the job done...
	public void generate(World w, int d, Biome b, Chunk c, int cx, int cz){
		int dirtlevelmap[][] = new int[16][16];
		int stonelevelmap[][] = new int[16][16];
		int bottomlevelmap[][] = new int[16][16];
		
		if(nregistered == 1){ 
			//easy - only one height generator
			b.generateheightmaps( w,  d,  c,  cx,  cz, dirtlevelmap, stonelevelmap, bottomlevelmap);		
			b.generate( w,  d,  c,  cx,  cz, dirtlevelmap, stonelevelmap, bottomlevelmap);
			return;
		}
		
		//build our starting height maps
		b.generateheightmaps( w,  d,  c,  cx,  cz, dirtlevelmap, stonelevelmap, bottomlevelmap);	

		//merge height differences between biomes
		//cheap and dirty smushing of biomes generated at different heights.
		//works good enough...
		//
		//fetches adjacent biomes and attempts to merge their heights into this one by generating what this one
		//would have been, and then averaging-ish. Fast, because it doesn't even look at other heights around it!
		//kind of meh, because it doesn't even look at other heights around it!
		int i, j;
		int mergedist = 1; //how wide the merging of different terrain is! dist[][] arrays must increase if this gets changed!
		
		for(i=-mergedist;i<=mergedist;i++){
			for(j=-mergedist;j<=mergedist;j++){
				if(i==0&&j==0)continue;
				Biome tb = w.getBiome(d, (cx+i)<<4, 0, (cz+j)<<4);
				if(!tb.uniquename.equals(b.uniquename)){
					//it is a different biome name, so we now have to generate a height map for ourselves in that biome!
					//we then merge in the differences...
					int tdirt[][] = new int[16][16];
					int tstone[][] = new int[16][16];
					int tbot[][] = new int[16][16];
					int ii, jj;
					float t;
					float u;
					float maxdist = 32; //really a factor of how hard to try adjusting. Actually better NOT trying too hard!
					int istart, iend;
					int jstart, jend;
					
					//generate a map for if this chunk was that biome...
					tb.generateheightmaps( w,  d,  c,  cx,  cz, tdirt, tstone, tbot);
					
					//has to be an easier way...
					jstart = istart = 0;
					jend = iend = 16;
					if(i == 0){
						if(j > 0){
							jstart = 8;
						}
						if(j < 0){
							jend = 8;						
						}
						for(ii=0;ii<16;ii++){
							for(jj=jstart;jj<jend;jj++){
								t = dirtlevelmap[ii][jj];
								u = tdirt[ii][jj];
								t *= (maxdist-distsz[ii][jj])/maxdist;
								t += u * distsz[ii][jj]/maxdist;
								dirtlevelmap[ii][jj] = (int)t;
								if(t - (int)t > 0.99f)dirtlevelmap[ii][jj]++;
								
								t = stonelevelmap[ii][jj];
								u = tstone[ii][jj];
								t *= (maxdist-distsz[ii][jj])/maxdist;
								t += u * distsz[ii][jj]/maxdist;
								stonelevelmap[ii][jj] = (int)t;
								if(t - (int)t > 0.99f)stonelevelmap[ii][jj]++;
								
								t = bottomlevelmap[ii][jj];
								u = tbot[ii][jj];
								t *= (maxdist-distsz[ii][jj])/maxdist;
								t += u * distsz[ii][jj]/maxdist;
								bottomlevelmap[ii][jj] = (int)t;
							}
						}
						
					}else if(j == 0){
						if(i > 0){
							istart = 8;
						}
						if(i < 0){
							iend = 8;							
						}
						for(ii=istart;ii<iend;ii++){
							for(jj=0;jj<16;jj++){
								t = dirtlevelmap[ii][jj];
								u = tdirt[ii][jj];
								t *= (maxdist-distsx[ii][jj])/maxdist;
								t += u * distsx[ii][jj]/maxdist;
								dirtlevelmap[ii][jj] = (int)t;
								if(t - (int)t > 0.99f)dirtlevelmap[ii][jj]++;
								
								t = stonelevelmap[ii][jj];
								u = tstone[ii][jj];
								t *= (maxdist-distsx[ii][jj])/maxdist;
								t += u * distsx[ii][jj]/maxdist;
								stonelevelmap[ii][jj] = (int)t;
								if(t - (int)t > 0.99f)stonelevelmap[ii][jj]++;
								
								t = bottomlevelmap[ii][jj];
								u = tbot[ii][jj];
								t *= (maxdist-distsx[ii][jj])/maxdist;
								t += u * distsx[ii][jj]/maxdist;
								bottomlevelmap[ii][jj] = (int)t;
							}
						}
					}else{
						//both are non-zero!
						if(i<0){
							istart = 0;
							iend = 8;
						}else{
							istart = 8;
							iend = 16;
						}
						if(j<0){
							jstart = 0;
							jend = 8;
						}else{
							jstart = 8;
							jend = 16;
						}
						maxdist = 50;
						for(ii=istart;ii<iend;ii++){
							for(jj=jstart;jj<jend;jj++){
								t = dirtlevelmap[ii][jj];
								u = tdirt[ii][jj];
								t *= (maxdist-distsxz[ii][jj])/maxdist;
								t += u * distsxz[ii][jj]/maxdist;
								dirtlevelmap[ii][jj] = (int)t;
								if(t - (int)t > 0.99f)dirtlevelmap[ii][jj]++;
								
								t = stonelevelmap[ii][jj];
								u = tstone[ii][jj];
								t *= (maxdist-distsxz[ii][jj])/maxdist;
								t += u * distsxz[ii][jj]/maxdist;
								stonelevelmap[ii][jj] = (int)t;
								if(t - (int)t > 0.99f)stonelevelmap[ii][jj]++;
								
								t = bottomlevelmap[ii][jj];
								u = tbot[ii][jj];
								t *= (maxdist-distsxz[ii][jj])/maxdist;
								t += u * distsxz[ii][jj]/maxdist;
								bottomlevelmap[ii][jj] = (int)t;
							}
						}
					}
					
				}			
			}
		}
					
	
		b.generate( w,  d,  c,  cx,  cz, dirtlevelmap, stonelevelmap, bottomlevelmap);
		
		
	}
		

}
