//
//  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 hzTmplMapS_h
#define hzTmplMapS_h
#include <sys/stat.h>
#include "hzProcess.h"
#include "hzString.h"
#include "hzLock.h"
#include "hzIsamT.h"
/*
**  The formal hzMapS template
*/
template<class KEY, class OBJ>  class   hzMapS
{
    //  Category:   Object Collection
    //
    //  The hzMapS template provides a memory resident one to one map of keys to objects. The keys are required to be unique and there may only be one
    //  object per key. Both the keys and the objects can be of any C++, HadronZoo or application specific type.
    _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
    hzMapS<KEY,OBJ>     (const hzMapS<KEY,OBJ>&) ;
    hzMapS<KEY,OBJ>&    operator=   (const hzMapS<KEY,OBJ>&) ;
public:
    hzMapS  (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_numSmaps++ ;
    } 
    hzMapS  (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_numSmaps++ ;
    } 
    hzMapS  (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_numSmaps++ ;
    }
    hzMapS  (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_numSmaps++ ;
    }
    ~hzMapS (void)  { _hzGlobal_Memstats.m_numSmaps-- ; }
    //  Init functions
    void    SetLock     (hzLockOpt eLock)       { base.SetLock(eLock) ; }
    void    SetName     (const hzString& name)  { base.SetName(name) ; }
    void    Clear       (void)                  { base.Clear() ; }
    //  Insert and delete by key
    hzEcode Insert  (const KEY& key, const OBJ& obj)
    {
        //  _hzfunc("hzMapS::Insert") ;
        _hz_map_bkt<KEY,OBJ>*   pBuck ;
        _hz_vn_Dat* pDN ;
        int32_t     nSlot ;
        pDN = base.InsertKeyU(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("hzMapS::Delete") ;
        _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) ;
        return E_OK ;
    }
    //  Locate keys or objects by position
    OBJ&    GetObj  (uint32_t nIndex) const
    {
        //  _hzfunc("hzMapS::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("hzMapS::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("hzMapS::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("hzMapS::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] ;
    }
    int32_t First       (const KEY key) const
    {
        //  _hzfunc("hzMapS::First") ;
        uint32_t    nPosn ;
        int32_t     nSlot ;
        if (base._findAllByKey(nSlot, nPosn, key, HZ_ISAMSRCH_LO))
            return 0x7fffffff & nPosn ;
        return -1 ;
    }
    int32_t Last        (const KEY key) const
    {
        //  _hzfunc("hzMapS::Last") ;
        uint32_t    nPosn ;
        int32_t     nSlot ;
        if (base._findAllByKey(nSlot, nPosn, key, HZ_ISAMSRCH_HI))
            return 0x7fffffff & nPosn ;
        return -1 ;
    }
    int32_t Target      (const KEY key) const
    {
        //  _hzfunc("hzMapS::Target") ;
        uint32_t    nPosn ;
        int32_t     nSlot ;
        if (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) ; }
} ;
template<class KEY, class OBJ>  class   hzPMapS
{
    //  Category:   Object Collection
    //
    //  Persistent 1:1 map. This is a Serial Datacron implimented as a memory resident 1:1 map, plus delta file.
    //
    //  Note that classes represented by KEY and OBJ must have serialization functions. Specifically they must have a >> ifstream operator, and either a << ostream operator or an
    //  operator const char*.
    //
    //  Note that unlike hzMapS, the classes represented by KEY and OBJ must have serialization functions.
    hzMapS<KEY,OBJ> m_map ;     //  The map
    std::ofstream   m_os ;      //  Delta file
    hzString        m_path ;    //  Delta file path
    bool            m_bOpen ;   //  Is delta file open for writing
    //  Prevent copies
    hzPMapS<KEY,OBJ>        (const hzPMapS<KEY,OBJ>&) ;
    hzPMapS<KEY,OBJ>&   operator=   (const hzPMapS<KEY,OBJ>&) ;
public:
    hzPMapS (void)
    {
        m_map.SetLock(HZ_NOLOCK) ;
        m_bOpen = false ;
    } 
    hzPMapS (hzLockOpt eLock)
    {
        m_map.SetLock(eLock) ;
        m_bOpen = false ;
    } 
    hzPMapS (const hzString& name)
    {
        m_map.SetLock(HZ_NOLOCK) ;
        m_map.SetName(name) ;
        m_bOpen = false ;
    }
    hzPMapS (hzLockOpt eLock, const hzString& name)
    {
        m_map.SetLock(eLock) ;
        m_map.SetName(name) ;
        m_bOpen = false ;
    }
    ~hzPMapS    (void)  { _hzGlobal_Memstats.m_numSmaps-- ; }
    //  Init functions
    void    SetName     (const hzString& name)  { m_map.SetName(name) ; }
    void    DeltaSet    (const hzString& path)  { m_path = path ; }
    void    DeltaOpen   (void)
    {
        std::ifstream   is ;    //  Delta file for input
        struct stat     fs ;    //  File status
        KEY             key ;   //  Key
        OBJ             obj ;   //  Object
        //rc = OpenInputStrm(is, *m_path) ;
        if (lstat(*m_path, &fs) == 0)
        {
            if (fs.st_size)
            {
                is.open(*m_path) ;
                for (; is.tellg() < fs.st_size ;)
                {
                    is >> key ;
                    if (is.fail())
                        is.clear() ;
                    if (is.tellg() == fs.st_size)
                        break ;
                    is >> obj ;
                    if (is.fail())
                        is.clear() ;
                    if (is.tellg() == fs.st_size)
                        threadLog("B: At EOF\n") ;
                    if (!key)   continue ;
                    if (!obj)   continue ;
                    m_map.Insert(key, obj) ;
                }
                is.close() ;
            }
        }
        m_bOpen = true ;
        m_os.open(*m_path, std::ios::app) ;
    }
    void    DeltaClose  (void)      { m_os.close() ; }
    void    Clear       (void)      { m_map.Clear() ; }
    //  Insert and delete by key
    hzEcode Insert  (const KEY& key, const OBJ& obj)
    {
        hzEcode rc ;    //  Return code
        if (!m_bOpen)
            return E_WRITEFAIL ;
        rc = m_map.Insert(key, obj) ;
        if (rc == E_OK)
        {
            m_os << key << "\n" ;
            m_os << obj << "\n" ;
            m_os.flush() ;
        }
        return rc ;
    }
    hzEcode Delete  (const KEY& key)
    {
        hzEcode rc ;    //  Return code
        if (!m_bOpen)
            return E_WRITEFAIL ;
        rc = m_map.Delete(key) ;
        m_os << key << "\n" ;
        m_os << "0\n" ;
        m_os.flush() ;
        return rc ;
    }
    //  Locate keys or objects by position
    OBJ&    GetObj  (uint32_t nIndex) const { return m_map.GetObj(nIndex) ; }
    KEY&    GetKey  (uint32_t nIndex) const { return m_map.GetKey(nIndex) ; }
    //  Locate elements by value
    bool    Exists      (const KEY& key) const  { return m_map.Exists(key) ; }
    OBJ&    operator[]  (const KEY& key) const  { return m_map.operator[](key) ; }
    int32_t First       (const KEY key) const   { return m_map.First(key) ; }
    int32_t Last        (const KEY key) const   { return m_map.Last(key) ; }
    int32_t Target      (const KEY key) const   { return m_map.Target(key) ; }
    //  Diagnostics
    uint32_t    Nodes       (void) const    { return m_map.Nodes() ; }
    uint32_t    Count       (void) const    { return m_map.Count() ; }
    hzEcode     NodeErrors  (void) const    { return m_map.NodeReport(true) ; }
    hzEcode     NodeReport  (void) const    { return m_map.NodeReport(false) ; }
} ;
#endif  //  hzTmplMapS_h