/*
 * KickQueue.java
 *
 * Created on December 20, 2000, 11:50 AM
 */

package pharynx;
import java.util.*;

/**
 *
 * @author  leon@eatworms.swmed.edu
 * @version 0.1
 */
public class KickQueue extends java.lang.Object {
    /*
     * So that it can hold multiple Kicks with identical times, and possibly
     * even indentical in all other respects, the queue is implemented as a
     * SortedMap whose keys are the times, and whose values are Maps of Kicks.
     * The keys for the Maps are unique tickets assigned to the Kicks when
     * they're queued.
     */
    private SortedMap queue;
    private long lastTicket = 0;
    private SortedMap ticketMap;

    public class Ticket implements Comparable {
        private long n = 0;
        Ticket(long n) {
            this.n = n;
        }

        public int compareTo(Object o2) {
            Ticket t2 = (Ticket) o2;
            if (n < t2.n) return -1;
            else if (n > t2.n) return 1;
            else return 0;
        }
        
    }

    /** Creates new KickQueue */
    public KickQueue() {
        queue = new TreeMap();
        ticketMap = new TreeMap();
    }

    /** Creates new KickQueue, loading an initial Kick */
    public KickQueue(Kick k) {
        this();
        add(k);
    }

    /** Creates new KickQueue, loading a list of initial Kicks */
    public KickQueue(List klist) {
        this();
        for(Iterator i = klist.listIterator(); i.hasNext();) {
            add((Kick) i.next());
        }
    }

    /** Find out whether there is anything in the queue */
    public boolean isEmpty() {
        return ticketMap.isEmpty();
    }

    /** add a Kick to the queue
     *
     * @param k     the Kick to add
     * @return      the return value is a unique Ticket that can be used to
     *              access this queue element again.
     */
    public Ticket add(Kick k) {
        Double t = new Double(k.time());
        Ticket tk = new Ticket(++lastTicket);
        ticketMap.put(tk, t);
        Map klist = (Map) queue.get(t);
        if (klist == null) {
            klist = new TreeMap();
            queue.put(t, klist);
        }
        klist.put(tk, k);
        return(tk);
    }

    /** retrieve and remove the first (earlest time) Kick */
    public Kick first() {
        Map kl = findFirst();
        if (null == kl) return null;
        Iterator i = kl.entrySet().iterator();
        Map.Entry ke = (Map.Entry) i.next();
        i.remove();
        Ticket tk = (Ticket) ke.getKey();
        Kick k = (Kick) ke.getValue();
        ticketMap.remove(tk);
        return k;
    }

    /** retrieve and remove the first (earliest time) Kick */
    public Kick remove() {
        return first();
    }

    /** retrieve and remove the Kick with Ticket tk */
    public Kick remove(Ticket tk) {
        Double t = (Double) ticketMap.get(tk);
        if (null == t) return null;
        Map kl = (Map) queue.get(t);
        Kick k = (Kick) kl.get(tk);
        kl.remove(tk);
        ticketMap.remove(tk);
        return k;
    }

    public Kick peek() {
        Map kl = findFirst();
        if (null == kl) return null;
        return (Kick) ((Map.Entry) kl.entrySet().iterator().next()).getValue();
    }

    public Kick peek(Ticket tk) {
        Map kl = (Map) ticketMap.get(tk);
        if (null == kl) return null;
        return (Kick) kl.get(tk);
    }

    private Map findFirst() {
        Double t;
        Map kl = null;
        for(; !queue.isEmpty();) {
            t = (Double) queue.firstKey();
            kl = (Map) queue.get(t);
            if ((kl != null) && !kl.isEmpty()) break;
            queue.remove(t);
        }
        return kl;
    }

    private Ticket findFirstTicket() {
        Map kl = findFirst();
        if (null == kl) return null;
        return (Ticket) ((Map.Entry) kl.entrySet().iterator().next()).getKey();
    }
    
    private void sizeCheck() {
        int s1 = ticketMap.size();
        int s2 = 0;
        for(Iterator i = queue.values().iterator(); i.hasNext();) {
            Object n = i.next();
            Map kl = (Map) n;
            int st = kl.size();
            s2 += st;
        }
        if (s1 != s2) {
            throw new CantHappenException("inconsistent maps in KickQueue");
        }
    }
}