package com.fastasyncworldedit.core.queue.implementation.chunk;

import com.fastasyncworldedit.core.queue.IChunkCache;
import com.fastasyncworldedit.core.queue.Trimable;
import com.fastasyncworldedit.core.util.MathMan;
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.objects.ObjectIterator;

import java.lang.ref.WeakReference;

public class ChunkCache<T extends Trimable> implements IChunkCache<T> {

    protected final Long2ObjectLinkedOpenHashMap<WeakReference<T>> getCache;
    private final IChunkCache<T> delegate;

    public ChunkCache(IChunkCache<T> delegate) {
        this.getCache = new Long2ObjectLinkedOpenHashMap<>();
        this.delegate = delegate;
    }

    /**
     * Get or create the IGetBlocks.
     *
     * @return cached IGetBlocks
     */
    @Override
    public synchronized T get(int x, int z) {
        long pair = MathMan.pairInt(x, z);
        final WeakReference<T> ref = getCache.get(pair);
        if (ref != null) {
            final T blocks = ref.get();
            if (blocks != null) {
                return blocks;
            }
        }
        final T blocks = newChunk(x, z);
        getCache.put(pair, new WeakReference<>(blocks));
        return blocks;
    }

    public T newChunk(int chunkX, int chunkZ) {
        return delegate.get(chunkX, chunkZ);
    }

    @Override
    public synchronized boolean trim(boolean aggressive) {
        if (getCache.isEmpty()) {
            return true;
        }
        boolean result = true;
        final ObjectIterator<Long2ObjectMap.Entry<WeakReference<T>>> iter = getCache
                .long2ObjectEntrySet().fastIterator();
        while (iter.hasNext()) {
            final Long2ObjectMap.Entry<WeakReference<T>> entry = iter.next();
            final WeakReference<T> value = entry.getValue();
            final T igb = value.get();
            if (igb == null) {
                iter.remove();
            } else {
                result = false;
                if (!aggressive) {
                    return false;
                }
                synchronized (igb) {
                    igb.trim(true);
                }
            }
        }
        return result;
    }

}
