/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2000-2009 Sun Microsystems, Inc. All rights reserved. 
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License ("CDDL") (collectively, the "License").  You may
 * not use this file except in compliance with the License.  You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or mq/legal/LICENSE.txt.  See the License for the specific language
 * governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at mq/legal/LICENSE.txt.  Sun designates
 * this particular file as subject to the "Classpath" exception as provided by
 * Sun in the GPL Version 2 section of the License file that accompanied this
 * code.  If applicable, add the following below the License Header, with the
 * fields enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or  to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright holder. 
 */

/*
 * @(#)PriorityQueue.java	1.8 06/27/07
 */ 

package com.sun.messaging.jmq.jmsclient;

import java.util.LinkedList;
import com.sun.messaging.jmq.io.*;

/**
 *
 * <p>Queue structure that handles message priorities. Messages received in a
 * Session are prioritized based on the message priority (0-9).
 * </P>
 */

class PriorityQueue implements MessageQueue {

    /**
     * array size 10 for message priority 0 - 9.
     */
    protected static final int ARRAY_SIZE = 10;

    /**
     * Linked list to hold LinkedList entries.  Each LinkedList entry is to
     * hold pkt/message of a certain priority.
     */
    private LinkedList[] qlist = null;

    /**
     * priority queue total size.
     */
    protected int qsize = 0;

    /**
     * Constructor.  Call init() to init. its data structure.
     */
    public PriorityQueue() {
        init();
    }

    /**
     * Initialize qlist to an array of LinkedList with size of
     * ARRAY_SIZE (10).
     *
     * Init all elements in the qlist to null.
     */
    protected void init() {

        //construct an array of arraylist
        qlist = new LinkedList [ARRAY_SIZE];

        //init each entry to null.
        for ( int i=0; i<ARRAY_SIZE; i++) {
            qlist[i] = null;
        }
    }

    /**
     * Get the queue size.
     */
     public synchronized int size() {
        return qsize;
     }

     /**
      * Check if the queue size is empty.
      * @return true if queue size is 0.  Otherwise, return false.
      */
     public synchronized boolean isEmpty() {
        return (qsize == 0);
     }

    /**
     * Clears all elements from the queue.
     **/
    public synchronized void
    clear () {
        for ( int i=0; i<ARRAY_SIZE; i++) {
            qlist[i] = null;
        }

        qsize = 0;
    }

    /**
     * Enqueues an object in the queue based on the priority of the obj.
     * The priority of nobj is obtained from ReadOnlyPacket or MessageImpl.
     * @param nobj new object to be enqueued.
     *
     * @see getPriority
     */
    public synchronized void
    enqueue(Object nobj) {

        int priority = getPriority(nobj);

        if ( qlist [priority] == null ) {
            qlist [priority] = new LinkedList ();
        }

        qlist [priority].add(nobj);

        qsize ++;

    }

    /**
     * get JMS message priority.
     * @param nobj get JMS message priority from this obj.
     * @return message priority.
     */
    protected int getPriority (Object nobj) {

        int priority = 0;

        try {
            if (nobj instanceof ReadOnlyPacket) {
                priority = ( (ReadOnlyPacket) nobj).getPriority();
            }
            else if (nobj instanceof MessageImpl) {
                priority = ( (MessageImpl) nobj).getJMSPriority();
            }
        } catch (Exception jmse) {
            /**
             * This should not happen.  Dump stack trace to output
             * stream if this SHOULD happen.
             */
            Debug.printStackTrace(jmse);
        }

        return priority;
    }

    /**
     * Dequeues an element from the queue.
     * @return dequeued object, or null if empty queue
    */
    public synchronized Object
    dequeue() {
        //var to hold element to be returned.
        Object obj = null;

        //index set to priority 9.
        int index = ARRAY_SIZE - 1;
        //loop from p9 to p0.
        while ( index >= 0 ) {
            //check if there are elements in a specific priority queue.
            if ( (qlist[index] != null) && (qlist[index].isEmpty() == false) ) {
                //get and remove the first element in the list that contains
                //element.
                obj = qlist[index].removeFirst();
                //decrease q size by one
                qsize --;
                //break out while loop.
                index = -1;
            }
            //decrease the priority.
            index --;
        }

        return obj;
    }

    /**
     * Get all elements in the priority queue and return as an array
     * of objects.
     *
     * @return an array of objects in the queue.
     */
    public synchronized Object[] toArray() {

        LinkedList list = new LinkedList();

        //start at p9.
        int index = ARRAY_SIZE - 1;

        while ( index >= 0 ) {

            /**
             * start from priority 9,
             * if the linked list is not null and is not empty,
             * add elements in the list to the return list
             */
            if ( qlist[index] != null && (qlist[index].isEmpty() == false) ) {

                int size = qlist[index].size();

                for ( int i=0; i < size; i++) {
                    list.add ( qlist[index].get(i) );
                }

            }

            index --;
        }

        return list.toArray();
    }

    /**
     * remove obj from the queue.
     * @param obj
     * @return
     */
    public synchronized boolean remove (Object obj) {

        //if obj contains obj and removed, found is set to true.
        boolean found = false;

        //start at p9.
        int index = ARRAY_SIZE -1;

        /**
         * while searching (from p9) and not found.
         */
        while ( index >= 0 && (found == false)) {

            /**
             * start from priority 9,
             * if the array list is not null and is not empty,
             * find and remove the obj from the list.
             */
            if ( qlist[index] != null && (qlist[index].isEmpty() == false) ) {

                if ( qlist[index].contains(obj) ) {
                    found = qlist[index].remove(obj);
                    qsize --;
                }

            }

            index --;
        }

        return found;
    }

}
