package ca.recrown.islandsurvivalcraft.world.shaders;

import java.security.InvalidAlgorithmParameterException;
import java.util.Random;

import org.bukkit.block.Biome;
import org.bukkit.util.noise.SimplexOctaveGenerator;

import ca.recrown.islandsurvivalcraft.world.IslandWorldMapper;

public class WorldHeightShader {
    private Random random;
    private SimplexOctaveGenerator noiseGenerator;
    private IslandWorldMapper islandLocator;
    private int seaLevel;
    private int worldHeight;
    private int minimumHeight;
    private final float probabilityOfAdditive = 0.75f;

    public WorldHeightShader(long seed, IslandWorldMapper islandLocator, int seaLevel, int worldHeight, int minimumHeight) {
        random = new Random(seed);
        noiseGenerator = new SimplexOctaveGenerator(random, 2);
        noiseGenerator.setScale(0.075D);
        this.islandLocator = islandLocator;
        this.seaLevel = seaLevel;
        this.worldHeight = worldHeight;
        this.minimumHeight = minimumHeight;
    }

    public int getAltitude(int worldX, int worldZ, Biome biome) throws InvalidAlgorithmParameterException {
        double modifier = getHeightModifier(worldX, worldZ);
        int baseValue = calculateTerrainHeight(worldX, worldZ);
        int height = 0;
        String biomeName = biome.name().toLowerCase();
        if (biomeName.contains("hills")) {
            height = (int) (modifier * 20D + baseValue);
        } else if (biomeName.contains("mountains")) {
            height = (int) (modifier * 45D + baseValue);
        } else if (biomeName.contains("plateau")) {
            height = (int) (Math.min(60, Math.max(2, modifier * 80D)) + baseValue);
        } else if (biomeName.contains("modified")) {
            height = (int) (modifier * 25D + baseValue);
        } else if (biomeName.contains("shattered")) {
            height = (int) (modifier * 35D + baseValue);
        } else if (biomeName.contains("tall")) {
            height = (int) (modifier * 30D + baseValue);
        } else if (!biomeName.contains("ocean")) {
            height = (int) (getNormalizedHeightModifier(worldX, worldZ) * 15D + baseValue);
        } else {
            height = (int) (getNormalizedHeightModifier(worldX, worldZ) * 5D + baseValue);
        }

        if (height > worldHeight) throw new InvalidAlgorithmParameterException("Resulting height is greater than world height! Biome this occurred on: " + biomeName);
        return height;
    }

    private int calculateTerrainHeight(int worldX, int worldZ) {
        double islandValue = islandLocator.getWorldBlockValue(worldX, worldZ);
        if (islandValue >= 0) {
            return seaLevel;
        }
        return (int) Math.max((- islandValue) * seaLevel, minimumHeight);
    }

    private double getHeightModifier(int worldX, int worldZ) {
        return (2 * probabilityOfAdditive - noiseGenerator.noise(worldX, worldZ, 0.5D, 0.5D, true));
    }

    private double maxHeightModifier() {
        return 2 * probabilityOfAdditive + 1;
    }

    private double minHeightModifier() {
        return 2 * probabilityOfAdditive - 1;
    }

    private double getNormalizedHeightModifier(int worldX, int worldZ) {
        double heightModifier = getHeightModifier(worldX, worldZ);
        if (heightModifier > 0) {
            return heightModifier / maxHeightModifier();
        }
        return - heightModifier / minHeightModifier();
    }
 }