////////////////////////////////////////////////////////////////////////////////
//                                                                             /
// 2012-2019 (c) Baical                                                        /
//                                                                             /
// 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 3.0 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.                                            /
//                                                                             /
////////////////////////////////////////////////////////////////////////////////
#ifndef BK_STREAMS_H
#define BK_STREAMS_H

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class ISubscriberOwner
{
public: 
    virtual void Unregister_Subscriber(Bk::IStreams *i_pStreams) = 0;
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class CStreams
    : public Bk::IStreams
{
    struct stStream
    {
        Bk::IStorageReader *m_pReader;
        Bk::IStreamEx      *m_pExtra;
        
        stStream(Bk::IStorageReader *i_pReader, Bk::IStreamEx *i_pExtra)
            : m_pReader(i_pReader)
            , m_pExtra(i_pExtra)
        {
            if (m_pReader)
            {
                m_pReader->Add_Ref();
            }

            if (m_pExtra)
            {
                m_pExtra->Add_Ref();
            }
        }

        ~stStream()
        {
            if (m_pReader)
            {
                m_pReader->Release();
                m_pReader = NULL;
            }

            if (m_pExtra)
            {
                m_pExtra->Release();
                m_pExtra = NULL;
            }
        }
    };

    tINT32 volatile   m_lReference;
    CBList<stStream*> m_cStreams;
    CLock             m_cLock;
    CMEvent           m_hSem;
    ISubscriberOwner *m_iOwner;

public:
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CStreams(ISubscriberOwner *i_iOwner)
        : m_lReference(1)
        , m_iOwner(i_iOwner)
    {
        CLock l_cLock(&m_cLock);
        m_hSem.Init(1, EMEVENT_MULTI);
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    virtual ~CStreams()
    {
        if (m_iOwner)
        {
            m_iOwner->Unregister_Subscriber(this);
        }

        m_cLock.Lock();
        m_cStreams.Clear(TRUE);
        m_cLock.Unlock();
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    Bk::eResult __stdcall Pull(Bk::IStorageReader *&o_rReader, Bk::IStreamEx *&o_rExtra, tUINT32 i_uiTimeout)
    {
        Bk::eResult l_eResult = Bk::eErrorTimeout;

        if (MEVENT_SIGNAL_0 == m_hSem.Wait(i_uiTimeout))
        {
            CLock       l_cLock(&m_cLock);
            pAList_Cell l_pEl     = m_cStreams.Get_First();
            stStream   *l_pStream = m_cStreams.Get_Data(l_pEl);
            if (l_pStream)
            {
                o_rReader = l_pStream->m_pReader;
                o_rExtra  = l_pStream->m_pExtra;

                if (o_rReader)
                {
                    o_rReader->Add_Ref();
                }

                if (o_rExtra)
                {
                    o_rExtra->Add_Ref();
                }

                l_eResult = Bk::eOk;
            }

            if (l_pEl)
            {
                m_cStreams.Del(l_pEl, TRUE);
            }
        }

        return l_eResult;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    Bk::eResult Push(Bk::IStorageReader *i_pReader, Bk::IStreamEx *i_pExtra)
    {
        CLock l_cLock(&m_cLock);
        m_cStreams.Add_After(m_cStreams.Get_Last(), new stStream(i_pReader, i_pExtra));
        m_hSem.Set(MEVENT_SIGNAL_0);
        return Bk::eOk;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    tINT32 __stdcall Add_Ref()
    {
        return ATOMIC_INC(&m_lReference);
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    tINT32 __stdcall Release()
    {
        tINT32 l_lResult = ATOMIC_DEC(&m_lReference);
        if ( 0 >= l_lResult )
        {
            delete this;
        }

        return l_lResult;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    Bk::eResult __stdcall Query_Interface(Bk::eInterface i_eId, void *&o_rUnknown)
    {
        UNUSED_ARG(i_eId);
        UNUSED_ARG(o_rUnknown);
        return Bk::eErrorNotImplemented;
    }
};

#endif //BK_STREAMS_H
