//https://www.redblobgames.com/maps/terrain-from-noise/
package com.mygdx.game.Dimension;

import com.github.czyzby.kiwi.util.tuple.immutable.Pair;
import com.mygdx.game.OpenSimplexNoise;

import java.util.HashMap;

import static com.mygdx.game.Restrictions.*;

public class Chunks {
    public static HashMap<Pair<Integer, Integer>, Block> blocks = new HashMap<>();
    private long seed = SEED;

    public void generateChunk(int x, int y){
        int startX = x << CHUNK_SHIFT;
        int startY = y << CHUNK_SHIFT;
        int endX = startX + CHUNK_SIZE;
        int endY = startY + CHUNK_SIZE;

        //Going from start of selected chunk to end of selected chunk in x and y
        for (int i = startX; i != endX; i++){
            for (int j = startY; j != endY; j++) {
                blocks.put(new Pair<>(i, j), getTerrain(i, j));
            }
        }
    }

    public double noise1(double nx, double ny) {
        OpenSimplexNoise gen1 = new OpenSimplexNoise(seed+1);

        return gen1.eval(nx, ny)/2 + 0.5;
    }

    public double noise2(double nx, double ny) {
        OpenSimplexNoise gen2 = new OpenSimplexNoise(seed);

        return gen2.eval(nx, ny)/2 + 0.5;
    }

    public Boolean isEmpty(int x, int y){
        return blocks.get(new Pair<>(x * 8, y * 8)) == null;
    }

    public Block getTerrain(int x, int y) {
        double moisture, elevation,nx,ny;
        double scale = 0.01;
        nx = x * scale;
        ny = y * scale;
        elevation = (0.17 * noise1( 1 * nx,  1 * ny)
                + 0.42 * noise1( 2 * nx,  2 * ny)
                + 0.16 * noise1( 4 * nx,  4 * ny)
                + 0.00 * noise1( 8 * nx,  8 * ny)
                + 0.00 * noise1(16 * nx, 16 * ny)
                + 0.03 * noise1(32 * nx, 32 * ny));
        elevation /= (0.17+0.42+0.16+0.00+0.00+0.03);
        elevation = Math.pow(elevation, 3.00);
        moisture = (1.00 * noise2( 1 * nx,  1 * ny)
                + 0.00 * noise2( 2 * nx,  2 * ny)
                + 1.00 * noise2( 4 * nx,  4 * ny)
                + 0.00 * noise2( 8 * nx,  8 * ny)
                + 0.00 * noise2(16 * nx, 16 * ny)
                + 0.00 * noise2(32 * nx, 32 * ny));
        moisture /= (0.00+1.00+0.00+0.00+0.00+0.00);

        return getBiomeBlock(moisture, elevation);
    }

    public Block getBiomeBlock(double moisture, double elevation) {
        String biome = getBiome(moisture,elevation);
        if (biome.equals("Tundra")) return BlockMaterials.materials.get("snow");
        if (biome.equals("Taiga")) return BlockMaterials.materials.get("snow");
        if (biome.equals("Snow")) return BlockMaterials.materials.get("snow");

        if (biome.equals("Grassland")) return BlockMaterials.materials.get("grass");
        if (biome.equals("Shrubland")) return BlockMaterials.materials.get("grass");
        if (biome.equals("TemperateDeciduousForest")) return BlockMaterials.materials.get("grass");

        if (biome.equals("TemperateRainForest")) return BlockMaterials.materials.get("grass");
        if (biome.equals("TropicalSeasonalForest")) return BlockMaterials.materials.get("grass");
        if (biome.equals("TropicalForest")) return BlockMaterials.materials.get("grass");
        if (biome.equals("TropicalRainForest")) return BlockMaterials.materials.get("grass");

        if (biome.equals("Beach")) return BlockMaterials.materials.get("sand");
        if (biome.equals("Bare")) return BlockMaterials.materials.get("sand");
        if (biome.equals("Scorched")) return BlockMaterials.materials.get("sand");
        if (biome.equals("TemperateDesert")) return BlockMaterials.materials.get("sand");
        if (biome.equals("SubtropicalDesert")) return BlockMaterials.materials.get("sand");

        return BlockMaterials.materials.get("water");
    }

    public String getBiome(double moisture,double elevation){
        if (elevation < 0.1) return "Ocean";
        if (elevation < 0.12) return "Beach";

        if (elevation > 0.8) {
            if (moisture < 0.1) return "Scorched";
            if (moisture < 0.2) return "Bare";
            if (moisture < 0.5) return "Tundra";
            return "Snow";
        }

        if (elevation > 0.6) {
            if (moisture < 0.33) return "TemperateDesert";
            if (moisture < 0.66) return "Shrubland";
            return "Taiga";
        }

        if (elevation > 0.3) {
            if (moisture < 0.16) return "TemperateDesert";
            if (moisture < 0.50) return "Grassland";
            if (moisture < 0.83) return "TemperateDeciduousForest";
            return "TemperateRainForest";
        }

        if (moisture < 0.16) return "SubtropicalDesert";
        if (moisture < 0.33) return "Grassland";
        if (moisture < 0.66) return "TropicalSeasonalForest";
        return "TropicalRainForest";
    }

}
