/*
 *  ZThreads, a platform-independant, multithreading and 
 *  synchroniation library
 *
 *  Copyright (C) 2000-2002, Eric Crahen, See LGPL.TXT for details
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 *  SUNY @ Buffalo, hereby disclaims all copyright interest in the
 *  ZThreads library written by Eric Crahen
 */

#ifndef __ZTLOCKEDQUEUE_H__
#define __ZTLOCKEDQUEUE_H__

#include "zthread/ZThread.h"
#include "zthread/Guard.h"
#include "zthread/Mutex.h"
#include "zthread/Queue.h"

#include <deque>

namespace ZThread {

/**
 * @class LockedQueue
 *
 * @author Eric Crahen <crahen@cse.buffalo.edu>
 * @date <2002-02-24T09:26:22-0500>
 * @version 1.3.4
 *
 * A LockedQueue is the simplest thread safe queue. All its methods are
 * guarded with a mutex.
 */

template <class T, class LOCK=Mutex>
class LockedQueue : public Queue<T> {
private:

  LockedQueue(LockedQueue*);
  LockedQueue(const LockedQueue&);
  LockedQueue& operator=(const LockedQueue&);

protected:
  //! Serialize access
  LOCK _lock;
  //! Backing deque
  std::deque<T> _data;
  //! Cancellation flag
  bool _canceled;

public:

  /**
   * Create a new LockedQueue
   */
  LockedQueue() 
    : _canceled(false) {}

  /**
   * Destroy the Queue
   */
  virtual ~LockedQueue() throw() {}

  /**
   * Add a new object to this Queue
   *
   * @param T object to add
   * 
   * @exception InterruptedException
   * @exception CancelationException - the queue has 
   * been canceled.
   */
  virtual void add(T item) 
    throw(Synchronization_Exception) {

    Guard<LOCK> g(_lock);
    
    if(_canceled)
      throw Cancelation_Exception();

    _data.push_back(item);

  }

  /**
   * Get an object from this Queue
   *
   * @return T next available object
   * 
   * @exception InterruptedException 
   * @exception CancelationException - the queue is empty and has 
   * been canceled.
   * @exception NoSuchElement_Exception - thrown if the queue is
   * empty
   */
  virtual T next()
    throw(Synchronization_Exception) {

    Guard<LOCK> g(_lock);

    if(_data.size() == 0 && _canceled)
      throw Cancelation_Exception();
    
    if(_data.size() == 0)
      throw NoSuchElement_Exception();

    T item = _data.front();
    _data.pop_front();
    
    return item;

  }


  /**
   * Get an object from this Queue within a given amount of time
   * 
   * @param unsigned long time limit 
   *
   * @return T next available object
   * 
   * @exception InterruptedException
   * @exception CancelationException - the queue is empty and has 
   * been canceled.
   * @exception Timeout_Exception - thrown if the given amount
   * of time has expired and the queue could not return an item
   * @exception NoSuchElement_Exception - thrown if the queue is
   * empty
   */
  virtual T next(unsigned long timeout)
    throw(Synchronization_Exception) {

    Guard<LOCK> g(_lock, timeout);

    if(_data.size() == 0 && _canceled)
      throw Cancelation_Exception();
    
    if(_data.size() == 0)
      throw NoSuchElement_Exception();

    T item = _data.front();
    _data.pop_front();
      
    return item;
      
  }


  /**
   * Cancel this queue. No more objects can be added to it
   *
   * @exception InterruptedException 
   *
   * @post - The next() method will continue to return objects until 
   * the queue has been emptied. After the queue is emptied, further 
   * calls to next() will throw CancelationException
   *
   * The add() method will begin to throw CancelationException and no
   * item will be accepted by this queue
   */
  virtual void cancel()
    throw(Synchronization_Exception) {

    Guard<LOCK> g(_lock);

    _canceled = true;

  }

  /**
   * Check the cancelation status of this Queue
   * 
   * @return bool - Queue cancled
   */
  inline virtual bool isCanceled()
    throw(Synchronization_Exception) {
    
    // Faster check since the queue will not become un-canceled
    if(_canceled)
      return true;
      
    Guard<LOCK> g(_lock);

    return _canceled;

  }

  /**
   * Test this queue to see if it is empty. This method may or may not
   * block the caller, it is completely up to the implementation to decide
   * weather or not it is appropriate to block the caller.
   *
   * @return boolean - true if empty  
   * @exception InterruptedException 
   */
  virtual bool empty()
    throw(Synchronization_Exception) {

    Guard<LOCK> g(_lock);
    return (_data.size() == 0);

  }

};

};

#endif




