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

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

import com.hypixel.hytale.server.worldgen.chunk.ChunkGenerator;
import com.hypixel.hytale.math.util.ChunkUtil;
import javax.annotation.Nullable;
import com.hypixel.hytale.server.worldgen.chunk.ZoneBiomeResult;
import javax.annotation.Nonnull;
import java.util.concurrent.TimeUnit;
import com.hypixel.hytale.server.worldgen.util.cache.ConcurrentSizedTimeoutCache;
import com.hypixel.hytale.server.worldgen.util.ObjectPool;

public class ChunkGeneratorCache
{
    private static final int CONCURRENCY_LEVEL;
    private final ZoneBiomeResultFunction zoneBiomeResultFunction;
    private final BiomeCountFunction biomeCountFunction;
    private final HeightFunction heightFunction;
    private final HeightNoiseFunction heightNoiseFunction;
    private final ObjectPool<CoordinateCache.CoordinateKey> keyPool;
    private final ConcurrentSizedTimeoutCache<CoordinateCache.CoordinateKey, CoreDataCacheEntry> cache;
    
    public ChunkGeneratorCache(final ZoneBiomeResultFunction zoneBiomeResultFunction, final BiomeCountFunction biomeCountFunction, final HeightFunction heightFunction, final HeightNoiseFunction heightNoiseFunction, final int maxSize, final long expireAfterSeconds) {
        this.zoneBiomeResultFunction = zoneBiomeResultFunction;
        this.biomeCountFunction = biomeCountFunction;
        this.heightFunction = heightFunction;
        this.heightNoiseFunction = heightNoiseFunction;
        this.keyPool = new ObjectPool<CoordinateCache.CoordinateKey>(maxSize, CoordinateCache.CoordinateKey::new);
        this.cache = new ConcurrentSizedTimeoutCache<CoordinateCache.CoordinateKey, CoreDataCacheEntry>(maxSize, ChunkGeneratorCache.CONCURRENCY_LEVEL, expireAfterSeconds, TimeUnit.SECONDS, this::computeKey, this::computeValue, this::destroyEntry);
    }
    
    @Nonnull
    public CoreDataCacheEntry get(final int seed, final int x, final int z) {
        return this.cache.get(localKey().setLocation(seed, x, z));
    }
    
    public ZoneBiomeResult getZoneBiomeResult(final int seed, final int x, final int z) {
        return this.get(seed, x, z).zoneBiomeResult;
    }
    
    @Nullable
    public InterpolatedBiomeCountList getBiomeCountResult(final int seed, final int x, final int z) {
        final CoreDataCacheEntry entry = this.get(seed, x, z);
        this.ensureBiomeCountList(seed, x, z, entry);
        return entry.biomeCountList;
    }
    
    public void putHeight(final int seed, final int x, final int z, final int height) {
        this.get(seed, x, z).height = height;
    }
    
    public int getHeight(final int seed, final int x, final int z) {
        final CoreDataCacheEntry entry = this.get(seed, x, z);
        this.ensureHeight(seed, x, z, entry);
        return entry.height;
    }
    
    public void ensureBiomeCountList(final int seed, final int x, final int z, @Nonnull final CoreDataCacheEntry entry) {
        if (entry.biomeCountList == null) {
            final InterpolatedBiomeCountList list = new InterpolatedBiomeCountList();
            this.biomeCountFunction.compute(seed, x, z, list);
            entry.biomeCountList = list;
        }
    }
    
    public void ensureHeight(final int seed, final int x, final int z, @Nonnull final CoreDataCacheEntry entry) {
        if (entry.height == -1) {
            entry.height = this.heightFunction.compute(seed, x, z);
        }
    }
    
    public void ensureHeightNoise(final int seed, final int x, final int z, @Nonnull final CoreDataCacheEntry entry) {
        if (entry.heightNoise == Double.NEGATIVE_INFINITY) {
            this.ensureBiomeCountList(seed, x, z, entry);
            entry.heightNoise = this.heightNoiseFunction.compute(entry.biomeCountList);
        }
    }
    
    @Nonnull
    protected final CoordinateCache.CoordinateKey computeKey(final CoordinateCache.CoordinateKey key) {
        return this.keyPool.apply(key);
    }
    
    @Nonnull
    protected final CoreDataCacheEntry computeValue(@Nonnull final CoordinateCache.CoordinateKey key) {
        final int seed = key.seed();
        final long coord = key.coord();
        final int x = ChunkUtil.xOfChunkIndex(coord);
        final int z = ChunkUtil.zOfChunkIndex(coord);
        return new CoreDataCacheEntry(this.zoneBiomeResultFunction.compute(seed, x, z));
    }
    
    protected final void destroyEntry(final CoordinateCache.CoordinateKey key, final CoreDataCacheEntry value) {
        this.keyPool.recycle(key);
    }
    
    @Nonnull
    protected static CoordinateCache.CoordinateKey localKey() {
        return ChunkGenerator.getResource().cacheCoordinateKey;
    }
    
    static {
        CONCURRENCY_LEVEL = Math.max(1, ChunkGenerator.POOL_SIZE / 2);
    }
    
    @FunctionalInterface
    public interface ZoneBiomeResultFunction
    {
        ZoneBiomeResult compute(final int p0, final int p1, final int p2);
    }
    
    @FunctionalInterface
    public interface BiomeCountFunction
    {
        void compute(final int p0, final int p1, final int p2, final InterpolatedBiomeCountList p3);
    }
    
    @FunctionalInterface
    public interface HeightFunction
    {
        int compute(final int p0, final int p1, final int p2);
    }
    
    @FunctionalInterface
    public interface HeightNoiseFunction
    {
        double compute(final InterpolatedBiomeCountList p0);
    }
}
