// 
// Decompiled by Procyon v0.6.0
// 

package com.hypixel.hytale.server.worldgen.climate;

import com.hypixel.hytale.math.util.FastRandom;
import com.hypixel.hytale.math.util.HashUtil;
import com.hypixel.hytale.math.vector.Vector2i;
import java.util.concurrent.CompletableFuture;
import javax.annotation.Nonnull;

public class ClimateSearch
{
    public static final int STEP_SIZE = 100;
    public static final int DEFAULT_RADIUS = 5000;
    public static final int MAX_RADIUS = 10000;
    public static final double PI2 = 6.283185307179586;
    public static final long SEED_OFFSET = 1659788403585L;
    public static final double TARGET_SCORE = 0.75;
    
    public static CompletableFuture<Result> search(final int seed, final int cx, final int cy, final int startRadius, final int searchRadius, @Nonnull final Rule rule, @Nonnull final ClimateNoise noise, @Nonnull final ClimateGraph graph) {
        return CompletableFuture.supplyAsync(() -> {
            double bestScore = 0.0;
            final Vector2i bestPosition = new Vector2i(cx, cy);
            final int radius = Math.min(searchRadius, 10000);
            final FastRandom rng = new FastRandom(HashUtil.hash(seed, 1659788403585L));
            final long start_ms = System.currentTimeMillis();
            int r = startRadius;
            while (r <= radius) {
                final int steps = (int)Math.floor(6.283185307179586 * r / 100.0);
                final double inc = 6.283185307179586 / steps;
                final double off = 6.283185307179586 * rng.nextDouble();
                for (int i = 0; i < steps; ++i) {
                    final double t = i * inc + off;
                    final int x = cx + (int)(Math.cos(t) * r);
                    final int y = cy + (int)(Math.sin(t) * r);
                    final double score = collect(seed, x, y, noise, graph, rule);
                    if (score > bestScore) {
                        bestPosition.assign(x, y);
                        bestScore = score;
                    }
                }
                if (bestScore >= 0.75) {
                    break;
                }
                else {
                    r += 100;
                }
            }
            final long time_ms = System.currentTimeMillis() - start_ms;
            return new Result(bestPosition, bestScore, time_ms);
        });
    }
    
    private static double collect(final int seed, final int x, final int y, final ClimateNoise noise, final ClimateGraph graph, final Rule rule) {
        final double continent = noise.continent.get(seed, x, y);
        if (!rule.continent.test(continent)) {
            return 0.0;
        }
        final double temperature = noise.temperature.get(seed, x, y);
        if (!rule.temperature.test(temperature)) {
            return 0.0;
        }
        final double intensity = noise.intensity.get(seed, x, y);
        if (!rule.intensity.test(intensity)) {
            return 0.0;
        }
        final double fade = graph.getFadeRaw(temperature, intensity);
        if (!rule.fade.test(fade)) {
            return 0.0;
        }
        return rule.score(continent, temperature, intensity, fade);
    }
    
    record Result(Vector2i position, double score, long time_ms) {
        public String pretty() {
            final double score = this.score * 100.0;
            return String.format("Position: {%d, %d}, Score: %.2f%%, Time: %dms", this.position.x, this.position.y, score, this.time_ms);
        }
    }
    
    public static class Rule
    {
        public final Range continent;
        public final Range temperature;
        public final Range intensity;
        public final Range fade;
        public final transient double sumWeight;
        
        public Rule(final Range continent, final Range temperature, final Range intensity, final Range fade) {
            this.continent = continent;
            this.temperature = temperature;
            this.intensity = intensity;
            this.fade = fade;
            this.sumWeight = continent.weight + temperature.weight + intensity.weight + fade.weight;
        }
        
        public double score(final double continent, final double temperature, final double intensity, final double fade) {
            double sumScore = 0.0;
            sumScore += this.continent.score(continent) * this.continent.weight;
            sumScore += this.temperature.score(temperature) * this.temperature.weight;
            sumScore += this.intensity.score(intensity) * this.intensity.weight;
            sumScore += this.fade.score(fade) * this.fade.weight;
            return sumScore / this.sumWeight;
        }
    }
    
    public static class Range
    {
        public static final Range DEFAULT;
        public final double value;
        public final double radius;
        public final double weight;
        
        public Range(final double value, final double radius, final double weight) {
            this.value = value;
            this.radius = radius;
            this.weight = weight;
        }
        
        public double score(final double value) {
            final double dif = Math.min(this.radius, Math.abs(value - this.value));
            return 1.0 - dif / this.radius;
        }
        
        public boolean test(final double value) {
            return Math.abs(value - this.value) <= this.radius;
        }
        
        static {
            DEFAULT = new Range(0.0, 1.0, 0.0);
        }
    }
}
