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

package com.hypixel.hytale.builtin.hytalegenerator.density.nodes;

import javax.annotation.Nullable;
import com.hypixel.hytale.math.vector.Vector3d;
import javax.annotation.Nonnull;
import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer;
import com.hypixel.hytale.builtin.hytalegenerator.density.Density;

public class MultiCacheDensity extends Density
{
    @Nonnull
    private final WorkerIndexer.Data<Cache> threadData;
    @Nonnull
    private Density input;
    
    public MultiCacheDensity(@Nonnull final Density input, final int threadCount, final int capacity) {
        this.input = input;
        this.threadData = new WorkerIndexer.Data<Cache>(threadCount, () -> new Cache(capacity));
    }
    
    @Override
    public double process(@Nonnull final Context context) {
        final Cache cache = this.threadData.get(context.workerId);
        Entry matchingEntry = cache.find(context.position);
        if (matchingEntry == null) {
            matchingEntry = cache.getNext();
            if (matchingEntry.position == null) {
                matchingEntry.position = new Vector3d();
            }
            matchingEntry.position.assign(context.position);
            matchingEntry.value = this.input.process(context);
        }
        return matchingEntry.value;
    }
    
    @Override
    public void setInputs(@Nonnull final Density[] inputs) {
        assert inputs.length != 0;
        assert inputs[0] != null;
        this.input = inputs[0];
    }
    
    private static class Cache
    {
        Entry[] entries;
        int oldestIndex;
        
        Cache(final int size) {
            this.entries = new Entry[size];
            for (int i = 0; i < size; ++i) {
                this.entries[i] = new Entry();
            }
            this.oldestIndex = 0;
        }
        
        Entry getNext() {
            final Entry entry = this.entries[this.oldestIndex];
            ++this.oldestIndex;
            if (this.oldestIndex >= this.entries.length) {
                this.oldestIndex = 0;
            }
            return entry;
        }
        
        @Nullable
        Entry find(@Nonnull final Vector3d position) {
            int startIndex = this.oldestIndex - 1;
            if (startIndex < 0) {
                startIndex += this.entries.length;
            }
            int index = startIndex;
            while (!position.equals(this.entries[index].position)) {
                if (++index >= this.entries.length) {
                    index = 0;
                }
                if (index == startIndex) {
                    return null;
                }
            }
            return this.entries[index];
        }
    }
    
    private static class Entry
    {
        Vector3d position;
        double value;
        
        Entry() {
            this.position = null;
            this.value = 0.0;
        }
    }
}
