package net.floodlightcontroller.flowcache;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;


/**
 * PriorityPendingQueue class - This class is a variant implementation for PriorityBlockingQueue
 * PriorityBlockingQueue implementation has two problems:
 * 1. service for events with the same priority has no guarantee of FIFO sequence. This can be solved by override of comparator though.
 * 2. PriorityBlockingQueue is implemented through heap, which has a O(log(n)) complexity for enqueue and dequeue operations.
 * to get a O(1) complexity with enqueue and dequeue operations, we propose this PriorityPendingList class.
 * <p>
 * PriorityPendingQueue has three separate queues: High Priority, Medium Priority and Low Priority.
 * the requirements here are:
 * 1. dequeue from the Queue will always return the event with the highest priority
 * 2. events with the same priority will be dequeued in their inserting order
 * 3. enqueue and dequeue have O(1) complexity
 *
 * current only support offer() and take() methods
 *
 * @author meiyang
 *
 */
public class PriorityPendingQueue<E> {
    private ArrayBlockingQueue<E> highPriorityQueue;
    private ArrayBlockingQueue<E> mediumPriorityQueue;
    private ArrayBlockingQueue<E> lowPriorityQueue;
    private final AtomicInteger count = new AtomicInteger(0);
    private final ReentrantLock takeLock = new ReentrantLock();
    private final Condition notEmpty = takeLock.newCondition();
    private final ReentrantLock putLock = new ReentrantLock();
    private final Condition notFull = putLock.newCondition();
    private final int capacity;
    public enum EventPriority {
        HIGH,
        MEDIUM,
        LOW,
    }
    public PriorityPendingQueue() {
        highPriorityQueue=   new ArrayBlockingQueue<E>(1000);
        mediumPriorityQueue= new ArrayBlockingQueue<E>(1000);
        lowPriorityQueue=    new ArrayBlockingQueue<E>(5000);
        capacity= Integer.MAX_VALUE;
    }

    public E take() throws InterruptedException {
        E x;
        int c = -1;
        final AtomicInteger count = this.count;
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lockInterruptibly();
        try {
            try {
                while (count.get() == 0)
                    notEmpty.await();
            } catch (InterruptedException ie) {
                notEmpty.signal(); // propagate to a non-interrupted thread
                throw ie;
            }
            x = extract();
            c = count.getAndDecrement();
            if (c > 1)
               notEmpty.signal();
            } finally {
            takeLock.unlock();
        }
        if (c == capacity)
            signalNotFull();
        return x;
    }

    public E poll() {
        final AtomicInteger count = this.count;
        if (count.get() == 0)
            return null;
        E x = null;
        int c = -1;
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lock();
        try {
            if (count.get() > 0) {
                x = extract();
                c = count.getAndDecrement();
                if (c > 1)
                    notEmpty.signal();
            }
        } finally {
            takeLock.unlock();
        }
        if (c == capacity)
            signalNotFull();
        return x;
    }

    public E peek() {
        //todo
        return null;
        }

    public boolean offer(E e, EventPriority p) {
        if (e == null) throw new NullPointerException();
        final AtomicInteger count = this.count;
        if (count.get() == capacity)
            return false;
        int c = -1;
        final ReentrantLock putLock = this.putLock;
        putLock.lock();
        try {
            if (count.get() < capacity) {
                insert(e,p);
                c = count.getAndIncrement();
                if (c + 1 < capacity)
                    notFull.signal();
            }
        } finally {
            putLock.unlock();
        }
        if (c == 0)
            signalNotEmpty();
        return c >= 0;
    }

    public boolean offer(E e) {
        return false;
    }

    private E extract() {
        E first = highPriorityQueue.poll();
        if (first==null)
            first = mediumPriorityQueue.poll();
        if (first==null)
            first = lowPriorityQueue.poll();
        return first;
    }

    private boolean insert(E e, EventPriority p) {
        boolean result = false;
        switch (p) {
            case HIGH:
                result = highPriorityQueue.offer(e);
                break;
            case MEDIUM:
                result = mediumPriorityQueue.offer(e);
                break;
            case LOW:
                result = lowPriorityQueue.offer(e);
                break;
        }
        return result;
    }

    private void signalNotFull() {
         final ReentrantLock putLock = this.putLock;
         putLock.lock();
         try {
             notFull.signal();
         } finally {
             putLock.unlock();
         }
     }

    private void signalNotEmpty() {
         final ReentrantLock takeLock = this.takeLock;
         takeLock.lock();
         try {
             notEmpty.signal();
         } finally {
             takeLock.unlock();
         }
     }
    private void fullyLock() {
        putLock.lock();
        takeLock.lock();
    }
    private void fullyUnlock() {
        takeLock.unlock();
        putLock.unlock();
    }
    public int size() {
        return count.get();
    }
    public void clear() {
        fullyLock();
        try {
            highPriorityQueue.clear();
            mediumPriorityQueue.clear();
            lowPriorityQueue.clear();
            count.set(0);
        } finally {
            fullyUnlock();
        }
    }
    public boolean isEmpty() {
        return count.get() == 0;
    }
}
@@ -66,21 +66,22 @@
 current.
-Array
+Linked
 Blocking
@@ -232,17 +232,16 @@
 tLock;%0A%0A
-%0A
 /**%0A * P
@@ -1233,37 +1233,38 @@
 %3E %7B%0A    private 
-Array
+Linked
 BlockingQueue%3CE%3E
@@ -1287,37 +1287,38 @@
 ue;%0A    private 
-Array
+Linked
 BlockingQueue%3CE%3E
@@ -1351,21 +1351,22 @@
 private 
-Array
+Linked
 Blocking
@@ -1885,29 +1885,30 @@
 ueue=   new 
-Array
+Linked
 BlockingQueu
@@ -1904,36 +1904,32 @@
 lockingQueue%3CE%3E(
-1000
 );%0A        mediu
@@ -1944,29 +1944,30 @@
 yQueue= new 
-Array
+Linked
 BlockingQueu
@@ -1975,12 +1975,8 @@
 %3CE%3E(
-1000
 );%0A 
@@ -2011,13 +2011,14 @@
 new 
-Array
+Linked
 Bloc
@@ -2034,12 +2034,8 @@
 %3CE%3E(
-5000
 );%0A 

add debug info