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

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

import com.hypixel.hytale.math.util.HashUtil;
import javax.annotation.Nullable;
import java.util.function.Function;
import com.hypixel.hytale.math.util.ChunkUtil;
import java.util.concurrent.TimeUnit;
import com.hypixel.hytale.server.worldgen.util.ObjectPool;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.worldgen.util.cache.SizedTimeoutCache;

public abstract class CoordinateCache<T>
{
    @Nonnull
    private final SizedTimeoutCache<CoordinateKey, T> cache;
    @Nonnull
    private final ObjectPool<CoordinateKey> vectorPool;
    
    public CoordinateCache(final int maxSize, final long expireAfterSeconds) {
        this.vectorPool = new ObjectPool<CoordinateKey>(maxSize, CoordinateKey::new);
        this.cache = new SizedTimeoutCache<CoordinateKey, T>(expireAfterSeconds, TimeUnit.SECONDS, maxSize, key -> {
            final int x = ChunkUtil.xOfChunkIndex(key.coord);
            final int z = ChunkUtil.zOfChunkIndex(key.coord);
            return this.compute(key.seed, x, z);
        }, (key, value) -> {
            this.vectorPool.recycle(key);
            this.onRemoval(value);
        });
    }
    
    @Nullable
    public T get(final int seed, final int x, final int y) {
        return this.cache.getWithReusedKey(this.localKey().setLocation(seed, x, y), this.vectorPool);
    }
    
    protected abstract CoordinateKey localKey();
    
    protected abstract T compute(final int p0, final int p1, final int p2);
    
    protected abstract void onRemoval(final T p0);
    
    public static class CoordinateKey implements Function<CoordinateKey, CoordinateKey>
    {
        private int seed;
        private long coord;
        private int hash;
        
        public CoordinateKey() {
            this(0, 0, 0);
        }
        
        public CoordinateKey(final int seed, final int x, final int y) {
            this.setLocation(seed, x, y);
        }
        
        public int seed() {
            return this.seed;
        }
        
        public long coord() {
            return this.coord;
        }
        
        @Nonnull
        public CoordinateKey setLocation(final int seed, final int x, final int y) {
            this.seed = seed;
            this.coord = ChunkUtil.indexChunk(x, y);
            this.hash = (int)HashUtil.hash(seed, this.coord);
            return this;
        }
        
        @Nonnull
        @Override
        public CoordinateKey apply(@Nonnull final CoordinateKey cachedKey) {
            this.seed = cachedKey.seed;
            this.coord = cachedKey.coord;
            this.hash = cachedKey.hash;
            return this;
        }
        
        @Override
        public int hashCode() {
            return this.hash;
        }
        
        @Override
        public boolean equals(final Object o) {
            final CoordinateKey coordinateKey = (CoordinateKey)o;
            return this.seed == coordinateKey.seed && this.coord == coordinateKey.coord;
        }
    }
}
