//
//  File:   hzTmplQue.h
//
//  Legal Notice: This file is part of the HadronZoo C++ Class Library.
//
//  Copyright 2025 HadronZoo Project (http://www.hadronzoo.com)
//
//  The HadronZoo C++ Class 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 of the License, or any later version.
//
//  The HadronZoo C++ Class 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 the HadronZoo C++ Class Library. If not, see http://www.gnu.org/licenses.
//
#ifndef hzTmplQue_h
#define hzTmplQue_h
#include "hzString.h"
#include "hzLock.h"
#include "hzProcess.h"
/*
**  Prototypes
*/
void    Fatal   (const char* va_alist ...) ;
template <class OBJ>    class   _hz_queitem
{
    //  Category:   Collection Support
    //
    //  List element containing an element and a pointer to the next element. For use in the unordered list class templates, hzList, hzQue and hzStack.
public:
    _hz_queitem*    next ;      //  Next item in list
    OBJ             m_Obj ;     //  Object in list
    _hz_queitem (OBJ obj)   { next = 0 ; m_Obj = obj ; }
} ;
template <class OBJ>    class   hzQue
{
    //  Category:   Object Collection
    //
    //  The hzQue class template faciliates a memory resident que of objects. A que is characterized by PUSH and POP operations in which new
    //  objects are injected at the back of the que (pushed) and objects are removed from the front of the que (pulled).
private:
    struct  _que_ca
    {
        //  Struct to hold actual content (avoid hard copying)
        _hz_queitem<OBJ>*   m_pList ;       //  Start of que
        _hz_queitem<OBJ>*   m_pLast ;       //  End of que
        hzLocker*           m_pLock ;       //  Locking (off by default)
        hzString            m_Name ;        //  Diagnostics & reports
        uint32_t            m_nCount ;      //  Population
        _mut uint32_t       m_nCopy ;       //  Copy count
        OBJ                 m_Default ;     //  Default (null) element
        _que_ca     (void)  { memset(&m_Default, 0, sizeof(OBJ)) ; m_pList = m_pLast = 0 ; m_pLock = 0 ; m_nCount = m_nCopy = 0 ; }
        ~_que_ca    (void)  {}
        void    SetLock (hzLockOpt eLock)
        {
            if (eLock == HZ_ATOMIC) m_pLock = new hzLockRW() ;
            if (eLock == HZ_MUTEX)  m_pLock = new hzLockRWD() ;
        }
        void    LockRead    (void)  { if (m_pLock) m_pLock->LockRead() ; }
        void    LockWrite   (void)  { if (m_pLock) m_pLock->LockWrite() ; }
        void    Unlock      (void)  { if (m_pLock) m_pLock->Unlock() ; }
    } ;
    _que_ca*    mx ;    //  Pointer to internal content
    //  Prevent copies
    hzQue<OBJ>  (const hzQue<OBJ>&) ;
    hzQue<OBJ>& operator=   (const hzQue<OBJ>&) ;
public:
    //  Constructors
    hzQue   (hzLockOpt eLock = HZ_NOLOCK)   { mx = new _que_ca() ; mx->SetLock(eLock) ; }
    hzQue   (const hzString name)           { mx = new _que_ca() ; mx->m_Name = name ; mx->SetLock(HZ_MUTEX) ; }
    //  Destructor
    ~hzQue  (void)
    {
        if (mx->m_nCopy > 0)
            mx->m_nCopy-- ;
        else
        {
            Clear() ;
            delete mx ;
            mx = 0 ;
        }
    }
    void    Clear   (void)
    {
        _hz_queitem<OBJ>*   pC ;    //  Curreent list item
        _hz_queitem<OBJ>*   pN ;    //  Next list item
        for (pC = mx->m_pList ; pC ; pC = pN)
        {
            pN = pC->next ;
            delete pC ;
        }
        mx->m_pList = mx->m_pLast = 0 ;
        mx->m_nCount = 0 ;
    }
    //  Assignment operator
    /*
    const hzQue<OBJ>&   operator=   (const hzQue<OBJ>& op)
    {
        //  If this control area is the same as that of the operand, do nothing. Otherwise decouple from or clear the control area and adopt that of the operand.
        if (mx == op.mx)
            return *this ;
            //  If there is an existing list, clear it.
        if (mx->m_nCopy)
            mx->m_nCopy-- ;
        else
            mx->Clear() ;
        mx = op.mx ;
        mx->m_nCopy++ ;
        return *this ;
    }
    */
    uint32_t    Count   (void)
    {
        //  Return the number of items in the queue
        return mx->m_nCount ;
    }
    hzEcode Push    (const OBJ& obj)
    {
        //  Push supplied object onto the queue
        _hz_queitem<OBJ>*   pTL ;   //  List item to accomodate new object
        if (!(pTL = new _hz_queitem<OBJ>(obj)))
            Fatal("Could not allocate new que element") ;
        if (mx->m_pList == 0)
            mx->m_pList = mx->m_pLast = pTL ;
        else
        {
            //mx->m_pLast->m_pNext = pTL ;
            mx->m_pLast->next = pTL ;
            mx->m_pLast = pTL ;
        }
        mx->m_nCount++ ;
        return E_OK ;
    }
    OBJ&    Pull    (void)
    {
        //  Pull an object from the queue
        _hz_queitem<OBJ>*   pTL ;   //  Object-link iterator
        OBJ     obj ;               //  Current object-link
        if ((pTL = mx->m_pList))
        {
            obj = mx->m_pList->m_Obj ;
            //mx->m_pList = mx->m_pList->m_pNext ;
            mx->m_pList = mx->m_pList->next ;
            delete pTL ;
            mx->m_nCount-- ;
        }
        return mx->m_Default ;
    }
    hzEcode Delete  (const OBJ& obj)
    {
        //  Delete the supplied object from the queue (on condition it is foundin the queue)
        _hz_queitem<OBJ>*   pTL ;   //  Current link
        _hz_queitem<OBJ>*   pPL ;   //  Previous link
        hzEcode rc = E_NOTFOUND ;   //  Return code
        //  match obj pointer to one on list
        pPL = 0 ;
        for (pTL = mx->m_pList ; pTL ; pTL = pTL->m_pNext)
        {
            if (pTL->m_Obj == obj)
            {
                //  found item to delete
                if (pTL == mx->m_pList)
                    //mx->m_pList = mx->m_pList->m_pNext ;
                    mx->m_pList = mx->m_pList->next ;
                //  If the item is the last
                if (pTL == mx->m_pLast)
                    mx->m_pLast = pPL ;
                //  tie adjacent items together
                if (pPL)
                    //pPL->m_pNext = pTL->m_pNext;
                    pPL->next = pTL->next;
                delete pTL ;
                mx->m_nCount-- ;
                rc = E_OK ;
                break ;
            }
            pPL = pTL ;
        }
        return rc ;
    }
} ;
#endif  //  hzTmplQue_h