package Client.Game.World.Utils;

import Client.Network.Client;

import java.util.Random;

public class PerlinNoise {

    //The functions were written originally in Java.
    //This class is from the internet. Just adjusted a little bit.

    public static final float AMPLITUDE = 70f;
    public static final int OCTAVES = 8;
    public static final float ROUGHNESS = 0.5f;

    private static final Random random = new Random();
    private static final int xOffset = 100;
    private static final int zOffset = 100;

    public static float generateHeight(float x, float z) {
        x = x < 0 ? -x : x;
        z = z < 0 ? -z : z;

        float total = 0;
        float d = (float) Math.pow(2, OCTAVES-1);
        for(int i=0;i<OCTAVES;i++){
            float freq = (float) (Math.pow(2, i) / d);
            float amp = (float) Math.pow(ROUGHNESS, i) * AMPLITUDE;
            total += getInterpolatedNoise((x+xOffset)*freq, (z + zOffset)*freq) * amp;
        }

        return (float) (int) total;

    }

    private static float getInterpolatedNoise(float x, float z){
        int intX = (int) x;
        int intZ = (int) z;
        float fracX = x - intX;
        float fracZ = z - intZ;

        float v1 = getSmoothNoise(intX, intZ);
        float v2 = getSmoothNoise(intX + 1, intZ);
        float v3 = getSmoothNoise(intX, intZ + 1);
        float v4 = getSmoothNoise(intX + 1, intZ + 1);
        float i1 = interpolate(v1, v2, fracX);
        float i2 = interpolate(v3, v4, fracX);
        return interpolate(i1, i2, fracZ);
    }

    private static float interpolate(float a, float b, float blend){
        double theta = blend * Math.PI;
        float f = (float)(1f - Math.cos(theta)) * 0.5f;
        return a * (1f - f) + b * f;
    }

    private static float getSmoothNoise(int x, int z) {
        float corners = (getNoise(x - 1, z - 1) + getNoise(x + 1, z - 1) + getNoise(x - 1, z + 1)
                + getNoise(x + 1, z + 1)) / 16f;
        float sides = (getNoise(x - 1, z) + getNoise(x + 1, z) + getNoise(x, z - 1)
                + getNoise(x, z + 1)) / 8f;
        float center = getNoise(x, z) / 4f;
        return corners + sides + center;
    }

    private static float getNoise(int x, int z) {
        random.setSeed(x * 49632 + z * 325176 + Client.seed);
        return random.nextFloat() * 2f - 1f;
    }
}