//
//  File:   hzAtom.cpp
//
//  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.
//
#include <iostream>
#include <fstream>
#include <pthread.h>
#include "hzTextproc.h"
#include "hzChars.h"
#include "hzCodec.h"
#include "hzDatabase.h"
using namespace std ;
/*
**  Functions to read atom values
*/
static  hzString    s_cpp_undef = "CPP_UNDEF" ;
static  hzString    s_hzo_undef = "HZO_UNDEF" ;
static  hzString    s_bool_true = "TRUE" ;
static  hzString    s_bool_false = "FALSE" ;
static  hzString    s_atom_error = "Atom in Error" ;
static  hzString    s_atom_state = "Atom Unknown Status" ;
const char* hzAtom::Show    (void) const
{
    //  Purpose:    Formulate a textual representation of the atom value
    //
    //  Arguments:  1)  eFmt    Format (optional)
    //
    //  Returns:    Instance of hzString by value being atom value in text form.
    _hzfunc("hzAtom::Show") ;
    const char* ptr ;       //  From string-like entities
    char*       pBuf ;      //  Scratch pad buffer
    hzMD5*      pMd5 ;      //  Digest
    hzXDate     xd ;        //  Recepticle for hzXDate data
    hzDomain    dom ;       //  Domain
    hzEmaddr    ema ;       //  Recepticle for hzEmaddr data
    hzUrl       url ;       //  Recepticle for hzUrl data
    hzIpaddr    ipa ;       //  Recepticle for hzIpaddr data
    hzSDate     sd ;        //  Recepticle for hzSDate data
    hzTime      ti ;        //  Recepticle for hzTime data
    hzString    str ;       //  Temp str
    //threadLog("status %d type %d val %x\n", m_eStatus, m_eType, m_Data.m_uInt64) ;
    if (m_eStatus == ATOM_CLEAR)    return 0 ;
    if (m_eStatus == ATOM_ERROR)    return s_atom_error ;
    if (m_eStatus != ATOM_SET)      return s_atom_state ;
    switch (m_eType)
    {
    case BASETYPE_UNDEF:        return 0 ;
    case BASETYPE_CPP_UNDEF:    return *s_cpp_undef ;
    case BASETYPE_DIGEST:       pMd5 = (hzMD5*) m_Data.m_pVoid ;
                                if (pMd5)
                                    return pMd5->Txt() ;
                                return 0 ;
    case BASETYPE_DOUBLE:       pBuf = _thisfn.ScratchPad(24) ; sprintf(pBuf, "%f", m_Data.m_Double) ;  return pBuf ;
    case BASETYPE_INT64:        pBuf = _thisfn.ScratchPad(24) ; sprintf(pBuf, "%ld", m_Data.m_sInt64) ; return pBuf ;
    case BASETYPE_INT32:        pBuf = _thisfn.ScratchPad(24) ; sprintf(pBuf, "%d", m_Data.m_sInt32) ;  return pBuf ;
    case BASETYPE_INT16:        pBuf = _thisfn.ScratchPad(24) ; sprintf(pBuf, "%d", m_Data.m_sInt16) ;  return pBuf ;
    case BASETYPE_BYTE:         pBuf = _thisfn.ScratchPad(24) ; sprintf(pBuf, "%d", m_Data.m_sByte) ;   return pBuf ;
    case BASETYPE_UINT64:       pBuf = _thisfn.ScratchPad(24) ; sprintf(pBuf, "%lu", m_Data.m_uInt64) ; return pBuf ;
    case BASETYPE_UINT32:       pBuf = _thisfn.ScratchPad(24) ; sprintf(pBuf, "%u", m_Data.m_uInt32) ;  return pBuf ;
    case BASETYPE_UINT16:       pBuf = _thisfn.ScratchPad(24) ; sprintf(pBuf, "%u", m_Data.m_uInt16) ;  return pBuf ;
    case BASETYPE_UBYTE:        pBuf = _thisfn.ScratchPad(24) ; sprintf(pBuf, "%u", m_Data.m_uByte) ;   return pBuf ;
    case BASETYPE_BOOL:         return m_Data.m_Bool ? *s_bool_true : *s_bool_false ;
    case BASETYPE_HZO_UNDEF:    return *s_hzo_undef ;
    case BASETYPE_STRING:       str._int_set(m_Data.m_uInt32) ; ptr = (const char*) str ; str._int_clr() ; return ptr ;
    case BASETYPE_DOMAIN:       dom._int_set(m_Data.m_uInt32) ; ptr = (const char*) dom ; dom._int_clr() ; return ptr ;
    case BASETYPE_EMADDR:       ema._int_set(m_Data.m_uInt32) ; ptr = (const char*) ema ; ema._int_clr() ; return ptr ;
    case BASETYPE_URL:          return *Url() ;
    case BASETYPE_IPADDR:       return *Ipaddr() ;
    case BASETYPE_TIME:         return *Time() ;
    case BASETYPE_SDATE:        return *SDate() ;
    case BASETYPE_XDATE:        xd.SetDate(m_Data.m_uInt64) ;
                                return xd.Txt() ;
    case BASETYPE_TEXT:         pBuf = _thisfn.ScratchPad(24) ; sprintf(pBuf, "TXT ref=%x", m_Data.m_uInt32) ;  return pBuf ;
    case BASETYPE_BINARY:       pBuf = _thisfn.ScratchPad(24) ; sprintf(pBuf, "BIN ref=%x", m_Data.m_uInt32) ;  return pBuf ;
    case BASETYPE_TXTDOC:       pBuf = _thisfn.ScratchPad(24) ; sprintf(pBuf, "DOC ref=%x", m_Data.m_uInt32) ;  return pBuf ;
    case BASETYPE_ENUM:         pBuf = _thisfn.ScratchPad(24) ; sprintf(pBuf, "%u",         m_Data.m_sInt32) ;  return pBuf ;
    case BASETYPE_APPDEF:       pBuf = _thisfn.ScratchPad(24) ; sprintf(pBuf, "APP ref=%x", m_Data.m_uInt32) ;  return pBuf ;
    case BASETYPE_CLASS:        pBuf = _thisfn.ScratchPad(24) ; sprintf(pBuf, "OBJ ref=%x", m_Data.m_uInt32) ;  return pBuf ;
    }
    return 0 ;
}
const hzMD5     hzAtom::MD5     (void) const
{
    //  Retrieve hzMD5 from this atom.
    //
    //  Arguments:  None
    //  Returns:    hzMD5 instance by value.
    hzMD5*  pMd5 ;      //  Pointer to cast
    if (m_eStatus == ATOM_SET && m_eType == BASETYPE_DIGEST)
    {
        pMd5 = (hzMD5*) &m_Data.m_pVoid ;
        return *pMd5 ;
    }
    return _hz_null_hzMD5 ;
}
const hzChain   hzAtom::Chain   (void) const
{
    //  Retrieve hzChain from this atom. This will be populated if the hzAtom has a value and the datatype is BASETYPE_STRING, and will be empty otherwise
    //
    //  Arguments:  None
    //  Returns:    hzChain instance by value.
    if (m_eStatus == ATOM_SET && (m_eType == BASETYPE_TXTDOC || m_eType == BASETYPE_BINARY))
    {
        hzChain     ret ;   //  To be returned
        hzChain     tmp ;   //  Cast
        tmp._int_set(m_Data.m_pVoid) ; ret = tmp ; tmp._int_clr() ; return ret ;
    }
    return _hz_null_hzChain ;
}
const hzString  hzAtom::Str (void) const
{
    //  Retrieve string from this atom. This will be populated only if the hzAtom has a value, the datatype is BASETYPE_STRING, and the string value was set to an actual hzString
    //  instance, as opposed to being set to a string held in a string repository.
    //
    //  Arguments:  None
    //  Returns:    hzString instance by value
    if (m_eStatus == ATOM_SET && m_eType == BASETYPE_STRING)
    {
        hzString    tmp ;   //  Cast
        hzString    ret ;   //  To be returned
        tmp._int_set(m_Data.m_uInt32) ; ret = tmp ; tmp._int_clr() ; return ret ;
    }
    return _hzGlobal_nullString ;
}
const char* hzAtom::Cstr    (void) const
{
    //  Retrieve Cstr from this atom. This will be populated if the hzAtom has a value and the datatype is BASETYPE_STRING, regardless of how the string value was set.
    //
    //  Arguments:  None
    //  Returns:    Instance of hzEmail by value.
    if (m_eStatus == ATOM_SET && m_eType == BASETYPE_STRING)
    {
        const char* pStr ;  //  Return string value
        hzString    tmp ;   //  Cast
        tmp._int_set(m_Data.m_uInt32) ; pStr = *tmp ; tmp._int_clr() ; return pStr ;
    }
    return 0 ;
}
const hzDomain  hzAtom::Domain  (void) const
{
    //  Retrieve domain name from this atom. This will be populated if the hzAtom has a value and the datatype is BASETYPE_DOMAIN and be empty otherwise
    //
    //  Arguments:  None
    //  Returns:    hzDomain instance by value
    if (m_eStatus == ATOM_SET && m_eType == BASETYPE_DOMAIN)
    {
        hzDomain    dom ;   //  To be returned
        hzDomain    tmp ;   //  Cast
        tmp._int_set(m_Data.m_uInt32) ; dom = tmp ; tmp._int_clr() ; return dom ;
    }
    return _hz_null_hzDomain ;
}
const hzEmaddr  hzAtom::Emaddr  (void) const
{
    //  Retrieve email address from this atom. This will be populated if the hzAtom has a value and the datatype is BASETYPE_EMADDR and be empty otherwise
    //
    //  Arguments:  None
    //  Returns:    hzEmail Instance by value
    if (m_eStatus == ATOM_SET && m_eType == BASETYPE_EMADDR)
    {
        hzEmaddr    ema ;   //  To be returned
        //hzEmaddr  tmp ;   //  Cast
        if (m_Data.m_uInt32)
        {
            ema._int_set(m_Data.m_uInt32) ;
            ema._inc_copy() ;
            return ema ;
        }
        //tmp._int_set(m_Data.m_uInt32) ; ema = tmp ; tmp._int_clr() ; return ema ;
    }
    return _hz_null_hzEmaddr ;
}
const hzUrl hzAtom::Url (void) const
{
    //  Retrieve URL from this atom. This will be populated if the hzAtom has a value and the datatype is BASETYPE_EMADDR and be empty otherwise
    //
    //  Arguments:  None
    //  Returns:    hzUrl instance of hzUrl by value.
    if (m_eStatus == ATOM_SET && m_eType == BASETYPE_URL)
    {
        hzUrl   url ;   //  To be returned
        hzUrl   tmp ;   //  Cast
        tmp._int_set(m_Data.m_uInt32) ; url = tmp ; tmp._int_clr() ; return url ;
    }
    return _hz_null_hzUrl ;
}
const hzXDate   hzAtom::XDate   (void) const
{
    //  Arguments:  None
    //  Returns:    Instance of hzXDate by value. This will be populated if the hzAtom has a value and the datatype is BASETYPE_XDATE and be empty otherwise.
    hzXDate tmp ;       //  Pointer to cast
    if (m_eStatus == ATOM_SET && m_eType == BASETYPE_XDATE)
    {
        tmp.SetDate(m_Data.m_uInt64) ;
        return tmp ;
    }
    return _hz_null_hzXDate ;
}
const hzSDate   hzAtom::SDate   (void) const
{
    //  Arguments:  None
    //  Returns:    Instance of hzSDate by value. This will be populated if the hzAtom has a value and the datatype is BASETYPE_SDATE and be empty otherwise.
    hzSDate tmp ;       //  Return value
    if (m_eStatus == ATOM_SET && m_eType == BASETYPE_SDATE)
    {
        tmp.SetDate(m_Data.m_uInt32) ;
        return tmp ;
    }
    return _hz_null_hzSDate ;
}
const hzTime    hzAtom::Time    (void) const
{
    //  Arguments:  None
    //  Returns:    Instance of hzTime by value. This will be populated if the hzAtom has a value and the datatype is BASETYPE_TIME and be empty otherwise.
    hzTime  tmp ;       //  Return value
    if (m_eStatus == ATOM_SET && m_eType == BASETYPE_TIME)
    {
        tmp.SetTime(m_Data.m_uInt32) ;
        return tmp ;
    }
    return _hz_null_hzTime ;
}
const hzIpaddr  hzAtom::Ipaddr  (void) const
{
    //  Arguments:  None
    //  Returns:    Instance of hzIpaddr by value. This will be populated if the hzAtom has a value and the datatype is BASETYPE_IPADDR and be empty otherwise.
    hzIpaddr    tmp ;       //  Return value
    if (m_eStatus == ATOM_SET && m_eType == BASETYPE_IPADDR)
    {
        tmp = m_Data.m_uInt32 ;
        return tmp ;
    }
    return _hzGlobal_nullIP ;
}
/*
**  Functions to set hzAtom Values
*/
hzEcode hzAtom::SetValue    (hdbBasetype eType, const hzString& S)
{
    //  Set the atom to the supplied data type and value.
    //
    //  Arguments:  1)  eType   The datatype
    //              2)  s       The string that either is or contains the value
    //
    //  Returns:    E_TYPE      If the anticipated data type is not specified or conflicts with current type
    //              E_BADVALUE  If the supplied string does not represent a valid value for the anticipated data type.
    //              E_OK        If the operation was successful.
    _hzfunc("hzAtom::SetValue(hzString)") ;
    const char* j ;                 //  For processing string data
    uint64_t    x ;                 //  Storage for integer data
    hzXDate     xd ;                //  Storage for hzXDate data
    hzSDate     sd ;                //  Storage for hzSDate data
    hzTime      ti ;                //  Storage for hzTime data
    hzIpaddr    ipa ;               //  Storage for hzIpaddr data
    bool        bMinus = false ;    //  Negation indicator
    hzEcode     rc = E_BADVALUE ;   //  Return code
    //  Clear atom first
    Clear() ;
    //  If no value, just return
    if (!S)
        return E_OK ;
    m_eType = eType ;
    /*
    **  HadronZoo string-like types
    */
    if (m_eType == BASETYPE_DIGEST)
    {
        hzMD5   Md5 ;       //  Pointer to digest value
        Md5 = S ;
        if (!Md5.IsNull())
        {
            m_Data.m_pVoid = new uchar[sizeof(hzMD5)] ;
            memcpy(m_Data.m_pVoid, Md5.Value(), sizeof(hzMD5)) ;
            m_eStatus = ATOM_SET ;
            return E_OK ;
        }
        m_eType = BASETYPE_UNDEF ;
        m_eStatus = ATOM_ERROR ;
        return E_BADVALUE ;
    }
    if (m_eType == BASETYPE_STRING)
    {
        hzString    tmp ;
        tmp = S ; m_Data.m_uInt32 = tmp._int_addr() ; tmp._int_clr() ;
        m_eType = eType ;
        m_eStatus = ATOM_SET ;
        //m_bCast = 1 ;
        return E_OK ;
    }
    if (m_eType == BASETYPE_DOMAIN)
    {
        hzDomain    tmp ;
        tmp = S ;
        if (tmp)
        {
            m_Data.m_uInt32 = tmp._int_addr() ;
            tmp._inc_copy() ;
            m_eType = eType ;
            m_eStatus = ATOM_SET ;
            //m_bCast = 1 ;
            return E_OK ;
        }
        m_eType = BASETYPE_UNDEF ;
        m_eStatus = ATOM_ERROR ;
        return E_BADVALUE ;
    }
    if (m_eType == BASETYPE_EMADDR)
    {
        /*
        hzEmaddr*   pEma ;      //  Cast
        pEma = (hzEmaddr*) &m_Data ;
        *pEma = S ;
        if (*pEma)
        {
            m_eType = eType ;
            m_eStatus = ATOM_SET ;
            return E_OK ;
        }
        */
        hzEmaddr    tmp ;
        tmp = S ;
        if (tmp)
        {
            m_Data.m_uInt32 = tmp._int_addr() ;
            tmp._inc_copy() ;
            m_eType = eType ;
            m_eStatus = ATOM_SET ;
            //m_bCast = 1 ;
            return E_OK ;
        }
        m_eType = BASETYPE_UNDEF ;
        m_eStatus = ATOM_ERROR ;
        return E_BADVALUE ;
    }
    if (m_eType == BASETYPE_URL)
    {
        hzUrl   tmp ;       //  Temp URL
        tmp = S ;
        if (tmp)
        {
            m_Data.m_uInt32 = tmp._int_addr() ;
            //tmp._int_clr() ;
            tmp._inc_copy() ;
            m_eType = eType ;
            m_eStatus = ATOM_SET ;
            //m_bCast = 1 ;
            return E_OK ;
        }
        m_eType = BASETYPE_UNDEF ;
        m_eStatus = ATOM_ERROR ;
        return E_BADVALUE ;
    }
    /*
    **  HadronZoo types without smart pointers
    */
    if (m_eType == BASETYPE_IPADDR)
    {
        ipa = *S ;
        if (ipa)
            { m_eStatus = ATOM_SET ; m_Data.m_uInt32 = (uint32_t) ipa ; return E_OK ; }
        return E_BADVALUE ;
    }
    if (m_eType == BASETYPE_XDATE)
    {
        rc = xd.SetDateTime(S) ;
        if (rc == E_OK && xd.AsVal())
            { m_eStatus = ATOM_SET ; m_Data.m_uInt64 = xd.AsVal() ; return E_OK ; }
        m_eStatus = ATOM_ERROR ;
        return E_BADVALUE ;
    }
    if (m_eType == BASETYPE_SDATE)
    {
        rc = sd.SetDate(S) ;
        if (rc == E_OK)
            { m_eStatus = ATOM_SET ; m_Data.m_uInt32 = sd.NoDays() ; return E_OK ; }
        m_eStatus = ATOM_ERROR ;
        return E_BADVALUE ;
    }
    if (m_eType == BASETYPE_TIME)
    {
        rc = ti.SetTime(S) ;
        if (rc == E_OK)
            { m_eStatus = ATOM_SET ; m_Data.m_uInt32 = ti.NoSecs() ; return E_OK ; }
        m_eStatus = ATOM_ERROR ;
        return E_BADVALUE ;
    }
    /*
    **  Numeric types (no smart pointers)
    */
    if (m_eType == BASETYPE_DOUBLE)
    {
        if (IsDouble(m_Data.m_Double, *S))
            { m_eStatus = ATOM_SET ; return E_OK ; }
        m_eStatus = ATOM_ERROR ;
        return E_BADVALUE ;
    }
    if (m_eType == BASETYPE_BOOL)
    {
        if (S == "TRUE" || S == "true" || S == "yes" || S == "y" || S == "1")   { m_Data.m_Bool = true ; m_eStatus = ATOM_SET ; return E_OK ; }
        if (S == "FALSE" || S == "false" || S == "no" || S == "n" || S == "0")  { m_Data.m_Bool = false ; m_eStatus = ATOM_SET ; return E_OK ; }
        m_eStatus = ATOM_ERROR ;
        return E_BADVALUE ;
    }
    //  Must be number or number equiv
    j = *S ;
    if (*j == CHAR_MINUS)
        { j++ ; bMinus = true ; }
    for (x = 0 ; IsDigit(*j) ; j++)
        { x *= 10 ; x += (*j - '0') ; }
    if (*j)
        return E_BADVALUE ;
    switch  (m_eType)
    {
    case BASETYPE_BYTE:     if (x > 0x7f)
                                return E_BADVALUE ;
                            m_Data.m_sByte = x & 0x7f ;
                            if (bMinus)
                                m_Data.m_sByte *= -1 ;
                            break ;
    case BASETYPE_INT16:    if (x > 0x7fff)
                                return E_BADVALUE ;
                            m_Data.m_sInt16 = x & 0x7fff ;
                            if (bMinus)
                                m_Data.m_sInt16 *= -1 ;
                            break ;
    case BASETYPE_INT32:    if (x > 0x7fffffff)
                                return E_BADVALUE ;
                            m_Data.m_sInt32 = x & 0x7fffffff ;
                            if (bMinus)
                                m_Data.m_sInt32 *= -1 ;
                            break ;
    case BASETYPE_INT64:    m_Data.m_sInt64 = x ;
                            if (bMinus)
                                m_Data.m_sInt64 *= -1 ;
                            break ;
    case BASETYPE_UBYTE:    if (x & 0xffffffffffffff00)
                                return E_BADVALUE ;
                            m_Data.m_uByte = (x & 0xff) ;
                            break ;
    //case BASETYPE_ENUM2:
    case BASETYPE_UINT16:   if (x & 0xffffffffffff0000)
                                return E_BADVALUE ;
                            m_Data.m_uInt16 = (x & 0xffff) ;
                            break ;
    case BASETYPE_UINT32:   if (x & 0xffffffff00000000)
                                return E_BADVALUE ;
                            m_Data.m_uInt32 = (x & 0xffffffff) ;
                            break ;
    //case BASETYPE_UUID:
    case BASETYPE_UINT64:   m_Data.m_uInt64 = x ;
                            break ;
    }
    m_eStatus = ATOM_SET ;
    return E_OK ;
}
hzEcode hzAtom::SetValue    (hdbBasetype eType, const _atomval& av)
{
    //  Set the atom to the supplied data type and value (supplied in an _atomval)
    //
    //  Arguments:  1)  eType   The datatype
    //              2)  s       The string that either is or contains the value
    //
    //  Returns:    E_TYPE      If the anticipated data type is not specified or conflicts with current type
    //              E_BADVALUE  If the supplied string does not represent a valid value for the anticipated data type.
    //              E_OK        If the operation was successful.
    _hzfunc("hzAtom::SetValue(atomval)") ;
    //  If the current and supplied data type are the same and the current m_Data equal to the supplied _atomval, do nothing
    /*
    if (m_eType == eType && m_Data.m_uInt64 == av.m_uInt64)
        return E_OK ;
    */
    //  Clear the atom (in all cases)
    Clear() ;
    if (!av.m_uInt64)
        return E_OK ;
    m_eType = eType ;
    m_Data = av ;
    m_eStatus = ATOM_SET ;
    return E_OK ;
}
hzEcode hzAtom::SetNumber   (const char* s)
{
    //  Set atom to a numeric data type and value if the supplied string amounts to a numberic value, i.e. is of the form
    //  [sign] digits [[.] digits] [[e][sign]digits]
    //
    //  Arguments:  1)  s   The supplied string.
    //
    //  Returns:    E_NOINIT    If the anticipated data type is not specified.
    //              E_BADVALUE  If the supplied string does not represent a valid value for the anticipated data type.
    //              E_OK        If the operation was successful.
    _hzfunc("hzAtom::SetNumber") ;
    const char* i ;                 //  Iterator
    uint64_t    valA ;              //  For digits past the decimal point
    double      valD ;              //  For double value
    uint32_t    valB ;              //  For digits past the decimal point
    uint32_t    valE ;              //  For digits past the exponent
    uint32_t    nDigits = 0 ;       //  Digit counter
    int32_t     nBytes = 0 ;        //  Byte count
    bool        bNeg = false ;      //  Minus operator indicator
    Clear() ;
    i = s ;
    if (!i)
        return E_NODATA ;
    //  Deal with leading sign
    if (*i == CHAR_MINUS)
        { bNeg = true ; i++ ; }
    else if (*i == CHAR_PLUS)
        i++ ;
    else if (i[0] == '0' && (i[1] == 'x' || i[1] == 'X'))
    {
        //  Whole token must be hex and limited to 16 bytes - otherwise fail.
        for (i += 2 ; IsHex(*i) ; nBytes++, i++)
        {
            valA *= 16 ;
            if (*i >= '0' && *i <= '9') { valA += (*i - '0') ; continue ; }
            if (*i >= 'A' && *i <= 'F') { valA += 10 ; valA += (*i - 'A') ; continue ; }
            if (*i >= 'a' && *i <= 'f') { valA += 10 ; valA += (*i - 'a') ; continue ; }
            break ;
        }
        if (*i)
            { m_eType = BASETYPE_UNDEF ; return E_FORMAT ; }
        if (!nBytes || nBytes > 16)
            { m_eType = BASETYPE_UNDEF ; return E_FORMAT ; }
        if      (nBytes > 8)    { m_eType = BASETYPE_UINT64 ; m_Data.m_uInt64 = valA ; }
        else if (nBytes > 4)    { m_eType = BASETYPE_UINT32 ; m_Data.m_uInt32 = valA & 0xffffffff ; }
        else if (nBytes > 2)    { m_eType = BASETYPE_UINT16 ; m_Data.m_uInt16 = valA & 0xffff ; }
        else
            { m_eType = BASETYPE_UBYTE ; m_Data.m_uByte = valA & 0xff ; }
        return E_OK ;
    }
    //  Expect a series of at least one digit
    for (nDigits = 0 ; IsDigit(*i) ; nBytes++, nDigits++, i++)
    {
        valA *= 10 ;
        if (*i >= '0' && *i <= '9')
            { valA += (*i - '0') ; continue ; }
        break ;
    }
    if (!nDigits)
        return BASETYPE_UNDEF ;
    //  Test for a period that is followed by at least one digit
    if (*i == CHAR_PERIOD)
    {
        i++ ;
        for (nDigits = 0 ; IsDigit(*i) ; nBytes++, nDigits++, i++)
        {
            valD *= 10.0 ;
            if (*i >= '0' && *i <= '9')
                { valB += (*i - '0') ; continue ; }
            break ;
        }
        if (!nDigits)
            return BASETYPE_UNDEF ;
        for (; nBytes ; nBytes--)
            valD /= 10.0 ;
        valD += (double) valA ;
        //  Test for the 'e' followed by at least one digit or a +/- followed by at least one digit
        if (*i == 'e')
        {
            i++ ;
            if (*i == CHAR_MINUS || *i == CHAR_PLUS)
                { nBytes++ ; i++ ; }
            for (nDigits = 0 ; IsDigit(*i) ; nBytes++, nDigits++, i++)
            {
                valE *= 10 ;
                if (*i >= '0' && *i <= '9')
                    { valE += (*i - '0') ; continue ; }
                break ;
            }
            if (!nDigits)
                return BASETYPE_UNDEF ;
        }
        m_Data.m_Double = valD ;
        m_eType = BASETYPE_DOUBLE ;
        return E_OK ;
    }
    //  Not a double
    if (nDigits > 8)
    {
        if (bNeg)
            { m_Data.m_sInt64 = valA ; m_eType = BASETYPE_INT64 ; }
        else
            { m_Data.m_uInt64 = valA ; m_eType = BASETYPE_UINT64 ; }
    }
    else if (nDigits > 4)
    {
        if (bNeg)
            { m_Data.m_sInt32 = valA ; m_eType = BASETYPE_INT32 ; }
        else
            { m_Data.m_uInt32 = valA ; m_eType = BASETYPE_UINT32 ; }
    }
    else if (nDigits > 2)
    {
        if (bNeg)
            { m_Data.m_sInt16 = valA ; m_eType = BASETYPE_INT16 ; }
        else
            { m_Data.m_uInt16 = valA ; m_eType = BASETYPE_UINT16 ; }
    }
    else
    {
        if (bNeg)
            { m_Data.m_sByte = valA ; m_eType = BASETYPE_BYTE ; }
        else
            { m_Data.m_uByte = valA ; m_eType = BASETYPE_UBYTE ; }
    }
    m_eStatus = ATOM_SET ;
    return E_OK ;
}
hzAtom& hzAtom::operator=   (const hzAtom& op)
{
    //  Set atom type and value to that of another, operand atom.
    //
    //  Argument:   a   The operand atom
    //  Returns:    Reference to this atom
    _hzfunc("hzAtom::operator=(hzAtom)") ;
    Clear() ;
    if (op.m_eType == BASETYPE_DIGEST)
    {
        if (op.m_Data.m_pVoid)
        {
            m_Data.m_pVoid = new uchar[16] ;
            memcpy(m_Data.m_pVoid, op.m_Data.m_pVoid, sizeof(hzMD5)) ;
            m_eType = op.m_eType ;
            m_eStatus = op.m_eStatus ;
        }
        return *this ;
    }
    switch  (op.m_eType)
    {
    case BASETYPE_DOMAIN:   operator=(op.Domain()) ;    break ;
    case BASETYPE_EMADDR:   operator=(op.Emaddr()) ;    break ;
    case BASETYPE_URL:      operator=(op.Url()) ;       break ;
    default:
        m_eType = op.m_eType ;
        m_eStatus = op.m_eStatus ;
        //m_bCast = 0 ;
        m_Data = op.m_Data ;
    }
    return *this ;
}
hzAtom& hzAtom::operator=   (const hzMD5& md5)
{
    _hzfunc("hzAtom::operator=(hzMD5)") ;
    Clear() ;
    if (!md5.IsNull())
    {
        m_Data.m_pVoid = new uchar[sizeof(hzMD5)] ;
        memcpy(m_Data.m_pVoid, &md5, sizeof(hzMD5)) ;
        m_eType = BASETYPE_DIGEST ;
        m_eStatus = ATOM_SET ;
    }
    return *this ;
}
hzAtom& hzAtom::operator=   (const hzChain& Z)
{
    //  Set atom value to a chain.
    //
    //  Argument:   Z   The data as chain
    //  Returns:    Reference to this atom
    
    _hzfunc("hzAtom::operator=(hzChain)") ;
    hzChain     tmpCh ;     //  Temporary chain
    Clear() ;
    if (Z.Size())
    {
        tmpCh = Z ;
        memcpy(&m_Data, &tmpCh, 8) ;
        memset(&tmpCh, 0, 8) ;
        m_eType = BASETYPE_BINARY ;
        m_eStatus = ATOM_SET ;
        //m_bCast = 1 ;
    }
    return *this ;
}
hzAtom& hzAtom::operator=   (const hzString& S)
{
    //  Set atom value to a string.
    //
    //  Argument:   s   The data as string
    //  Returns:    Reference to this atom
    _hzfunc("hzAtom::operator=(hzString)") ;
    Clear() ;
    if (S)
    {
        S._inc_copy() ;
        m_Data.m_uInt32 = S._int_addr() ;
        m_eType = BASETYPE_STRING ;
        m_eStatus = ATOM_SET ;
        //m_bCast = 1 ;
    }
    return *this ;
}
hzAtom& hzAtom::operator=   (const hzDomain& dom)
{
    _hzfunc("hzAtom::operator=(hzDomain)") ;
    Clear() ;
    if (dom)
    {
        dom._inc_copy() ;
        m_Data.m_uInt32 = dom._int_addr() ;
        m_eType = BASETYPE_DOMAIN ;
        m_eStatus = ATOM_SET ;
        //m_bCast = 1 ;
    }
    return *this ;
}
hzAtom& hzAtom::operator=   (const hzEmaddr& ema)
{
    //  Set atom value to an email address.
    //
    //  Argument:   ema     The data as email address
    //  Returns:    Reference to this atom
    _hzfunc("hzAtom::operator=(hzEmaddr)") ;
    Clear() ;
    if (ema)
    {
        ema._inc_copy() ;
        m_Data.m_uInt32 = ema._int_addr() ;
        m_eType = BASETYPE_EMADDR ;
        m_eStatus = ATOM_SET ;
        //m_bCast = 1 ;
    }
    return *this ;
}
hzAtom& hzAtom::operator=   (const hzUrl& url)
{
    //  Set atom value to a URL
    //
    //  Argument:   url The data as URL
    //  Returns:    Reference to this atom
    _hzfunc("hzAtom::operator=(hzUrl)") ;
    Clear() ;
    if (url)
    {
        url._inc_copy() ;
        m_Data.m_uInt32 = url._int_addr() ;
        m_eType = BASETYPE_URL ;
        m_eStatus = ATOM_SET ;
        //m_bCast = 1 ;
    }
    return *this ;
}
hzAtom& hzAtom::operator=   (const hzIpaddr& ipa)
{
    //  Set atom value to an IP address.
    //
    //  Argument:   ipa     The data as IP address
    //  Returns:    Reference to this atom
    _hzfunc("hzAtom::operator=(hzIpaddr)") ;
    Clear() ;
    if (ipa)
        { memcpy(&m_Data, &ipa, sizeof(hzIpaddr)) ; m_eType = BASETYPE_IPADDR ; m_eStatus = ATOM_SET ; }
    return *this ;
}
hzAtom& hzAtom::operator=   (const hzXDate& xd)
{
    //  Set atom value to a full date.
    //
    //  Argument:   xd  The data as full date & time
    //  Returns:    Reference to this atom
    _hzfunc("hzAtom::operator=(hzXDate)") ;
    Clear() ;
    m_eType = BASETYPE_XDATE ;
    m_Data.m_uInt64 = xd.AsVal() ;
    if (m_Data.m_uInt64)
        m_eStatus = ATOM_SET ;
    return *this ;
}
hzAtom& hzAtom::operator=   (const hzSDate& sd)
{
    //  Set atom value to a short date.
    //
    //  Argument:   sd  The data as short form date
    //  Returns:    Reference to this atom
    _hzfunc("hzAtom::operator=(hzSDate)") ;
    Clear() ;
    m_eType = BASETYPE_SDATE ;
    if (!sd)
        { m_Data.m_sInt64 = 0 ; m_eStatus = ATOM_CLEAR ; }
    else
        { m_Data.m_uInt32 = sd.NoDays() ; m_eStatus = ATOM_SET ; }
    return *this ;
}
hzAtom& hzAtom::operator=   (const hzTime& time)
{
    //  Set atom value to a time. Note that this function will have no effect unless the atom has type of undefined or of
    //  BASETYPE_TIME
    //
    //  Argument:   tim Time of day
    //  Returns:    Reference to this atom
    _hzfunc("hzAtom::operator=(hzTime)") ;
    Clear() ;
    m_eType = BASETYPE_TIME ;
    if (!time)
        { m_Data.m_sInt64 = 0 ; m_eStatus = ATOM_CLEAR ; }
    else
        { m_Data.m_uInt32 = time.NoSecs() ; m_eStatus = ATOM_SET ; }
    return *this ;
}
hzAtom& hzAtom::operator=   (double val)
{
    //  If the current atom type is unknown or double, set the value
    //
    //  Argument:   val Numeric value (double)
    //  Returns:    Reference to this atom
    _hzfunc("hzAtom::operator=(double)") ;
    Clear() ;
    m_eType = BASETYPE_DOUBLE ;
    m_Data.m_Double = val ;
    m_eStatus = ATOM_SET ;
    return *this ;
}
hzAtom& hzAtom::operator=   (int64_t val)
{
    //  If the current atom type is unknown or int64_t, set the value
    //
    //  Argument:   val Numeric value (int64_t)
    //  Returns:    Reference to this atom
    _hzfunc("hzAtom::operator=(int64)") ;
    Clear() ;
    m_eType = BASETYPE_INT64 ;
    m_Data.m_sInt64 = val ;
    m_eStatus = ATOM_SET ;
    return *this ;
}
hzAtom& hzAtom::operator=   (uint64_t val)
{
    //  If the current atom type is unknown or unt64, set the value
    //
    //  Argument:   val Unsigned numeric value (uint64_t)
    //  Returns:    Reference to this atom
    _hzfunc("hzAtom::operator=(uint64)") ;
    Clear() ;
    m_eType = BASETYPE_UINT64 ;
    m_Data.m_uInt64 = val ;
    m_eStatus = ATOM_SET ;
    return *this ;
}
hzAtom& hzAtom::operator=   (int32_t val)
{
    //  If the current atom type is unknown or int32_t, set the value
    //
    //  Argument:   val The data as an int32_t
    //  Returns:    Reference to this atom
    _hzfunc("hzAtom::operator=(int32)") ;
    Clear() ;
    m_eType = BASETYPE_INT32 ;
    m_Data.m_sInt32 = val ;
    m_eStatus = ATOM_SET ;
    return *this ;
}
hzAtom& hzAtom::operator=   (uint32_t val)
{
    //  If the current atom type is unknown or uint32_t, set the value
    //
    //  Argument:   val The data as an uint32_t
    //  Returns:    Reference to this atom
    _hzfunc("hzAtom::operator=(uint32)") ;
    Clear() ;
    m_eType = BASETYPE_UINT32 ;
    m_Data.m_uInt32 = val ;
    m_eStatus = ATOM_SET ;
    return *this ;
}
hzAtom& hzAtom::operator=   (int16_t val)
{
    //  If the current atom type is unknown or int16_t, set the value
    //
    //  Argument:   val The data as an int16_t
    //  Returns:    Reference to this atom
    _hzfunc("hzAtom::operator=(int16)") ;
    Clear() ;
    m_eType = BASETYPE_INT16 ;
    m_Data.m_sInt16 = val ;
    m_eStatus = ATOM_SET ;
    return *this ;
}
hzAtom& hzAtom::operator=   (uint16_t val)
{
    //  If the current atom type is unknown or uint16_t, set the value
    //
    //  Argument:   val The data as an uint16_t
    //  Returns:    Reference to this atom
    _hzfunc("hzAtom::operator=(uint16)") ;
    Clear() ;
    m_eType = BASETYPE_UINT16 ;
    m_Data.m_uInt16 = val ;
    m_eStatus = ATOM_SET ;
    return *this ;
}
hzAtom& hzAtom::operator=   (char val)
{
    //  Set the atom value to the supplied char. The type of the atom will need to be either undefined or one of the numeric types.
    //
    //  Argument:   val The data as a single char
    //  Returns:    Reference to this atom
    _hzfunc("hzAtom::operator=(char)") ;
    Clear() ;
    m_eType = BASETYPE_BYTE ;
    m_Data.m_sByte = val ;
    m_eStatus = ATOM_SET ;
    return *this ;
}
hzAtom& hzAtom::operator=   (uchar val)
{
    //  Set the atom value to the supplied unsigned char. The type of the atom will need to be either undefined or one of the numeric types.
    //
    //  Argument:   val The data as a single unsigned char
    //  Returns:    Reference to this atom
    _hzfunc("hzAtom::operator=(uchar)") ;
    Clear() ;
    m_eType = BASETYPE_UBYTE ;
    m_Data.m_uByte = val ;
    m_eStatus = ATOM_SET ;
    return *this ;
}
hzAtom& hzAtom::operator=   (bool b)
{
    //  Set the atom value to the supplied boolean. The type of the atom will need to be either undefined or TYPE_BOOL
    //
    //  Argument:   b   The data as a boolean
    //  Returns:    Reference to this atom
    _hzfunc("hzAtom::operator=(bool)") ;
    Clear() ;
    m_eStatus = ATOM_SET ;
    m_eType = BASETYPE_BOOL ;
    m_Data.m_Bool = b ;
    return *this ;
}
hzAtom& hzAtom::Clear   (void)
{
    //  Clear the atom value to null. Note this does not set the type to undefined.
    //
    //  Arguments:  None
    //  Returns:    Reference to this atom
    _hzfunc("hzAtom::Clear") ;
    //  Check atom integrity
    if (m_eStatus == ATOM_CLEAR)
    {
        //  Must not have either data type or value
        if (m_eType == BASETYPE_UNDEF)
        {
            if (m_Data.m_uInt64)
                hzerr(E_CORRUPT, "Atom status clear, no data type but non-zero value") ;
            m_Data.m_uInt64 = 0 ;
            m_eStatus = ATOM_CLEAR ;
            m_eType = BASETYPE_UNDEF ;
        }
        else
        {
            if (m_Data.m_uInt64)
                hzerr(E_CORRUPT, "Atom status clear but has data type and value") ;
            else
                hzerr(E_CORRUPT, "Atom status clear, no value but type %s", Basetype2Txt(m_eType)) ;
            m_Data.m_uInt64 = 0 ;
            m_eStatus = ATOM_CLEAR ;
            m_eType = BASETYPE_UNDEF ;
        }
        //  Atom is clear so just return
        return *this ;
    }
    if (m_eStatus == ATOM_ERROR)
    {
        //  Must not have a type, cast, or a value
        if (m_eType != BASETYPE_UNDEF)  hzerr(E_CORRUPT, "Atom status error - but with data type %s", Basetype2Txt(m_eType)) ;
        if (m_Data.m_uInt64)            hzerr(E_CORRUPT, "Atom status error - but with value") ;
        m_Data.m_uInt64 = 0 ;
        m_eStatus = ATOM_CLEAR ;
        m_eType = BASETYPE_UNDEF ;
        return *this ;
    }
    if (m_eStatus == ATOM_SET)
    {
        //  Must have data type. Value could be 0 for numeric data types.
        if (m_eType == BASETYPE_UNDEF)
            hzexit(E_CORRUPT, "Atom status set but no data type") ;
    }
    else
    {
        //  Status is unknown
        hzexit(E_CORRUPT, "Atom status unknown") ;
    }
    //  To get this far the atom must be set, have data type and a legal value for the type
    if (m_eType == BASETYPE_DIGEST)
    {
        if (m_Data.m_pVoid)
            delete [] (uchar*) m_Data.m_pVoid ;
    }
    //if (m_bCast && m_Data.m_uInt32)
    if (m_Data.m_uInt32)
    {
        if (m_eType == BASETYPE_STRING)
        {
            hzString    tmp ;
            tmp._int_set(m_Data.m_uInt32) ;
            tmp.Clear() ;
        }
        if (m_eType == BASETYPE_DOMAIN)
        {
            hzDomain    tmp ;
            tmp._int_set(m_Data.m_uInt32) ;
            tmp.Clear() ;
        }
        if (m_eType == BASETYPE_EMADDR)
        {
            hzEmaddr    tmp ;
            tmp._int_set(m_Data.m_uInt32) ;
            tmp.Clear() ;
        }
        if (m_eType == BASETYPE_URL)
        {
            hzUrl   tmp ;
            tmp._int_set(m_Data.m_uInt32) ;
            tmp.Clear() ;
        }
    }
    m_Data.m_uInt64 = 0 ;
    m_eType = BASETYPE_UNDEF ;
    m_eStatus = ATOM_CLEAR ;
    //m_bCast = 0 ;
    return *this ;
}
std::ostream&   operator<<  (std::ostream& os, const hzAtom& obj)
{
    //  Category:   Data Output
    //
    //  Facilitates streaming (printing of value) of any atom.
    //
    //  Arguments:  1)  os  The output stream
    //              2)  obj The atom to write out
    //
    //  Returns:    Reference to the supplied output stream
    _hzfunc("hzAtom::operator<<") ;
    switch (obj.Type())
    {
    case BASETYPE_DOMAIN:   os << *obj.Domain() ;   break ;
    case BASETYPE_EMADDR:   os << *obj.Emaddr() ;   break ;
    case BASETYPE_URL:      os << *obj.Url() ;      break ;
    case BASETYPE_STRING:   os << *obj.Str() ;      break ;
    case BASETYPE_IPADDR:   os << *obj.Ipaddr() ;   break ;
    case BASETYPE_XDATE:    os << *obj.XDate() ;    break ;
    case BASETYPE_SDATE:    os << *obj.SDate() ;    break ;
    case BASETYPE_TIME:     os << *obj.Time() ;     break ;
    case BASETYPE_DOUBLE:   os << obj.Double() ;    break ;
    //  Kludge to fix no stream handling of _int64_t
    case BASETYPE_INT64:    os << FormalNumber(obj.Int64()) ;   break ;
    case BASETYPE_INT32:    os << FormalNumber(obj.Int32()) ;   break ;
    case BASETYPE_INT16:    os << obj.Int16() ; break ;
    case BASETYPE_BYTE:     os << obj.Byte() ;  break ;
    case BASETYPE_BOOL:     if (obj.Bool())
                                os << "true" ;
                            else
                                os << "false" ;
                            break ;
    case BASETYPE_UNDEF:    os << "Unknown type" ;
                            break ;
    }
    return os ;
}