package org.jacorb.notification.queue;

/*
 *        JacORB - a free Java ORB
 *
 *   Copyright (C) 1999-2014 Gerald Brose / The JacORB Team.
 *
 *   This library is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU Library General Public
 *   License as published by the Free Software Foundation; either
 *   version 2 of the License, or (at your option) any later version.
 *
 *   This library is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *   Library General Public License for more details.
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this library; if not, write to the Free
 *   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.jacorb.notification.interfaces.Message;

/**
 * @author Alphonse Bendt
 */

public abstract class AbstractBoundedEventQueue implements MessageQueue
{
    private final Object lock_;
    private final int capacity_;
    private final List listeners_ = new ArrayList();
    private final EventQueueOverflowStrategy overflowStrategy_;

    protected AbstractBoundedEventQueue(int capacity, EventQueueOverflowStrategy overflowStrategy,
            Object lock)
    {
        lock_ = lock;
        capacity_ = capacity;
        overflowStrategy_ = overflowStrategy;
    }

    public final String getDiscardPolicyName()
    {
        return overflowStrategy_.getDiscardPolicyName();
    }
    
    protected abstract Message getEarliestTimeout();

    protected abstract Message getLeastPriority();

    protected abstract Message getNextElement();

    protected abstract Message getOldestElement();

    protected abstract Message getYoungestElement();

    protected abstract Message[] getElements(int max);

    protected abstract void addElement(Message message);

    protected abstract Message[] getAllElements();

    public abstract String getOrderPolicyName();
    
    public Message[] getAllMessages(boolean wait) throws InterruptedException
    {
        synchronized (lock_)
        {
            if (wait)
            {
                return getAllBlocking();
            }

            return getAllElements();
        }
    }

    /**
     * @pre current thread own monitor lock_
     */
    private Message[] getAllBlocking() throws InterruptedException
    {
        while (isEmpty())
        {
            lock_.wait();
        }

        return getAllElements();
    }

    public Message getMessage(boolean wait) throws InterruptedException
    {
        synchronized (lock_)
        {
            if (wait)
            {
                return getEventBlocking();
            }

            if (isEmpty())
            {
                return null;
            }

            return getNextElement();
        }
    }

    public Message[] getMessages(int max, boolean wait) throws InterruptedException
    {
        synchronized (lock_)
        {
            if (wait)
            {
                return getEventsBlocking(max);
            }

            return getElements(max);
        }
    }

    /**
     * @pre current thread owns monitor lock_
     */
    private Message[] getEventsBlocking(int max) throws InterruptedException
    {
        while (isEmpty())
        {
            lock_.wait();
        }

        return getElements(max);
    }

    /**
     * @pre current thread owns monitor lock_
     */
    private Message getEventBlocking() throws InterruptedException
    {
        while (isEmpty())
        {
            lock_.wait();
        }

        return getNextElement();
    }

    public void put(Message event)
    {
        synchronized (lock_)
        {
            while (getSize() >= capacity_)
            {
                overflowStrategy_.removeElementFromQueue(this);
                
                fireMessageDiscarded();
            }

            addElement(event);

            lock_.notifyAll();
        }
    }
    
    private void fireMessageDiscarded()
    {
        final Iterator i = listeners_.iterator();
        
        while(i.hasNext())
        {
            ((DiscardListener)i.next()).messageDiscarded(capacity_);
        }
    }
    
    public void addDiscardListener(DiscardListener listener)
    {
        listeners_.add(listener);
    }
    
    public void removeDiscardListener(DiscardListener listener)
    {
        listeners_.remove(listener);
    }
}