// -*- C++ -*-
//  Copyright (C) 2009-2010, Vaclav Haisman. All rights reserved.
//  
//  Redistribution and use in source and binary forms, with or without modifica-
//  tion, are permitted provided that the following conditions are met:
//  
//  1. Redistributions of  source code must  retain the above copyright  notice,
//     this list of conditions and the following disclaimer.
//  
//  2. Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//  
//  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
//  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
//  FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
//  APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
//  INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
//  DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
//  OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
//  ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
//  (INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
//  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#ifndef DCMTK_LOG4CPLUS_HELPERS_QUEUE_H
#define DCMTK_LOG4CPLUS_HELPERS_QUEUE_H

#include "dcmtk/oflog/config.h"

#if defined (DCMTK_LOG4CPLUS_HAVE_PRAGMA_ONCE)
#pragma once
#endif

#if ! defined (DCMTK_LOG4CPLUS_SINGLE_THREADED)

#include "dcmtk/oflog/spi/logevent.h"
#include "dcmtk/oflog/thread/threads.h"
#include "dcmtk/oflog/thread/syncprim.h"
#include "dcmtk/ofstd/ofvector.h"


namespace dcmtk {
namespace log4cplus { namespace thread {


//! Single consumer, multiple producers queue.
class DCMTK_LOG4CPLUS_EXPORT Queue
    : public virtual helpers::SharedObject
{
public:
    //! Type of the state flags field.
    typedef unsigned flags_type;

    //! Queue storage type.
    typedef OFVector<spi::InternalLoggingEvent> queue_storage_type;

    Queue (unsigned len = 100);
    virtual ~Queue ();

    // Producers' methods.

    //! Puts event <code>ev</code> into queue, sets QUEUE flag and
    //! sets internal event object into signaled state. If the EXIT
    //! flags is already set upon entering the function, nothing is
    //! inserted into the queue. The function can block on internal
    //! semaphore if the queue has reached maximal allowed
    //! length. Calling thread is unblocked either by consumer thread
    //! removing item from queue or by any other thread calling
    //! signal_exit().
    //!
    //! \param ev spi::InternalLoggingEvent to be put into the queue.
    //! \return Flags.
    flags_type put_event (spi::InternalLoggingEvent const & ev);

    //! Sets EXIT flag and DRAIN flag and sets internal event object
    //! into signaled state.
    //! \param drain If true, DRAIN flag will be set, otherwise unset.
    //! \return Flags, ERROR_BIT can be set upon error.
    flags_type signal_exit (bool drain = true);

    // Consumer's methods.

    //! The get_events() function is used by queue's consumer. It
    //! fills <code>buf</code> argument and sets EVENT flag in return
    //! value. If EXIT flag is already set in flags member upon
    //! entering the function then depending on DRAIN flag it either
    //! fills <code>buf</code> argument or does not fill the argument,
    //! if the queue is non-empty. The function blocks by waiting for
    //! internal event object to be signaled if the queue is empty,
    //! unless EXIT flag is set. The calling thread is unblocked when
    //! items are added into the queue or when exit is signaled using
    //! the signal_exit() function.
    //!
    //!
    //! Upon error, return value has one of the error flags set.
    //!
    //! \param buf Pointer to storage of spi::InternalLoggingEvent
    //! instances to be filled from queue.
    //! \return Flags.
    flags_type get_events (queue_storage_type * buf);

    //! Possible state flags.
    enum Flags
    {
        //! EVENT flag is set in return value of get_event() call if
        //! the <code>ev</code> argument is filled with event from the queue.
        EVENT       = 0x0001,

        //! QUEUE flag is set by producers when they put item into the
        //! queue.
        QUEUE       = 0x0002,

        //! EXIT flag is set by signal_exit() call, signaling that the
        //! queue worker thread should end itself.
        EXIT        = 0x0004,

        //! When DRAIN flag is set together with EXIT flag, the queue
        //! worker thread will first drain the queue before exiting.
        DRAIN       = 0x0008,

        //! ERROR_BIT signals error.
        ERROR_BIT   = 0x0010,

        //! ERROR_AFTER signals error that has occurred after queue has
        //! already been touched.
        ERROR_AFTER = 0x0020
    };

protected:
    //! Queue storage.
    queue_storage_type queue;

    //! Mutex protecting queue and flags.
    Mutex mutex;

    //! Event on which consumer can wait if it finds queue empty.
    ManualResetEvent ev_consumer;

    //! Semaphore that limits the queue length.
    Semaphore sem;

    //! State flags.
    flags_type flags;

private:
    Queue (Queue const &);
    Queue & operator = (Queue const &);
};


typedef helpers::SharedObjectPtr<Queue> QueuePtr;


} } // namespace log4cplus { namespace thread {
} // end namespace dcmtk


#endif // DCMTK_LOG4CPLUS_SINGLE_THREADED

#endif // DCMTK_LOG4CPLUS_HELPERS_QUEUE_H
