//----------------------------------------------------------------------------
//
// TSDuck - The MPEG Transport Stream Toolkit
// Copyright (c) 2005-2020, Thierry Lelegard
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS 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 COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, 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.
//
//----------------------------------------------------------------------------
//!
//!  @file
//!  Transport stream packet queue for inter-thread communication.
//!
//----------------------------------------------------------------------------

#pragma once
#include "tsTSPacket.h"
#include "tsPCRAnalyzer.h"
#include "tsMutex.h"
#include "tsCondition.h"

namespace ts {
    //!
    //! Transport stream packet queue for inter-thread communication.
    //! @ingroup mpeg
    //!
    //! A writer thread produces packets. The input packets are directly written
    //! into the buffer. The writer thread invokes lockWriteBuffer() to get
    //! a write window inside the buffer. When packets have been written into
    //! this buffer, the writer thread calls releaseWriteBuffer().
    //!
    //! A reader thread consumes packets. The packets are consumed one by one
    //! and copied out of the buffer using getPacket().
    //!
    //! The input bitrate, if known, is transmitted to the reader thread. If the
    //! writer thread is aware of the exact bitrate, it calls setBitrate() and
    //! the specified value is returned to the reader thread. If the input bitrate
    //! is unknown, the buffer automatically computes it based on PCR's.
    //!
    //! Termination conditions can be triggered on both sides.
    //!
    class TSDUCKDLL TSPacketQueue
    {
        TS_NOCOPY(TSPacketQueue);
    public:
        //!
        //! Default size in packets of the buffer.
        //!
        static const size_t DEFAULT_SIZE = 1000;

        //!
        //! Default constructor.
        //! @param [in] size Size of the buffer in packets.
        //!
        TSPacketQueue(size_t size = DEFAULT_SIZE);

        //!
        //! Reset and resize the buffer.
        //! It is illegal to reset the buffer while the writer thread has locked the buffer.
        //! This is not enforced by this class. It is the responsibility of the application to check this.
        //! @param [in] size New size of the buffer in packets. By default, when set to NPOS,
        //! reset the queue without resizing the buffer.
        //!
        void reset(size_t size = NPOS);

        //!
        //! Get the size of the buffer in packets.
        //! @return The size of the buffer in packets.
        //!
        size_t bufferSize() const;

        //!
        //! Called by the writer thread to get a write buffer.
        //! The writer thread is suspended until enough free space is made in the buffer
        //! or the reader thread triggers a stop condition.
        //! @param [out] buffer Address of the write buffer.
        //! @param [out] buffer_size Size in packets of the write buffer.
        //! @param [in] min_size Minimum number of free packets to get. This is just a
        //! hint. The returned size can be smaller, for instance when the write window
        //! of the circular buffer is close to the end of the buffer.
        //! @return True when the write buffer is correctly available.
        //! False when the reader thread has signalled a stop condition.
        //!
        bool lockWriteBuffer(TSPacket*& buffer, size_t& buffer_size, size_t min_size = 1);

        //!
        //! Called by the writer thread to release the write buffer.
        //! The packets were written by the writer thread at the address which
        //! was returned by lockWriteBuffer().
        //! @param [in] count Number of packets which were written in the buffer.
        //! Must be no greater than the size which was returned by lockWriteBuffer().
        //!
        void releaseWriteBuffer(size_t count);

        //!
        //! Called by the writer thread to report the input bitrate.
        //! @param [in] bitrate Input bitrate. If zero, the input bitrate is unknown
        //! and will be computed from PCR's.
        //!
        void setBitrate(BitRate bitrate);

        //!
        //! Called by the writer thread to report the end of input thread.
        //!
        void setEOF();

        //!
        //! Check if the reader thread has reported a stop condition.
        //! @return True if the reader thread has reported a stop condition.
        //!
        bool stopped() const { return _stopped; }

        //!
        //! Called by the reader thread to get the next packet without waiting.
        //! The reader thread is never suspended. If no packet is available, return false.
        //! @param [out] packet The returned packet. Unmodified when no packet is available.
        //! @param [out] bitrate Input bitrate or zero if unknown.
        //! @return True if a packet was returned in @a packet. False if none was available
        //! or an end of file occured.
        //!
        bool getPacket(TSPacket& packet, BitRate& bitrate);

        //!
        //! Called by the reader thread to wait for packets.
        //! The reader thread is suspended until at least one packet is available.
        //! @param [out] buffer Address of packet buffer.
        //! @param [in] buffer_count Size of @a buffer in number of packets.
        //! @param [out] actual_count Number of returned packets in @a buffer.
        //! @param [out] bitrate Input bitrate or zero if unknown.
        //! @return True if a packets were returned in @a buffer. False on error or end of file.
        //!
        bool waitPackets(TSPacket* buffer, size_t buffer_count, size_t& actual_count, BitRate& bitrate);

        //!
        //! Check if the writer thread has reported an end of file condition.
        //! @return True if the writer thread has reported an end of file condition.
        //!
        bool eof() const;

        //!
        //! Called by the reader thread to tell the writer thread to stop immediately.
        //!
        void stop();

    private:
        volatile bool     _eof;         // The writer thread has reported an end of file.
        volatile bool     _stopped;     // The read thread has reported a stop condition.
        mutable Mutex     _mutex;       // Protect access to shared data.
        mutable Condition _enqueued;    // Signaled when packets are inserted.
        mutable Condition _dequeued;    // Signaled when packets were freed.
        TSPacketVector    _buffer;      // The packet buffer.
        PCRAnalyzer       _pcr;         // PCR analyzer to get the bitrate.
        size_t            _inCount;     // Number of packets currently inside the buffer.
        size_t            _readIndex;   // Index of next packet to read.
        size_t            _writeIndex;  // Index of next packet to write.
        BitRate           _bitrate;     // Bitrate as set by the writer thread.

        // Get bitrate, must be called with mutex held.
        BitRate getBitrate() const;
    };
}
