package de.renew.engine.searchqueue;

import java.util.Enumeration;
import java.util.Hashtable;

import de.renew.engine.searcher.Searchable;
import de.renew.engine.thread.SimulationThreadPool;


/**
 * A deterministic search queue simply provides searchable
 * objects in the same order in which they arrived at the queue.
 * This is useful for debugging purposes.
 * <p>
 * The caller is responsible for excluding concurrent access
 * to the queue.
 *
 * @author Olaf Kummer
 */
class DeterministicSearchQueue implements SearchQueueData {
    private final double _time;
    private DSQListNode _list;
    private final Hashtable<Searchable, DSQListNode> _lookup;

    DeterministicSearchQueue(double time) {
        _time = time;
        _list = null;
        _lookup = new Hashtable<>();
    }

    @Override
    public double getTime() {
        assert SimulationThreadPool.isSimulationThread() : "is not in a simulation thread";
        return _time;
    }

    @Override
    public void include(Searchable searchable) {
        assert SimulationThreadPool.isSimulationThread() : "is not in a simulation thread";
        DSQListNode node = _lookup.get(searchable);
        if (node != null) {
            // node is already in lookup, doesn't need to be inserted
            return;
        }

        // insert node into lookup
        node = new DSQListNode(searchable);
        _lookup.put(searchable, node);
        if (_list == null) {
            _list = node;
        } else {
            node.setPrevious(_list.getPrevious());
            node.setNext(_list);
            _list.getPrevious().setNext(node);
            _list.setPrevious(node);
        }
    }

    private void discard(DSQListNode node) {
        _lookup.remove(node.getElement());
        if (node == node.getNext()) {
            // Remove the last entry.
            _list = null;
        } else {
            // Remove the list from the cycle.
            node.getNext().setPrevious(node.getPrevious());
            node.getPrevious().setNext(node.getNext());

            // Possibly change the initial node.
            if (_list == node) {
                _list = node.getNext();
            }
        }
    }

    @Override
    public void exclude(Searchable searchable) {
        assert SimulationThreadPool.isSimulationThread() : "is not in a simulation thread";
        DSQListNode node = _lookup.get(searchable);
        if (node != null) {
            discard(node);
        }
    }

    @Override
    public Searchable extract() {
        assert SimulationThreadPool.isSimulationThread() : "is not in a simulation thread";
        // Get a single searchable from the list of searchables.
        // It will be checked soon, so it will either turn out
        // to be not enabled, or we must move it to the end
        // of the list to get fairness. Remove it from the list.
        if (_list == null) {
            return null;
        }

        Searchable result = _list.getElement();
        discard(_list);
        return result;
    }

    @Override
    public Enumeration<Searchable> elements() {
        assert SimulationThreadPool.isSimulationThread() : "is not in a simulation thread";
        return new DSQEnumeration(_list);
    }

    @Override
    public int size() {
        return _lookup.size();
    }
}