package ca.recrown.islandsurvivalcraft.world.generation.shaders;

import java.util.Random;

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

import ca.recrown.islandsurvivalcraft.world.IslandWorldMap;

public class WorldHeightShader {
    private final IslandWorldMap mapper;
    private final SimplexOctaveGenerator noiseGenerator;
    private final int seaLevel;
    private final int worldHeight;
    private final int minimumHeight;

    public WorldHeightShader(Random random, IslandWorldMap islandLocator, int seaLevel, int worldHeight, int minimumHeight) {
        this.mapper = islandLocator;
        this.seaLevel = seaLevel;
        this.worldHeight = worldHeight;
        this.minimumHeight = minimumHeight;
        this.noiseGenerator = new SimplexOctaveGenerator(random, 8);
        this.noiseGenerator.setScale(0.0225d);
    }

    public int getTerrainHeight(int worldX, int worldZ, Biome[] biomeSet) {
        int height = 0;
        String biomeName = biomeSet[0].name().toLowerCase();
        if (!mapper.isLand(worldX, worldZ)) {
            height = (int) calculateTerrainFactor(worldX, worldZ, seaLevel * 0.8d, 1.7d, 0.5d, 1d, 0.1d);
        } else if (biomeName.contains("beach")) {
            height = (int) (getIslandBiomeHeight(worldX, worldZ, biomeSet[1], 10d));
        } else if (biomeName.contains("shore")) {
            height = (int) (getIslandBiomeHeight(worldX, worldZ, biomeSet[1], -30d));
        } else {
            height = getIslandBiomeHeight(worldX, worldZ, biomeSet[0], 0d) + 1;
        }

        height = Math.max(minimumHeight, height);
        if (height > worldHeight) throw new IllegalStateException(String.format("Resulting height is greater than world height! Current biome set: %s, maximum height: %d, minimum height %d", biomeSet, worldHeight, minimumHeight));
        return height;
    }

    private int getIslandBiomeHeight(int worldX, int worldZ, Biome biome, double heightModifier) {
        int res = 0;
        String biomeName = biome.name().toLowerCase();
        if (biomeName.contains("hills")) {
            res = (int) calculateTerrainFactor(worldX, worldZ, 60d + heightModifier, 1.5d, 0.5d);
        } else if (biomeName.contains("mountains")) {
            res = (int) calculateTerrainFactor(worldX, worldZ, 200d + heightModifier, 1.8d, 0.5d);
        } else if (biomeName.contains("plateau")) {
            res = (int) Math.min(calculateTerrainFactor(worldX, worldZ, 60d + heightModifier), seaLevel + 30d);
        } else if (biomeName.contains("modified")) {
            res = (int) calculateTerrainFactor(worldX, worldZ, 80d + heightModifier);
        } else if (biomeName.contains("shattered")) {
            res = (int) calculateTerrainFactor(worldX, worldZ, 90d + heightModifier);
        } else if (biomeName.contains("tall")) {
            res = (int) calculateTerrainFactor(worldX, worldZ, 80d + heightModifier);
        } else {
            res = (int) calculateTerrainFactor(worldX, worldZ, 35d + heightModifier);
        }
        return res;
    }

    private double calculateTerrainFactor(int worldX, int worldZ, double heightFactor, double freq, double amplitude, double shift, double exaggerationFactor) {
        double res = seaLevel;
        double shapeValue = (noiseGenerator.noise(worldX, worldZ, freq, amplitude, true) + shift) / (shift + 1d);
        if (shift == 1d && exaggerationFactor != 1d) shapeValue = Math.pow(shapeValue, exaggerationFactor);
        double islandValue = mapper.getWorldValue(worldX, worldZ);
        res += (islandValue) * (shapeValue * heightFactor);
        return res;
    }

    private double calculateTerrainFactor(int worldX, int worldZ, double heightFactor, double freq, double amp) {
        return calculateTerrainFactor(worldX, worldZ, heightFactor, freq, amp, 0.75d, 1d);
    }

    private double calculateTerrainFactor(int worldX, int worldZ, double heightFactor) {
        return calculateTerrainFactor(worldX, worldZ, heightFactor, 1.6d, 0.5d);
    }
 }