package ca.recrown.islandsurvivalcraft.utilities.caching;

import java.util.concurrent.locks.ReentrantLock;

class CacheUsageStack<K, V> {
    private volatile int size;
    private volatile CacheValue<K, V> first, last;
    private final ReentrantLock lock = new ReentrantLock();

    private void addValueToStackTop(CacheValue<K, V> value) {
        if (value == first) return;
        if (first != null) {
            first.front = value;
            value.back = first;
        } else {
            last = value;
        }

        value.front = null;
        first = value;
        size++;
    }

    private void removeValueFromStack(CacheValue<K, V> value) {
        if (value.front != null) {
            value.front.back = value.back;
        } else {
            first = value.back;
        }
        if (value.back != null) {
            value.back.front = value.front;
        } else {
            last = value.front;
        }
        value.front = null;
        value.back = null;
        size--;
    }

    public void moveToTop(CacheValue<K, V> value) {
        lock.lock();
        try {
            if (!value.detached) {
                removeValueFromStack(value);
                addValueToStackTop(value);
            }
        } finally {
            lock.unlock();
        }
    }

    public boolean tryMoveToTop(CacheValue<K, V> value) {
        boolean success = false;
        success = lock.tryLock();
        if (success) {
            try {
                if (!value.detached) {
                    removeValueFromStack(value);
                    addValueToStackTop(value);
                }
            } finally {
                lock.unlock();
            }
        }
        return success;
    }

    public void add(CacheValue<K, V> value) {
        lock.lock();
        try {
            addValueToStackTop(value);
            value.detached = false;
        } finally {
            lock.unlock();
        }
    }

    public CacheValue<K, V> pop() {
        CacheValue<K, V> cacheValue;
        lock.lock();
        try {
            cacheValue = last;
            removeValueFromStack(last);
            cacheValue.detached = true;
        } finally {
            lock.unlock();
        }
        return cacheValue;
    }

    public void clear() {
        lock.lock();
        try {
            first = null;
            last = null;
        } finally {
            lock.unlock();
        }
        size = 0;
    }

    public int size() {
        return size;
    }

    @Override
    public String toString() {
        StringBuilder stringBulder = new StringBuilder();
        CacheValue<K, V> current = first;
        while (current != null) {
            stringBulder.append(first + "\n");
            current = current.back;
        }
        return stringBulder.toString();
    }
}