//
//  File:   hzNamering.cpp
//
//  Legal Notice: This file is part of the HadronZoo C++ Class Library.
//
//  Copyright 1998, 2018 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.
//
//
//  Implimentation of hzNamering class described in hzNamering.h
//
#include <fstream>
#include "hzBasedefs.h"
#include "hzString.h"
#include "hzErrcode.h"
#include "hzProcess.h"
#include "hzNamering.h"
extern  uint32_t    _hzGlobal_numNamrings ; //  Total allocation of name-rings
hzNamering::hzNamering  (void)
{
    //  Default constructor with optional bool arg for locking. Creates a new name-ring and sets the maps compare functions to operate as case insensitive.
    _hzfunc("hzNamering::hzNamering") ;
    mx = new _name_ring() ;
    if (!mx)
        hzexit(E_MEMORY, "Could not allocate internal structure") ;
    //mx->m_Roots.SetCompare(StringCompareI) ;
    //mx->m_Members.SetCompare(StringCompareI) ;
    _hzGlobal_numNamrings++ ;
}
hzNamering::hzNamering  (hzNamering& op)
{
    //  Copy constructor. Note that while this will create a new instance of hzNamering, the internal component is not copied.
    Clear() ;
    mx = op.mx ;
    mx->m_nNoCopies++ ;
    _hzGlobal_numNamrings++ ;
}
hzNamering::~hzNamering (void)
{
    //  Delete the hzNamering instance. If the internal component is pointed to by another hzNamering then this is not deleted.
    if (mx->m_nNoCopies)
        mx->m_nNoCopies-- ;
    else
    {
        Clear() ;
        delete mx ;
    }
    _hzGlobal_numNamrings-- ;
}
/*
**  MT Locking
*/
//  Allocate a hzDLock to control access to the hzNamering. This should be done at startup but can be done at any point.
hzEcode hzNamering::UseLocking  (const char* name)      { return mx->m_Lock.Setname(name) ; }
hzEcode hzNamering::UseLocking  (const hzString& name)  { return mx->m_Lock.Setname(name) ; }
void    hzNamering::Clear   (void)
{
    //  Clear the name-ring of all keys and elements
    //
    //  Arguments:  None
    //  Returns:    None
    _hzfunc("hzNamering::Clear") ;
    mx->m_Lock.LockWrite() ;
    mx->m_Roots.Clear() ;
    mx->m_Members.Clear() ;
    mx->m_Lock.Unlock() ;
}
uint32_t    hzNamering::RootIncidence   (const hzString& member) const
{
    //  Perform a lookup for a string assumed to be a member of one or more groups.
    //
    //  Arguments:  1)  member  Name of name-ring member sought
    //
    //  Returns:    Number of groups the string was found in.
    _hzfunc("hzNamering::RootIncidence") ;
    uint32_t    nLo = 0 ;   //  First matching member found in root
    uint32_t    nHi = 0 ;   //  Last matching member found in root
    if (!member)
        return -1 ;
    mx->m_Lock.LockWrite() ;
    nLo = mx->m_Roots.First(member) ;
    if (nLo >= 0)
        nHi = mx->m_Roots.Last(member) ;
    mx->m_Lock.Unlock() ;
    if (nHi < 0)
        return 0 ;
    return (nHi - nLo) + 1 ;
}
hzString    hzNamering::RootLocate  (const hzString& member) const
{
    //  Perform a lookup for a string assumed to be a member of one or more groups and stop at the first group the member fits.
    //
    //  Arguments:  1)  member  Name of name-ring member sought
    //
    //  Returns:    Instance of hzString by value being root of the first group the member fits.
    _hzfunc("hzNamering::RootLocate(1)") ;
    hzString    S ;     //  Target  string
    uint32_t    nPos ;  //  First name group found
    if (member)
    {
        mx->m_Lock.LockWrite() ;
        nPos = mx->m_Roots.First(member) ;
        if (nPos >= 0)
            S = mx->m_Roots.GetObj(nPos) ;
        mx->m_Lock.Unlock() ;
    }
    return S ;
}
uint32_t    hzNamering::RootLocate  (hzVect<hzString>& results, const hzString& member) const
{
    //  Locate all possible roots for a given member and place the root name for each in the supplied results vector.
    //
    //  Arguments:  1)  results Vector of strings found
    //              2)  member  Name of name-ring member sought
    //
    //  Returns:    Number of roots found
    _hzfunc("hzNamering::RootLocate(2)") ;
    hzString    S ;         //  Target string
    uint32_t    nPos ;      //  First name group found
    uint32_t    nLo = 0 ;   //  First matching member found in root
    uint32_t    nHi = 0 ;   //  Last matching member found in root
    results.Clear() ;
    if (!member)
        return 0 ;
    mx->m_Lock.LockWrite() ;
    nLo = mx->m_Roots.First(member) ;
    if (nLo >= 0)
    {
        nHi = mx->m_Roots.Last(member) ;
        for (nPos = nLo ; nPos <= nHi ; nPos++)
        {
            S = mx->m_Roots.GetObj(nPos) ;
            results.Add(S) ;
        }
    }
    mx->m_Lock.Unlock() ;
    return results.Count() ;
}
uint32_t    hzNamering::MemberLocate    (hzVect<hzString>& results, const hzString& root) const
{
    //  Locate all members of a supplied root.
    //
    //  Arguments:  1)  results A vector of strings to be populated by the operation. These will be members of the supplied root.
    //              2)  root    The supplied root.
    //
    //  Returns:    Number of members found.
    _hzfunc("hzNamering::MemberLocate") ;
    hzString        S ;         //  Target string
    uint32_t    nPos ;      //  First name group found
    uint32_t    nLo = 0 ;   //  First matching member found in root
    uint32_t    nHi = 0 ;   //  Last matching member found in root
    results.Clear() ;
    if (!root)
        return -1 ;
    mx->m_Lock.LockWrite() ;
    nLo = mx->m_Members.First(root) ;
    if (nLo >= 0)
    {
        nHi = mx->m_Members.Last(root) ;
        for (nPos = nLo ; nPos <= nHi ; nPos++)
        {
            S = mx->m_Members.GetObj(nPos) ;
            results.Add(S) ;
        }
    }
    mx->m_Lock.Unlock() ;
    return results.Count() ;
}
hzString    hzNamering::RootLocate  (uint32_t nPos) const
{
    //  Locate the Nth root of the name-ring
    //
    //  Arguments:  1)  nPos    The Nth root of the name-ring
    //
    //  Returns:    Instance of hzString by value being Nth root name
    _hzfunc("hzNamering::RootLocate(3)") ;
    hzString    S ;         //  Target string
    mx->m_Lock.LockWrite() ;
    if (nPos >= 0 && nPos < mx->m_Roots.Count())
    {
        S = mx->m_Groups.GetObj(nPos) ;
    }
    mx->m_Lock.Unlock() ;
    return S ;
}
hzEcode hzNamering::AddRoot (const hzString& root)
{
    //  Insert a new element with a key or update an existing element that matches the key.
    //  If root exists, reject
    //
    //  Arguments:  1)  root    The root to add
    //
    //  Returns:    E_ARGUMENT  If the supplied root name is blank
    //              E_DUPLICATE If a group with that root name already exists
    //              E_OK        If the group is added
    _hzfunc("hzNamering::AddRoot") ;
    hzEcode     rc = E_OK ;     //  Return code
    if (!root)
        return E_ARGUMENT ;
    mx->m_Lock.LockWrite() ;
    if (mx->m_Groups.Exists(root))
        rc = E_DUPLICATE ;
    else
    {
        mx->m_Groups.Insert(root) ;
        mx->m_Roots.Insert(root,root) ;
        mx->m_Members.Insert(root,root) ;
    }
    mx->m_Lock.Unlock() ;
    return rc ;
}
hzEcode hzNamering::AddMember   (const hzString& root, const hzString& member)
{
    //  Add a member to the group with the given root name
    //
    //  Arguments:  1)  root    The name of the root
    //              2)  member  The name of the member
    //
    //  Returns:    E_ARGUMENT  If either the group's root name or the new member name are blank
    //              E_OK        If the member is added to the group
    _hzfunc("hzNamering::AddMember") ;
    if (!root || !member)
        return E_ARGUMENT ;
    mx->m_Lock.LockWrite() ;
    if (!mx->m_Groups.Exists(root))
    {
        mx->m_Groups.Insert(root) ;
        mx->m_Roots.Insert(root,root) ;
    }
    mx->m_Roots.Insert(member,root) ;
    mx->m_Members.Insert(root,member) ;
    mx->m_Lock.Unlock() ;
    return E_OK ;
}
hzEcode hzNamering::DelRoot     (const hzString& root)
{
    //  Delete from m_Root then delete all references to it from m_Roots (not implimented)
    //
    //  Arguments:  1)  root    The name of the root
    //
    //  Returns:    E_NOTFOUND  If the supplied root does not exist
    //              E_OK        If the root was deleted
    _hzfunc("hzNamering::DelRoot") ;
    hzEcode rc = E_OK ;     //  Return code
    mx->m_Lock.LockWrite() ;
        //  STUB
    mx->m_Lock.Unlock() ;
    return rc ;
}
hzEcode hzNamering::DelMember   (const hzString& root)
{
    //  Delete from m_Roots only (not implimented)
    //
    //  Arguments:  1)  root    The name of the root
    //
    //  Returns:    E_NOTFOUND  If the supplied member does not exist
    //              E_OK        If the member was deleted
    _hzfunc("hzNamering::DelMember") ;
    hzEcode rc = E_OK ;     //  Return code
    mx->m_Lock.LockWrite() ;
        //  STUB
    mx->m_Lock.Unlock() ;
    return rc ;
}
hzNamering& hzNamering::operator=   (hzNamering& op)
{
    //  Make this namering equal the operand namering
    _hzfunc("hzNamering::op=") ;
    mx->m_Lock.LockWrite() ;
        Clear() ;
        mx = op.mx ;
        mx->m_nNoCopies++ ;
    mx->m_Lock.Unlock() ;
    return *this ;
}
void    hzNamering::Report  (std::ofstream& os) const
{
    //  Provide a namering summary report to a file stream
    //
    //  Arguments:  1)  os  Output stream
    //  Returns:    None
    _hzfunc("hzNamering::Report(os)") ;
    hzString    key ;   //  Lookup key for roots
    hzString    obj ;   //  Object found for key
    uint32_t    nW ;    //  Number of applicable roots for a word
    os << "member::root\n" ;
    for (nW = 0 ; nW < mx->m_Roots.Count() ; nW++)
    {
        key = mx->m_Roots.GetKey(nW) ;
        obj = mx->m_Roots.GetObj(nW) ;
        os << "\t[" << key << "]\t->\t[" << obj << "]\n" ;
    }
    os << "root::member\n" ;
    for (nW = 0 ; nW < mx->m_Members.Count() ; nW++)
    {
        key = mx->m_Members.GetKey(nW) ;
        obj = mx->m_Members.GetObj(nW) ;
        os << "\t[" << key << "]\t->\t[" << obj << "]\n" ;
    }
    os << "@end\n\n" ;
}
void    hzNamering::Report  (hzLogger* plog) const
{
    //  Provide a namering summary report to a logfile
    //
    //  Arguments:  1)  plog    Output logger
    //  Returns:    None
    _hzfunc("hzNamering::Report(log)") ;
    hzString    key ;   //  Lookup key for roots
    hzString    obj ;   //  Object found for key
    uint32_t    nW ;    //  Number of applicable roots for a word
    if (!plog)
        plog = GetThreadLogger() ;
    if (plog)
    {
        plog->Out("member::root\n") ;
        for (nW = 0 ; nW < mx->m_Roots.Count() ; nW++)
        {
            key = mx->m_Roots.GetKey(nW) ;
            obj = mx->m_Roots.GetObj(nW) ;
            plog->Out("\t[%s]\t->\t[%s]\n", *key, *obj) ;
        }
        plog->Out("root::member\n") ;
        for (nW = 0 ; nW < mx->m_Members.Count() ; nW++)
        {
            key = mx->m_Members.GetKey(nW) ;
            obj = mx->m_Members.GetObj(nW) ;
            plog->Out("\t[%s]\t->\t[%s]\n", *key, *obj) ;
        }
        plog->Out("@end\n\n") ;
    }
}