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

package com.hypixel.hytale.builtin.hytalegenerator.positionproviders.cached;

import com.hypixel.hytale.math.vector.Vector3i;
import com.hypixel.hytale.builtin.hytalegenerator.VectorUtil;
import java.util.function.Consumer;
import java.util.Objects;
import java.util.ArrayList;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.math.util.HashUtil;
import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer;
import javax.annotation.Nonnull;
import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider;

public class CachedPositionProvider extends PositionProvider
{
    @Nonnull
    private final PositionProvider positionProvider;
    private final int sectionSize;
    private WorkerIndexer.Data<CacheThreadMemory> threadData;
    
    public CachedPositionProvider(@Nonnull final PositionProvider positionProvider, final int sectionSize, final int cacheSize, final boolean useInternalThreadData, final int threadCount) {
        if (sectionSize <= 0 || cacheSize < 0 || threadCount <= 0) {
            throw new IllegalArgumentException();
        }
        this.positionProvider = positionProvider;
        this.sectionSize = sectionSize;
        this.threadData = new WorkerIndexer.Data<CacheThreadMemory>(threadCount, () -> new CacheThreadMemory(cacheSize));
    }
    
    @Override
    public void positionsIn(@Nonnull final Context context) {
        this.get(context);
    }
    
    public void get(@Nonnull final Context context) {
        final CacheThreadMemory cachedData = this.threadData.get(context.workerId);
        final Vector3i minSection = this.sectionAddress(context.minInclusive);
        final Vector3i maxSection = this.sectionAddress(context.maxExclusive);
        final Vector3i sectionAddress = minSection.clone();
        sectionAddress.x = minSection.x;
        while (sectionAddress.x <= maxSection.x) {
            sectionAddress.z = minSection.z;
            while (sectionAddress.z <= maxSection.z) {
                sectionAddress.y = minSection.y;
                while (sectionAddress.y <= maxSection.y) {
                    final long key = HashUtil.hash(sectionAddress.x, sectionAddress.y, sectionAddress.z);
                    Vector3d[] section = cachedData.sections.get(key);
                    if (section == null) {
                        final Vector3d sectionMin = this.sectionMin(sectionAddress);
                        final Vector3d sectionMax = sectionMin.clone().add(this.sectionSize, this.sectionSize, this.sectionSize);
                        final ArrayList<Vector3d> generatedPositions = new ArrayList<Vector3d>();
                        final Vector3d minInclusive = sectionMin;
                        final Vector3d maxExclusive = sectionMax;
                        final ArrayList<Vector3d> obj = generatedPositions;
                        Objects.requireNonNull(obj);
                        final Context childContext = new Context(minInclusive, maxExclusive, (Consumer<Vector3d>)obj::add, null, context.workerId);
                        this.positionProvider.positionsIn(childContext);
                        section = new Vector3d[generatedPositions.size()];
                        generatedPositions.toArray(section);
                        cachedData.sections.put(key, section);
                        cachedData.expirationList.addFirst(key);
                        if (cachedData.expirationList.size() > cachedData.size) {
                            final long removedKey = cachedData.expirationList.removeLast();
                            cachedData.sections.remove(removedKey);
                        }
                    }
                    for (final Vector3d position : section) {
                        if (VectorUtil.isInside(position, context.minInclusive, context.maxExclusive)) {
                            context.consumer.accept(position.clone());
                        }
                    }
                    final Vector3i vector3i = sectionAddress;
                    ++vector3i.y;
                }
                final Vector3i vector3i2 = sectionAddress;
                ++vector3i2.z;
            }
            final Vector3i vector3i3 = sectionAddress;
            ++vector3i3.x;
        }
    }
    
    @Nonnull
    private Vector3i sectionAddress(@Nonnull final Vector3d pointer) {
        final Vector3i address = pointer.toVector3i();
        address.x = this.sectionFloor(address.x) / this.sectionSize;
        address.y = this.sectionFloor(address.y) / this.sectionSize;
        address.z = this.sectionFloor(address.z) / this.sectionSize;
        return address;
    }
    
    @Nonnull
    private Vector3d sectionMin(@Nonnull final Vector3i sectionAddress) {
        final Vector3d vector3d;
        final Vector3d min = vector3d = sectionAddress.toVector3d();
        vector3d.x *= this.sectionSize;
        final Vector3d vector3d2 = min;
        vector3d2.y *= this.sectionSize;
        final Vector3d vector3d3 = min;
        vector3d3.z *= this.sectionSize;
        return min;
    }
    
    private int toSectionAddress(final double position) {
        int positionAddress = (int)position;
        positionAddress = this.sectionFloor(positionAddress);
        positionAddress /= this.sectionSize;
        return positionAddress;
    }
    
    public int sectionFloor(final int voxelAddress) {
        if (voxelAddress < 0) {
            return voxelAddress - voxelAddress % this.sectionSize - this.sectionSize;
        }
        return voxelAddress - voxelAddress % this.sectionSize;
    }
}
