//
//  File:   hzTmplMapS.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 hzTmplMapM_h
#define hzTmplMapM_h
#include "hzString.h"
#include "hzLock.h"
#include "hzProcess.h"
#include "hzIsamT.h"
/*
**  The hzMapM template
*/
template<class KEY, class OBJ>  class   hzMapM
{
    //  Category:   Object Collection
    //
    //  The hzMapM template provides a memory resident one to many map of keys (class KEY) to objects (class OBJ). Although there can be many objects
    //  with the same key the hzMapM template uses the same _hz_map_Pair ISAM as hzMapS - meaning that the key is repeated for each object associated
    //  with it. Note for any given key, objects are inserted in the order of incidence so the insert location is always one place after the last.
    _hz_tmpl_ISAM   base ;          //  _hz_set_isam_Value ordered list by value
    KEY             m_NullKey ;     //  Null key
    mutable KEY     m_DefaultKey ;  //  Default key (effectively NULL)
    OBJ             m_NullObj ;     //  Null key
    mutable OBJ     m_DefaultObj ;  //  Default key (effectively NULL)
    //  Prevent copies
    hzMapM<KEY,OBJ>     (const hzMapM<KEY,OBJ>&) ;
    hzMapM<KEY,OBJ>&    operator=   (const hzMapM<KEY,OBJ>&) ;
public:
    hzMapM  (void)
    {
        base.Start(sizeof(KEY), sizeof(OBJ)) ;
        base.SetLock(HZ_NOLOCK) ;
        base.m_compare = _tmpl_map_compare<KEY,OBJ> ;
        memset(&m_NullKey, 0, sizeof(KEY)) ;
        memset(&m_NullObj, 0, sizeof(OBJ)) ;
        _hzGlobal_Memstats.m_numMmaps++ ;
    } 
    hzMapM  (hzLockOpt eLock)
    {
        base.Start(sizeof(KEY), sizeof(OBJ)) ;
        base.SetLock(eLock) ;
        base.m_compare = _tmpl_map_compare<KEY,OBJ> ;
        memset(&m_NullKey, 0, sizeof(KEY)) ;
        memset(&m_NullObj, 0, sizeof(OBJ)) ;
        _hzGlobal_Memstats.m_numMmaps++ ;
    } 
    hzMapM  (const hzString& name)
    {
        base.Start(sizeof(KEY), sizeof(OBJ)) ;
        base.SetLock(HZ_NOLOCK) ;
        base.SetName(name) ;
        base.m_compare = _tmpl_map_compare<KEY,OBJ> ;
        memset(&m_NullKey, 0, sizeof(KEY)) ;
        memset(&m_NullObj, 0, sizeof(OBJ)) ;
        _hzGlobal_Memstats.m_numMmaps++ ;
    }
    hzMapM  (hzLockOpt eLock, const hzString& name)
    {
        base.Start(sizeof(KEY), sizeof(OBJ)) ;
        base.SetLock(eLock) ;
        base.SetName(name) ;
        base.m_compare = _tmpl_map_compare<KEY,OBJ> ;
        memset(&m_NullKey, 0, sizeof(KEY)) ;
        memset(&m_NullObj, 0, sizeof(OBJ)) ;
        _hzGlobal_Memstats.m_numMmaps++ ;
    }
    ~hzMapM (void)  { _hzGlobal_Memstats.m_numMmaps-- ; }
    //  Init functions
    void    SetName         (const hzString& name)  { base.SetName(name) ; }
    void    SetLock         (hzLockOpt eLock)       { base.SetLock(eLock) ; }
    void    Clear   (void)  { base.Clear() ; }
    //  Insert and delete by key
    hzEcode Insert  (const KEY& key, const OBJ& obj)
    {
        _hzfunc("hzMapM::Insert") ;
        _hz_map_bkt<KEY,OBJ>*   pBuck ;
        _hz_vn_Dat* pDN ;
        int32_t     nSlot ;
        pDN = base.InsertKeyM(nSlot, &key) ;
        if (pDN)
        {
            pBuck = (_hz_map_bkt<KEY,OBJ>*) pDN->m_pElements ;
            pBuck->m_Keys[nSlot] = key ;
            pBuck->m_Objs[nSlot] = obj ;
            return E_OK ;
        }
        return hzerr(E_CORRUPT, "Failed to Insert") ;
    }
    hzEcode Delete  (const KEY& key)
    {
        _hzfunc("hzMapM:Delete") ;
        _hz_map_bkt<KEY,OBJ>*   pBuck ;
        _hz_vn_Dat* pDN ;
        int32_t     nSlot ;
        pDN = base._findDnodeByKey(nSlot, &key, HZ_ISAMSRCH_LO) ;
        if (!pDN)
            return E_NOTFOUND ;
        pDN = base.DeleteKey(nSlot, &key) ;
        //pBuck = (_hz_map_bkt<KEY,OBJ>*) pDN->m_pElements ;
        //pBuck->m_Keys[nSlot] = m_NullKey ;
        //pBuck->m_Objs[nSlot] = m_NullObj ;
        return E_OK ;
    }
    hzEcode Delete  (uint32_t nPosn)
    {
        _hzfunc("hzMapM:Delete(Posn)") ;
        _hz_map_bkt<KEY,OBJ>*   pBuck ;
        _hz_vn_Dat* pDN ;
        int32_t     nSlot ;
        pDN = base._findDnodeByPos(nSlot, nPosn, HZ_ISAMSRCH_LO) ;
        if (pDN)
            return E_NOTFOUND ;
        pBuck = (_hz_map_bkt<KEY,OBJ>*) pDN->m_pElements ;
        pBuck->m_Keys[nSlot] = m_NullKey ;
        pBuck->m_Objs[nSlot] = m_NullObj ;
        return base.DeletePosn(nPosn) ;
    }
    //  Locate keys or objects by position
    OBJ&    GetObj  (uint32_t nIndex) const
    {
        _hzfunc("hzMapM:GetObj") ;
        _hz_map_bkt<KEY,OBJ>*   pBuck ;
        _hz_vn_Dat* pDN ;
        int32_t     nSlot ;
        pDN = base._findDnodeByPos(nSlot, nIndex, false) ;
        if (!pDN)
        {
            m_DefaultObj = m_NullObj ;
            return m_DefaultObj ;
        }
        pBuck = (_hz_map_bkt<KEY,OBJ>*) pDN->m_pElements ;
        return pBuck->m_Objs[nSlot] ;
    }
    KEY&    GetKey  (uint32_t nIndex) const
    {
        _hzfunc("hzMapM:GetKey") ;
        _hz_map_bkt<KEY,OBJ>*   pBuck ;
        _hz_vn_Dat* pDN ;
        int32_t     nSlot ;
        pDN = base._findDnodeByPos(nSlot, nIndex, false) ;
        if (!pDN)
        {
            m_DefaultKey = m_NullKey ;
            return m_DefaultKey ;
        }
        pBuck = (_hz_map_bkt<KEY,OBJ>*) pDN->m_pElements ;
        return pBuck->m_Keys[nSlot] ;
    }
    //  Locate elements by value
    bool    Exists      (const KEY& key) const
    {
        _hzfunc("hzMapM:Exists") ;
        _hz_vn_Dat* pDN ;
        int32_t     nSlot ;
        pDN = base._findDnodeByKey(nSlot, &key, HZ_ISAMSRCH_LO) ;
        if (!pDN)
            return false ;
        return true ;
    }
    OBJ&    operator[]  (const KEY& key) const
    {
        _hzfunc("hzMapM:operator[]") ;
        _hz_map_bkt<KEY,OBJ>*   pBuck ;
        _hz_vn_Dat* pDN ;
        int32_t     nSlot ;
        pDN = base._findDnodeByKey(nSlot, &key, HZ_ISAMSRCH_LO) ;
        if (!pDN)
        {
            m_DefaultObj = m_NullObj ;
            return m_DefaultObj ;
        }
        pBuck = (_hz_map_bkt<KEY,OBJ>*) pDN->m_pElements ;
        return pBuck->m_Objs[nSlot] ;
    }
    //  Iteration support functions
    int32_t First       (const KEY key) const
    {
        _hzfunc("hzMapM:First") ;
        _hz_vn_Dat* pDN ;
        uint32_t    nPosn ;
        int32_t     nSlot ;
        if ((pDN = base._findAllByKey(nSlot, nPosn, &key, HZ_ISAMSRCH_LO)))
        {
            return 0x7fffffff & nPosn ;
        }
        return -1 ;
    }
    int32_t Last        (const KEY key) const
    {
        _hzfunc("hzMapM:Last") ;
        _hz_vn_Dat* pDN ;
        uint32_t    nPosn ;
        int32_t     nSlot ;
        if ((pDN = base._findAllByKey(nSlot, nPosn, &key, HZ_ISAMSRCH_HI)))
        {
            return 0x7fffffff & nPosn ;
        }
        return -1 ;
    }
    int32_t Target      (const KEY key) const
    {
        _hzfunc("hzMapM:Target") ;
        _hz_vn_Dat* pDN ;
        uint32_t    nPosn ;
        int32_t     nSlot ;
        if ((pDN = base._findAllByKey(nSlot, nPosn, &key, HZ_ISAMSRCH_END)))
        {
            return 0x7fffffff & nPosn ;
        }
        return -1 ;
    }
    //  Diagnostics
    uint32_t    Nodes       (void) const    { return base.Nodes() ; }
    uint32_t    Count       (void) const    { return base.Count() ; }
    hzEcode     NodeErrors  (void) const    { return base.NodeReport(true) ; }
    hzEcode     NodeReport  (void) const    { return base.NodeReport(false) ; }
} ;
/*
**  The hzSet template
*/
#endif  //  hzTmplMapM_h