//
//  File:   hdbADP.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.
//
//
//  Implimentation of the HadronZoo Proprietary Database Suite
//
#include <iostream>
#include <fstream>
#include <cstdio>
#include <unistd.h>
#include <stdlib.h>
#include <stdarg.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <netdb.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/un.h>
#include "hzBasedefs.h"
#include "hzString.h"
#include "hzChars.h"
#include "hzChain.h"
#include "hzDate.h"
#include "hzTextproc.h"
#include "hzCodec.h"
#include "hzDocument.h"
#include "hzDirectory.h"
#include "hzDatabase.h"
#include "hzProcess.h"
#include "hzIpServer.h"
using namespace std ;
/*
**  hdbADP Functions
*/
hdbADP::hdbADP  (void)
{
    m_pClassSubscriber = 0 ;
    m_pMbr_Subscriber_username = 0 ;
    m_pMbr_Subscriber_userpass = 0 ;
    m_pMbr_Subscriber_email = 0 ;
    m_pMbr_Subscriber_UID = 0 ;
    m_pReposSubscriber = 0 ;
    m_pSiteindex = 0 ;
    //m_nsqClsSys = 1 ;         //  System data classes have IDs starting at 1
    m_nsqClsUsr = HZ_ADP_CLS_RNG_USER ;
    m_nsqClsCfg = HZ_ADP_CLS_RNG_APPL ;
    m_nsqClsCtx = HZ_ADP_CLS_RNG_CONTEXT ;
    m_nsqMbrSys = HZ_ADP_MBR_RNG_SYSTEM ;
    m_nsqMbrUsr = HZ_ADP_MBR_RNG_USER ;
    m_nsqMbrCfg = HZ_ADP_MBR_RNG_APPL ;
    //  Delta Server Params
    m_unixSock = -1 ;
    m_nDeltaID = 0 ;
    m_eDeltaMode = DELTA_MODE_NULL ;
}
hzEcode hdbADP::ExportADP   (void)
{
    //  Export the Application Delta Profile (ADP)
    //
    //  The current ADP for any application using the HadronZoo Database Suite (HDB), will be in the standard location "/home/deltasvr/appname/appname.adps".
    _hzfunc("hdbADP::Export") ;
    hzMapS  <uint32_t,const hdbClass*>      classById ; //  For ordering classes by ID
    hzMapS  <uint32_t,const hdbObjRepos*>   reposById ; //  For ordering classes by ID
    ofstream            os ;            //  Output stream
    ifstream            is ;            //  Input stream for previous ADP if applicable
    hzChain             Z ;             //  For building the ADP
    hzChain             Y ;             //  Previous ADP
    const hdbEnum*      pEnum ;         //  Data enum
    const hdbClass*     pClass ;        //  Data class
    const hdbObjRepos*  pRepos ;        //  Data object repository
    hzString            dsDir ;         //  Delta server directory
    hzString            fname ;         //  Filename
    hzString            bkfile ;        //  Filename
    uint32_t            nC ;            //  Data class iterator
    hzEcode             rc = E_OK ;     //  Return code
    dsDir = "/home/deltasvr/" + m_appName ;
    rc = AssertDir(dsDir, 0777) ;
    if (rc != E_OK)
        return hzerr(rc, "Could not assert delta dir [%s]\n", *dsDir) ;
    fname = dsDir + "/" + m_appName + ".adp" ;
    bkfile = dsDir + "/" + m_appName + ".bak" ;
    threadLog("Exporting ADP to %s\n", *fname) ;
    if (TestFile(*fname) == E_OK)
    {
        is.open(*fname) ;
        Y << is ;
        is.close() ;
    }
    Z.Printf("<AppDeltaProfile app=\"%s\">\n", *m_appName) ;
    //  List data enums
    for (nC = 0 ; nC < m_mapEnums.Count() ; nC++)
    {
        pEnum = m_mapEnums.GetObj(nC) ;
        Z.Printf("\t<enum name=\"%s\"/>\n", pEnum->txtType()) ;
    }
    //  List data classes
    for (nC = 0 ; nC < m_mapClasses.Count() ; nC++)
    {
        pClass = m_mapClasses.GetObj(nC) ;
        classById.Insert(pClass->ClassId(), pClass) ;
    }
    for (nC = 0 ; nC < classById.Count() ; nC++)
    {
        pClass = classById.GetObj(nC) ;
        pClass->DescClass(Z, 1) ;
    }
    //  List data object repositories
    for (nC = 0 ; nC < m_mapObjRepos.Count() ; nC++)
    {
        pRepos = m_mapObjRepos.GetObj(nC) ;
        reposById.Insert(pRepos->DeltaId(), pRepos) ;
    }
    for (nC = 0 ; nC < reposById.Count() ; nC++)
    {
        pRepos = reposById.GetObj(nC) ;
        pRepos->DescRepos(Z, 1) ;
    }
    Z << "</AppDeltaProfile>\n" ;
    if (Y.Size())
    {
        if (Y == Z)
            { threadLog("ADP is an exact match\n") ; return E_OK ; }
        threadLog("Backing up ADP\n") ;
        Filecopy(*bkfile, *fname) ;
    }
    threadLog("Exporting current ADP\n") ;
    os.open(*fname) ;
    os << Z ;
    os.close() ;
    return rc ;
}
hzEcode hdbADP::_rdClass    (hzXmlNode* pN)
{
    //  Read a <class> tag on behalf of the hdbADProfile::Import function.
    _hzfunc("hdbADProfile::_readClass") ;
    const hdbDatatype*  pType ;     //  Data type
    hdbClass*       pClass ;        //  Data class
    hzXmlNode*      pN2 ;           //  Second level node
    hzAttrset       ai ;            //  Attribute iterator
    hzString        cname ;         //  Class name
    hzString        desig ;         //  Class designation
    hzString        str_id ;        //  Member id
    hzString        str_uid ;       //  Member uid
    hzString        str_pop ;       //  Member population control as string
    hzString        str_typ ;       //  Member data type
    hzString        str_sub ;       //  Member sub class
    hzString        str_nam ;       //  Member name
    hdbPopCtl       popCtl ;        //  Member population control as enum
    hzEcode         rc = E_OK ;     //  Return code
    if (!pN->NameEQ("class"))
        return E_CONFIG ;
    str_id = cname = (char*) 0 ;
    for (ai = pN ; ai.Valid() ; ai.Advance())
    {
        if      (ai.NameEQ("id"))       str_id = ai.Value() ;
        else if (ai.NameEQ("desig"))    desig = ai.Value() ;
        else if (ai.NameEQ("name"))     cname = ai.Value() ;
        else
            { rc = E_CONFIG ; threadLog("Line %d: <class> bad param %s=%s\n", pN->Line(), ai.Name(), ai.Value()) ; break ; }
    }
    if (!cname) return hzerr(E_ARGUMENT, "No class name supplied") ;
    if (!desig) return hzerr(E_ARGUMENT, "No class designation supplied") ;
    if      (desig == "sys")    pClass = new hdbClass(*this, HDB_CLASS_DESIG_SYS) ;
    else if (desig == "usr")    pClass = new hdbClass(*this, HDB_CLASS_DESIG_USR) ;
    else if (desig == "cfg")    pClass = new hdbClass(*this, HDB_CLASS_DESIG_CFG) ;
    else
        return hzerr(E_ARGUMENT, "Invalid class designation supplied (%s). Must be sys|usr|cfg") ;
    pClass->InitStart(cname) ;
    threadLog("Reading class %s\n", *cname) ;
    m_mapDatatypes.Insert(cname, pClass) ;
    m_mapClasses.Insert(cname, pClass) ;
    //  Read in member parameters
    for (pN2 = pN->GetFirstChild() ; pN2 ; pN2 = pN2->Sibling())
    {
        if (!pN2->NameEQ("member"))
            { rc = E_CONFIG ; threadLog("Line %d: <class> only <member> allowed. %s unexpected\n", pN2->Line(), pN2->txtName()) ; break ; }
        str_id = str_uid = str_pop = str_typ = str_sub = str_nam = (char*) 0 ;
        for (ai = pN2 ; ai.Valid() ; ai.Advance())
        {
            if      (ai.NameEQ("posn"))     str_id = ai.Value() ;
            else if (ai.NameEQ("uid"))      str_uid = ai.Value() ;
            else if (ai.NameEQ("pop"))      str_pop = ai.Value() ;
            else if (ai.NameEQ("datatype")) str_typ = ai.Value() ;
            else if (ai.NameEQ("subclass")) str_sub = ai.Value() ;
            else if (ai.NameEQ("name"))     str_nam = ai.Value() ;
            else
                { rc = E_CONFIG ; threadLog("Line %d: <member> bad param %s=%s\n", pN2->Line(), ai.Name(), ai.Value()) ; break ; }
        }
        if (!str_typ)
            str_typ = str_sub ;
        pType = m_mapDatatypes[str_typ] ;
        if (!pType)
            { rc = E_NOTFOUND ; threadLog("Line %d: <member> No such data type as %s\n", pN2->Line(), *str_typ) ; break ; }
        if      (str_pop == "SingleOptional")   popCtl = HDB_MBR_POP_SINGLE_OPTIONAL ;
        else if (str_pop == "SingleCompulsory") popCtl = HDB_MBR_POP_SINGLE_COMPULSORY ;
        else if (str_pop == "ArrayOptional")    popCtl = HDB_MBR_POP_SINGLE_COMPULSORY ;
        else if (str_pop == "ArrayCompulsory")  popCtl = HDB_MBR_POP_SINGLE_COMPULSORY ;
        else
        {
            rc = E_CONFIG ; 
            threadLog("Line %d: Population control must be either SingleOptional, SingleCompulsory, ArrayOptional or ArrayCompulsory. %s not accepted\n", *str_pop) ;
        }
        pClass->InitMember(str_nam, pType, popCtl) ;
    }
    pClass->InitDone() ;
    return rc ;
}
hzEcode hdbADP::ImportADP   (const hzString& appName)
{
    //  Import the Application Delta Profile (ADP), of the named application.
    //
    //  The current ADP for any application using the HadronZoo Database Suite (HDB), will be in the standard location "/home/deltasvr/appname/appname.adp".
    _hzfunc("hdbADP::Import") ;
    hzArray <hzString>  ar ;        //  Enum values
    ifstream        is ;            //  Input stream for previous ADP if applicable
    hzDocXml        docADP ;        //  XML document
    hzChain         Z ;             //  For building the ADP
    hzChain         Y ;             //  Previous ADP
    hzXmlNode*      pRoot ;         //  Document root node
    hzXmlNode*      pN ;            //  First level node
    hzAttrset       ai ;            //  Attribute iterator
    hdbEnum*        pEnum ;         //  Data enum
    hzString        fname ;         //  Filename
    hzString        bkfile ;        //  Filename
    hzString        cname ;         //  Class name
    hzString        str_id ;        //  Member id
    hzString        str_uid ;       //  Member uid
    hzString        str_min ;       //  Member minPop
    hzString        str_max ;       //  Member minPop
    hzString        str_typ ;       //  Member data type
    hzString        str_sub ;       //  Member sub class
    hzString        str_nam ;       //  Member name
    hzString        S ;             //  Temp string (enum values)
    uint32_t        n ;             //  Enum value iterator
    hzEcode         rc = E_OK ;     //  Return code
    fname = "/home/deltasvr/" + appName + "/" + appName + ".adp" ;
    if (TestFile(*fname) == E_OK)
    {
        is.open(*fname) ;
        Y << is ;
        is.close() ;
    }
    rc = docADP.Load(*fname) ;
    if (rc != E_OK)
    {
        threadLog("Could not load ADP document (%s)\n", *fname) ;
        threadLog(docADP.Error()) ;
        return rc ;
    }
    pRoot = docADP.GetRoot() ;
    if (!pRoot)
    {
        threadLog("ADP document (%s) has no route\n", *fname) ;
        return E_NOINIT ;
    }
    for (pN = pRoot->GetFirstChild() ; pN ; pN = pN->Sibling())
    {
        if (pN->NameEQ("enum"))
        {
            for (ai = pN ; ai.Valid() ; ai.Advance())
            {
                if (ai.NameEQ("name"))
                    str_nam = ai.Value() ;
                else
                    { rc = E_CONFIG ; threadLog("Line %d: <enum> bad param %s=%s\n", pN->Line(), ai.Name(), ai.Value()) ; break ; }
            }
            pEnum = new hdbEnum() ;
            pEnum->SetTypename(str_nam) ;
            //  Add to data types
            m_mapDatatypes.Insert(str_nam, pEnum) ;
            m_mapEnums.Insert(str_nam, pEnum) ;
            SplitCSV(ar, pN->m_fixContent) ;
            for (n = 0 ; n < ar.Count() ; n++)
            {
                S = ar[n] ;
                //  if (S.Length() > pEnum->m_nMax)
                //      pEnum->m_nMax = S.Length() ;
                //pEnum->m_Numbers.Add(strNo) ;
                pEnum->AddItem(S) ;
            }
        }
        else if (pN->NameEQ("class"))
        {
            rc = _rdClass(pN) ;
            if (rc != E_OK)
                break ;
        }
    }
    return rc ;
}
hzEcode hdbADP::InitStandard    (const hzString& appName)
{
    //  Add the fundamental C++ and the HadronZoo in-built data types to the global map of datatypes _hzGlobal_Datatypes.
    //
    //  Arguments:  None
    //
    //  Returns:    E_SEQUENCE  If this function has already been called
    //              E_OK        Otherwise.
    _hzfunc("hdbADP::InitStandard") ;
    hdbCpptype*     ct ;    //  Pointer to Cpp data type
    hdbHzotype*     ht ;    //  Pointer to Cpp data type
    if (!this)
        Fatal("No ADP Instance") ;
    if (m_appName)
        return hzerr(E_DUPLICATE, "This function has already been called setting app name to %s", *m_appName) ;
    if (!appName)
        return hzerr(E_ARGUMENT, "No application name supplied") ;
    m_appName = appName ;
    //  Group 1:    C++ Fundamental types
    datatype_DIGEST = ct = new hdbCpptype() ;   ct->SetTypename("hashMD5") ;    ct->SetBasetype(BASETYPE_DIGEST) ;  m_mapDatatypes.Insert(ct->strType(), ct) ;
    datatype_DOUBLE = ct = new hdbCpptype() ;   ct->SetTypename("double") ;     ct->SetBasetype(BASETYPE_DOUBLE) ;  m_mapDatatypes.Insert(ct->strType(), ct) ;
    datatype_INT64  = ct = new hdbCpptype() ;   ct->SetTypename("int64") ;      ct->SetBasetype(BASETYPE_INT64) ;   m_mapDatatypes.Insert(ct->strType(), ct) ;
    datatype_INT32  = ct = new hdbCpptype() ;   ct->SetTypename("int32") ;      ct->SetBasetype(BASETYPE_INT32) ;   m_mapDatatypes.Insert(ct->strType(), ct) ;
    datatype_INT16  = ct = new hdbCpptype() ;   ct->SetTypename("int16") ;      ct->SetBasetype(BASETYPE_INT16) ;   m_mapDatatypes.Insert(ct->strType(), ct) ;
    datatype_BYTE   = ct = new hdbCpptype() ;   ct->SetTypename("byte") ;       ct->SetBasetype(BASETYPE_BYTE) ;    m_mapDatatypes.Insert(ct->strType(), ct) ;
    datatype_UINT64 = ct = new hdbCpptype() ;   ct->SetTypename("uint64") ;     ct->SetBasetype(BASETYPE_UINT64) ;  m_mapDatatypes.Insert(ct->strType(), ct) ;
    datatype_UINT32 = ct = new hdbCpptype() ;   ct->SetTypename("uint32") ;     ct->SetBasetype(BASETYPE_UINT32) ;  m_mapDatatypes.Insert(ct->strType(), ct) ;
    datatype_UINT16 = ct = new hdbCpptype() ;   ct->SetTypename("uint16") ;     ct->SetBasetype(BASETYPE_UINT16) ;  m_mapDatatypes.Insert(ct->strType(), ct) ;
    datatype_UBYTE  = ct = new hdbCpptype() ;   ct->SetTypename("ubyte") ;      ct->SetBasetype(BASETYPE_UBYTE) ;   m_mapDatatypes.Insert(ct->strType(), ct) ;
    datatype_BOOL   = ct = new hdbCpptype() ;   ct->SetTypename("bool") ;       ct->SetBasetype(BASETYPE_BOOL) ;    m_mapDatatypes.Insert(ct->strType(), ct) ;
    //  Group 2:    HadronZoo in-built
    datatype_DOMAIN = ht = new hdbHzotype() ;   ht->SetTypename("domain") ;     ht->SetBasetype(BASETYPE_DOMAIN) ;  m_mapDatatypes.Insert(ht->strType(), ht) ;
    datatype_EMADDR = ht = new hdbHzotype() ;   ht->SetTypename("emaddr") ;     ht->SetBasetype(BASETYPE_EMADDR) ;  m_mapDatatypes.Insert(ht->strType(), ht) ;
    datatype_URL    = ht = new hdbHzotype() ;   ht->SetTypename("url") ;        ht->SetBasetype(BASETYPE_URL) ;     m_mapDatatypes.Insert(ht->strType(), ht) ;
    datatype_IPADDR = ht = new hdbHzotype() ;   ht->SetTypename("ipaddr") ;     ht->SetBasetype(BASETYPE_IPADDR) ;  m_mapDatatypes.Insert(ht->strType(), ht) ;
    datatype_TIME   = ht = new hdbHzotype() ;   ht->SetTypename("time") ;       ht->SetBasetype(BASETYPE_TIME) ;    m_mapDatatypes.Insert(ht->strType(), ht) ;
    datatype_SDATE  = ht = new hdbHzotype() ;   ht->SetTypename("sdate") ;      ht->SetBasetype(BASETYPE_SDATE) ;   m_mapDatatypes.Insert(ht->strType(), ht) ;
    datatype_XDATE  = ht = new hdbHzotype() ;   ht->SetTypename("xdate") ;      ht->SetBasetype(BASETYPE_XDATE) ;   m_mapDatatypes.Insert(ht->strType(), ht) ;
    datatype_STRING = ht = new hdbHzotype() ;   ht->SetTypename("string") ;     ht->SetBasetype(BASETYPE_STRING) ;  m_mapDatatypes.Insert(ht->strType(), ht) ;
    datatype_TEXT   = ht = new hdbHzotype() ;   ht->SetTypename("text") ;       ht->SetBasetype(BASETYPE_TEXT) ;    m_mapDatatypes.Insert(ht->strType(), ht) ;
    datatype_BINARY = ht = new hdbHzotype() ;   ht->SetTypename("binary") ;     ht->SetBasetype(BASETYPE_BINARY) ;  m_mapDatatypes.Insert(ht->strType(), ht) ;
    datatype_TXTDOC = ht = new hdbHzotype() ;   ht->SetTypename("txtdoc") ;     ht->SetBasetype(BASETYPE_TXTDOC) ;  m_mapDatatypes.Insert(ht->strType(), ht) ;
    return E_OK ;
}
hzEcode hdbADP::InitSubscribers (const hzString& dataDir)
{
    //  Create the subscriber data class and repository
    _hzfunc("hdbADP::InitSubscribers") ;
    //hdbObjRepos*  pRepos ;        //  Subscriber repository pointer
    hzString        S ;             //  Temporary string
    hzEcode         rc = E_OK ;     //  Return code
    if (!this)
        hzexit(E_CORRUPT, "No ADP instance") ;
    if (m_pClassSubscriber)
        rc = hzerr(E_DUPLICATE, "Subscriber class already declared") ;
    S = "subscriber" ;
    if (m_mapObjRepos.Exists(S))
        rc = hzerr(E_DUPLICATE, "This function has already been called") ;
    if (!dataDir)
        rc = hzerr(E_ARGUMENT, "No application data directory") ;
    if (rc != E_OK)
        return rc ;
    //  Allocate and initialize subscriber class
    m_pClassSubscriber = new hdbClass(*this, HDB_CLASS_DESIG_SYS) ;
    rc = m_pClassSubscriber->InitStart(S) ;
    if (rc == E_OK) { S = "username" ;  rc = m_pClassSubscriber->InitMember(S, datatype_STRING, HDB_MBR_POP_SINGLE_COMPULSORY) ; }
    if (rc == E_OK) { S = "userpass" ;  rc = m_pClassSubscriber->InitMember(S, datatype_STRING, HDB_MBR_POP_SINGLE_COMPULSORY) ; }
    if (rc == E_OK) { S = "userEmail" ; rc = m_pClassSubscriber->InitMember(S, datatype_EMADDR, HDB_MBR_POP_SINGLE_OPTIONAL) ; }
    if (rc == E_OK) { S = "userUID" ;   rc = m_pClassSubscriber->InitMember(S, datatype_UINT32, HDB_MBR_POP_SINGLE_OPTIONAL) ; }
    if (rc == E_OK) { S = "userType" ;  rc = m_pClassSubscriber->InitMember(S, datatype_STRING, HDB_MBR_POP_SINGLE_OPTIONAL) ; }
    if (rc == E_OK)
    {
        rc = m_pClassSubscriber->InitDone() ;
        //  Obtain member pointers
        m_pMbr_Subscriber_username = m_pClassSubscriber->GetMember(0) ;
        m_pMbr_Subscriber_userpass = m_pClassSubscriber->GetMember(1) ;
        m_pMbr_Subscriber_email = m_pClassSubscriber->GetMember(2) ;
        m_pMbr_Subscriber_UID = m_pClassSubscriber->GetMember(3) ;
        m_pMbr_Subscriber_type = m_pClassSubscriber->GetMember(4) ;
    }
    //  Resister subscriber class
    if (rc == E_OK)
        rc = RegisterDataClass(m_pClassSubscriber) ;
    if (rc != E_OK)
        hzexit(rc, "Could not init subsriber class") ;
    //  Create and initialize susscriber repository
    m_pReposSubscriber = new hdbObjRepos(*this) ;
    if (!m_pReposSubscriber)
        hzexit(E_MEMORY, "No subsciber cache allocated") ;
    rc = m_pReposSubscriber->InitStart(m_pClassSubscriber, m_pClassSubscriber->strName(), dataDir, HDB_REPOS_CACHE) ;
    if (rc != E_OK)
        hzexit(rc, "Could not init subsriber repos") ;
    //S = "username" ;
    rc = m_pReposSubscriber->InitMbrIndex(m_pMbr_Subscriber_username, true) ;
    if (rc != E_OK)
        hzexit(rc, "Could not init subsriber repos index") ;
    rc = m_pReposSubscriber->InitDone() ;
    if (rc != E_OK)
        hzexit(rc, "Could not complete subsriber repos initialization") ;
    //  Resister subscriber repository
    rc = m_mapObjRepos.Insert(m_pReposSubscriber->strName(), m_pReposSubscriber) ;
    /*
    if (rc == E_OK)
    {
        m_pMbr_Subscriber_username = m_pClassSubscriber->GetMember(0) ;
        m_pMbr_Subscriber_userpass = m_pClassSubscriber->GetMember(1) ;
        m_pMbr_Subscriber_email = m_pClassSubscriber->GetMember(2) ;
        m_pMbr_Subscriber_UID = m_pClassSubscriber->GetMember(3) ;
        m_pMbr_Subscriber_type = m_pClassSubscriber->GetMember(4) ;
    }
    */
    return rc ;
}
hzEcode hdbADP::InitSiteIndex   (const hzString& dataDir)
{
    //  The site index is an optional free text index for webapps that comes as standard. If deployed, it maps each word found within webapp page and article content, to the set of
    //  pages and articles in which the word appears. It is implemented directly as a hdbIndexText instance, without involving a data class or a data class repository.
    _hzfunc("hdbADP::InitSiteIndex") ;
    hdbClass*       pClass ;        //  Subscriber class pointer
    hdbObjRepos*    pRepos ;        //  Subscriber repository pointer
    hzString        S ;             //  Temporary string
    hzEcode         rc ;            //  Return code
    if (!this)          hzexit(E_CORRUPT, "No ADP instance") ;
    if (m_pSiteindex)   return hzerr(E_SEQUENCE, "This function has already been called") ;
    if (!dataDir)       return hzerr(E_ARGUMENT, "No application data directory") ;
    S = "siteindex" ;
    m_pSiteindex = new hdbIndexText() ;
    //  Allocate and initialize subscriber class
    pClass = new hdbClass(*this, HDB_CLASS_DESIG_SYS) ;
    rc = pClass->InitStart(S) ;
    if (rc == E_OK) { S = "pageUrl" ;   rc = pClass->InitMember(S, datatype_STRING, HDB_MBR_POP_SINGLE_COMPULSORY) ; }
    if (rc == E_OK) { S = "PageTitle" ; rc = pClass->InitMember(S, datatype_STRING, HDB_MBR_POP_SINGLE_COMPULSORY) ; }
    if (rc == E_OK)
    {
        rc = pClass->InitDone() ;
    }
    //  Resister subscriber class
    if (rc == E_OK)
        rc = RegisterDataClass(pClass) ;
    if (rc != E_OK)
        hzexit(rc, "Could not init subsriber class") ;
    //  Create and initialize susscriber repository
    pRepos = new hdbObjRepos(*this) ;
    if (!pRepos)
        hzexit(E_MEMORY, "No subsciber cache allocated") ;
    rc = pRepos->InitStart(pClass, pClass->strType(), dataDir, HDB_REPOS_CACHE) ;
    if (rc != E_OK)
        hzexit(rc, "Could not init subsriber repos") ;
    S = "pageUrl" ;
    rc = pRepos->InitMbrIndex(S, true) ;
    if (rc != E_OK)
        hzexit(rc, "Could not init subsriber repos index") ;
    rc = pRepos->InitDone() ;
    if (rc != E_OK)
        hzexit(rc, "Could not complete subsriber repos initialization") ;
    threadLog("Complete subsriber repos initialization") ;
    //  Resister subscriber repository
    rc = m_mapObjRepos.Insert(pRepos->strName(), pRepos) ;
    threadLog("Subscriber Repos Reg complete\n") ;
    return rc ;
}
#if 0
hzEcode hdbADP::InitSearch  (void)
{
    //  Create the search data class
    _hzfunc("hdbADP::InitSearch") ;
    static  bool    bBeenHere = false ;     //  Set once run
    hdbClass*       pClass ;        //  Subscriber class pointer
    hzString        S ;             //  Temporary string
    hzEcode         rc ;            //  Return code
    if (bBeenHere)
        return hzerr(E_SEQUENCE, "This function has already been called") ;
    bBeenHere = true ;
    //  Allocate and initialize subscriber class
    S = "sitesrch" ;
    pClass = new hdbClass(*this, HDB_CLASS_DESIG_SYS) ;
    rc = pClass->InitStart(S) ;
    if (rc == E_OK)
        { S = "criteria" ;  rc = pClass->InitMember(S, datatype_STRING, 1, 1) ; }
    if (rc == E_OK)
        rc = pClass->InitDone() ;
    //  Resister subscriber class
    if (rc == E_OK)
        rc = RegisterDataClass(pClass) ;
    if (rc != E_OK)
        hzexit(rc, "Could not init sitesrch class") ;
    return E_OK ;
}
#endif
hzEcode hdbADP::InitFinancials  (const hzString& dataDir)
{
    _hzfunc("hdbADP::InitFinancials") ;
    hdbEnum*        pEnum ;     //  Enum pointer
    hdbClass*       pClass ;    //  Data class pointer
    hdbObjRepos*    pRepos ;    //  Repository pointer
    hzString        cname ;     //  Current data class name
    hzString        mname ;     //  Current member name
    hzEcode         rc ;        //  Return code
    cname = "Currency" ;
    if (m_mapObjRepos.Exists(cname))
        return hzerr(E_SEQUENCE, "This function has already been called") ;
    if (!dataDir)
        return hzerr(E_ARGUMENT, "No application data directory") ;
    //  Setup the 'Account Type' enum
    mname = "enumAccType" ;
    pEnum = new hdbEnum() ;
    pEnum->SetTypename(mname) ;
    pEnum->AddItem("ACC_NULL",      0x0000) ;
    pEnum->AddItem("ACC_ASSET",     0x0001) ;
    pEnum->AddItem("ACC_BANK",      0x0002) ;
    pEnum->AddItem("ACC_CASH",      0x0004) ;
    pEnum->AddItem("ACC_DIRECTOR",  0x0008) ;
    pEnum->AddItem("ACC_FOREX",     0x0010) ;
    pEnum->AddItem("ACC_GOV",       0x0020) ;
    pEnum->AddItem("ACC_SHARE",     0x0040) ;
    pEnum->AddItem("ACC_STOCK",     0x0080) ;
    pEnum->AddItem("ACC_TRADE",     0x0100) ;
    RegisterDataEnum(pEnum) ;
    /*
    **  Currencies
    */
    //  Set up Currency data class
    pClass = new hdbClass(*this, HDB_CLASS_DESIG_SYS) ;
    rc = pClass->InitStart(cname) ;
    if (rc == E_OK) { mname = "Name" ;      rc = pClass->InitMember(mname, "string", HDB_MBR_POP_SINGLE_COMPULSORY) ; }
    if (rc == E_OK) { mname = "Symbol" ;    rc = pClass->InitMember(mname, "string", HDB_MBR_POP_SINGLE_COMPULSORY) ; }
    rc = pClass->InitDone() ;
    if (rc == E_OK)
        rc = RegisterDataClass(pClass) ;
    //  Create and initialize Currency repository
    pRepos = new hdbObjRepos(*this) ;
    if (!pRepos)
        hzexit(E_MEMORY, "No Currency cache allocated") ;
    rc = pRepos->InitStart(pClass, pClass->strType(), dataDir, HDB_REPOS_CACHE) ;
    if (rc != E_OK)
        hzexit(rc, "Could not init subsriber repos") ;
    mname = "Name" ;
    rc = pRepos->InitMbrIndex(mname, true) ;
    if (rc != E_OK)
        hzexit(rc, "Could not init subsriber repos index") ;
    rc = pRepos->InitDone() ;
    if (rc != E_OK)
        hzexit(rc, "Could not complete subsriber repos initialization") ;
    rc = m_mapObjRepos.Insert(pRepos->strName(), pRepos) ;
    pRepos->Open() ;
    /*
    **  Categories
    */
    //  Set up Currency data class
    cname = "Category" ;
    pClass = new hdbClass(*this, HDB_CLASS_DESIG_SYS) ;
    rc = pClass->InitStart(cname) ;
    if (rc == E_OK) { mname = "Code" ;  rc = pClass->InitMember(mname, "string", HDB_MBR_POP_SINGLE_COMPULSORY) ; }
    if (rc == E_OK) { mname = "Desc" ;  rc = pClass->InitMember(mname, "string", HDB_MBR_POP_SINGLE_COMPULSORY) ; }
    rc = pClass->InitDone() ;
    if (rc == E_OK)
        rc = RegisterDataClass(pClass) ;
    //  Create and initialize Currency repository
    pRepos = new hdbObjRepos(*this) ;
    if (!pRepos)
        hzexit(E_MEMORY, "No Currency cache allocated") ;
    rc = pRepos->InitStart(pClass, pClass->strType(), dataDir, HDB_REPOS_CACHE) ;
    if (rc != E_OK)
        hzexit(rc, "Could not init subsriber repos") ;
    mname = "Code" ;
    rc = pRepos->InitMbrIndex(mname, true) ;
    if (rc != E_OK)
        hzexit(rc, "Could not init subsriber repos index") ;
    rc = pRepos->InitDone() ;
    if (rc != E_OK)
        hzexit(rc, "Could not complete subsriber repos initialization") ;
    rc = m_mapObjRepos.Insert(pRepos->strName(), pRepos) ;
    pRepos->Open() ;
    /*
    **  Accounts
    */
    //  Set up Account data class
    cname = "Account" ;
    pClass = new hdbClass(*this, HDB_CLASS_DESIG_SYS) ;
    rc = pClass->InitStart(cname) ;
    if (rc == E_OK) { mname = "Opened" ;    rc = pClass->InitMember(mname, "sdate",         HDB_MBR_POP_SINGLE_COMPULSORY) ; }
    if (rc == E_OK) { mname = "Closed" ;    rc = pClass->InitMember(mname, "sdate",         HDB_MBR_POP_SINGLE_COMPULSORY) ; }
    if (rc == E_OK) { mname = "Currency" ;  rc = pClass->InitMember(mname, "string",        HDB_MBR_POP_SINGLE_COMPULSORY) ; }
    if (rc == E_OK) { mname = "Code" ;      rc = pClass->InitMember(mname, "string",        HDB_MBR_POP_SINGLE_COMPULSORY) ; }
    if (rc == E_OK) { mname = "Desc" ;      rc = pClass->InitMember(mname, "string",        HDB_MBR_POP_SINGLE_COMPULSORY) ; }
    if (rc == E_OK) { mname = "InitBal" ;   rc = pClass->InitMember(mname, "int32",         HDB_MBR_POP_SINGLE_COMPULSORY) ; }
    if (rc == E_OK) { mname = "Type" ;      rc = pClass->InitMember(mname, "enumAccType",   HDB_MBR_POP_SINGLE_COMPULSORY) ; }
    rc = pClass->InitDone() ;
    if (rc == E_OK)
        RegisterDataClass(pClass) ;
    //  Create and initialize Account repository
    pRepos = new hdbObjRepos(*this) ;
    if (!pRepos)
        hzexit(E_MEMORY, "No Currency cache allocated") ;
    rc = pRepos->InitStart(pClass, pClass->strType(), dataDir, HDB_REPOS_CACHE) ;
    if (rc != E_OK)
        hzexit(rc, "Could not init subsriber repos") ;
    mname = "Code" ;
    rc = pRepos->InitMbrIndex(mname, true) ;
    if (rc != E_OK)
        hzexit(rc, "Could not init subsriber repos index") ;
    rc = pRepos->InitDone() ;
    if (rc != E_OK)
        hzexit(rc, "Could not complete subsriber repos initialization") ;
    rc = m_mapObjRepos.Insert(pRepos->strName(), pRepos) ;
    pRepos->Open() ;
    /*
    **  Transactions
    */
    //  Set up Transaction data class
    cname = "Transaction" ;
    pClass = new hdbClass(*this, HDB_CLASS_DESIG_SYS) ;
    rc = pClass->InitStart(cname) ;
    if (rc == E_OK) { mname = "Date" ;      rc = pClass->InitMember(mname, "sdate",     HDB_MBR_POP_SINGLE_COMPULSORY) ; }
    if (rc == E_OK) { mname = "Currency" ;  rc = pClass->InitMember(mname, "string",    HDB_MBR_POP_SINGLE_COMPULSORY) ; }
    if (rc == E_OK) { mname = "Category" ;  rc = pClass->InitMember(mname, "string",    HDB_MBR_POP_SINGLE_COMPULSORY) ; }
    if (rc == E_OK) { mname = "From" ;      rc = pClass->InitMember(mname, "string",    HDB_MBR_POP_SINGLE_COMPULSORY) ; }
    if (rc == E_OK) { mname = "To" ;        rc = pClass->InitMember(mname, "string",    HDB_MBR_POP_SINGLE_COMPULSORY) ; }
    if (rc == E_OK) { mname = "Desc" ;      rc = pClass->InitMember(mname, "string",    HDB_MBR_POP_SINGLE_COMPULSORY) ; }
    if (rc == E_OK) { mname = "Note" ;      rc = pClass->InitMember(mname, "string",    HDB_MBR_POP_SINGLE_COMPULSORY) ; }
    if (rc == E_OK) { mname = "Qty" ;       rc = pClass->InitMember(mname, "string",    HDB_MBR_POP_SINGLE_COMPULSORY) ; }
    if (rc == E_OK) { mname = "Value" ;     rc = pClass->InitMember(mname, "int32",     HDB_MBR_POP_SINGLE_COMPULSORY) ; }
    if (rc == E_OK) { mname = "Type" ;      rc = pClass->InitMember(mname, "int32",     HDB_MBR_POP_SINGLE_COMPULSORY) ; }
    rc = pClass->InitDone() ;
    if (rc == E_OK)
        RegisterDataClass(pClass) ;
    //  Create and initialize Transaction repository
    pRepos = new hdbObjRepos(*this) ;
    if (!pRepos)
        hzexit(E_MEMORY, "No Currency cache allocated") ;
    rc = pRepos->InitStart(pClass, pClass->strType(), dataDir, HDB_REPOS_CACHE) ;
    if (rc != E_OK)
        hzexit(rc, "Could not init subsriber repos") ;
    mname = "Date" ;
    rc = pRepos->InitMbrIndex(mname, true) ;
    if (rc != E_OK)
        hzexit(rc, "Could not init subsriber repos index") ;
    mname = "From" ;
    rc = pRepos->InitMbrIndex(mname, true) ;
    if (rc != E_OK)
        hzexit(rc, "Could not init subsriber repos index") ;
    rc = pRepos->InitDone() ;
    if (rc != E_OK)
        hzexit(rc, "Could not complete subsriber repos initialization") ;
    rc = m_mapObjRepos.Insert(pRepos->strName(), pRepos) ;
    pRepos->Open() ;
    return rc ;
}
hzEcode hdbADP::InitBlockedIPs  (const hzString& dataDir)
{
    //  Create the Blocked-IP data class and repository
    _hzfunc("hdbADP::InitBlockedIPs") ;
    static  bool    bBeenHere = false ;     //  Set once run
    hdbClass*       pClass ;        //  Subscriber class pointer
    hdbObjRepos*    pRepos ;        //  Subscriber repository pointer
    hzString        S ;             //  Temporary string
    hzEcode         rc ;            //  Return code
    if (bBeenHere)
        return hzerr(E_SEQUENCE, "This function has already been called") ;
    bBeenHere = true ;
    if (!dataDir)
        return hzerr(E_ARGUMENT, "No application data directory") ;
    //  Allocate and initialize subscriber class
    S = "blockedIP" ;
    pClass = new hdbClass(*this, HDB_CLASS_DESIG_SYS) ;
    rc = pClass->InitStart(S) ;
    if (rc == E_OK) { S = "ipa" ;   rc = pClass->InitMember(S, datatype_IPADDR, HDB_MBR_POP_SINGLE_COMPULSORY) ; }
    if (rc == E_OK)
        rc = pClass->InitDone() ;
    //  Resister subscriber class
    if (rc == E_OK)
        RegisterDataClass(pClass) ;
    if (rc != E_OK)
        hzexit(rc, "Could not init blockedIP class") ;
    //  Create and initialize susscriber repository
    pRepos = new hdbObjRepos(*this) ;
    if (!pRepos)
        hzexit(E_MEMORY, "No blockedIP cache allocated") ;
    rc = pRepos->InitStart(pClass, pClass->strType(), dataDir, HDB_REPOS_CACHE) ;
    if (rc != E_OK)
        hzexit(rc, "Could not init blockedIP repos") ;
    S = "ipa" ;
    rc = pRepos->InitMbrIndex(S, true) ;
    if (rc != E_OK)
        hzexit(rc, "Could not init subsriber repos index") ;
    rc = pRepos->InitDone() ;
    if (rc != E_OK)
        hzexit(rc, "Could not complete subsriber repos initialization") ;
    //  Resister subscriber repository
    rc = m_mapObjRepos.Insert(pRepos->strName(), pRepos) ;
    return rc ;
}
void    hdbADP::Report  (hzChain& Z)
{
    //  Write out ADP
    //
    //  Diagnostics
    //
    //  Argument:   Z   Output chain
    //
    //  Returns:    None
    _hzfunc("hdbADP::Report") ;
    const hdbDatatype*  pDT ;       //  Data type
    const hdbClass*     pClass ;    //  Data class
    const hdbMember*    pMbr ;      //  Data class member
    hdbEnum*            pSlct ;     //  Data enum
    uint32_t    x ;         //  General iterator
    uint32_t    y ;         //  General iterator
    Z.Printf("APPLICATION DELTA PROFILE\n") ;
    //  Z.Printf("User Categories\n") ;
    //  for (x = 0 ; x < m_UserTypes.Count() ; x++)
    //      { ut = m_UserTypes.GetObj(x) ; Z.Printf(" -- %s\n", *ut.m_Refname) ; }
    Z.Printf("Data types\n") ;
    Z.Printf(" -- Validation formats\n") ;
    for (x = 0 ; x < m_mapDatatypes.Count() ; x++)
    {
        pDT = m_mapDatatypes.GetObj(x) ;
        if (pDT->Basetype() != BASETYPE_APPDEF)
            continue ;
        Z.Printf("\t -- %s\n", pDT->txtType()) ;
    }
    Z.Printf(" -- Selectors\n") ;
    for (x = 0 ; x < m_mapDatatypes.Count() ; x++)
    {
        pDT = m_mapDatatypes.GetObj(x) ;
        if (pDT->Basetype() != BASETYPE_ENUM)
            continue ;
        pSlct = (hdbEnum*) pDT ;
        Z.Printf("\t -- %s\n", pSlct->txtType()) ;
    }
    Z.Printf(" -- Clases\n") ;
    for (x = 0 ; x < m_mapClasses.Count() ; x++)
    {
        pClass = m_mapClasses.GetObj(x) ;
        Z.Printf("\t -- %s\n", pClass->txtType()) ;
        for (y = 0 ; y < pClass->MbrCount() ; y++)
        {
            pMbr = pClass->GetMember(y) ;
            Z.Printf("\t\t -- %s [%s]\n", pMbr->txtName(), Basetype2Txt(pMbr->Basetype())) ;
        }
    }
    Z.Printf(" -- Class Delta ID assignments\n") ;
    for (x = 0 ; x < m_mapClsCtxDtId.Count() ; x++)
    {
        y = m_mapClsCtxDtId.GetKey(x) ;
        pClass = m_mapClsCtxDtId.GetObj(x) ;
        Z.Printf("\t\t -- [%d] %s\n", y, pClass->txtType()) ;
    }
}
hzEcode hdbADP::RegisterDataClass   (const hdbClass* pClass)
{
    //  Insert a new data class into the ADP. This may only be done during initialization.
    //
    //  Argument:   pClass  The data class to be inserted
    _hzfunc("hdbADP::RegisterDataClass") ;
    if (!this)
        hzexit(E_CORRUPT, "No ADP instance") ;
    //  No data class?
    if (!pClass)
        return E_ARGUMENT ;
    //  Unnamed data class?
    if (!pClass->strType())
        return E_NOINIT ;
    //  Data class already has delta id?
    if (pClass->ClassId())
        return hzerr(E_DUPLICATE, "Data class %s already registered with id %d", pClass->txtType(), pClass->ClassId()) ;
    //  Data type already registered?
    if (m_mapDatatypes.Exists(pClass->strType()))
        return hzerr(E_DUPLICATE, "Data type %s already registered. Cannot admit class of this name", pClass->txtType()) ;
    //  Allocate the class id
    switch  (pClass->Designation())
    {
    case HDB_CLASS_DESIG_SYS:   if      (pClass->strType() == "subscriber")     pClass->_setId(HZ_ADP_CLS_SUBSCRIBER) ;
                                else if (pClass->strType() == "siteindex")      pClass->_setId(HZ_ADP_CLS_SITEINDEX) ;
                                else if (pClass->strType() == "FinCurrency")    pClass->_setId(HZ_ADP_CLS_FIN_CRCY) ;
                                else if (pClass->strType() == "FinCategory")    pClass->_setId(HZ_ADP_CLS_FIN_CAT) ;
                                else if (pClass->strType() == "FinAccount")     pClass->_setId(HZ_ADP_CLS_FIN_ACC) ;
                                else if (pClass->strType() == "FinTransaction") pClass->_setId(HZ_ADP_CLS_FIN_TRNS) ;
                                else
                                {
                                    hzerr(E_BADVALUE, "Unrecognized System Class (%s)", pClass->txtType()) ;
                                    return E_BADVALUE ;
                                }
                                break ;
    case HDB_CLASS_DESIG_USR:   pClass->_setId(m_nsqClsUsr++) ;
                                break ;
    case HDB_CLASS_DESIG_CFG:   pClass->_setId(m_nsqClsCfg++) ;
                                break ;
    }
    if (!pClass->ClassId())
        return hzerr(E_NOINIT, "Data class %s did not aquire a delta id", pClass->txtType()) ;
    //  Insert data class into ADP map of datatypes
    m_mapDatatypes.Insert(pClass->strType(), pClass) ;
    m_mapClasses.Insert(pClass->strType(), pClass) ;
    m_mapClsCtxName.Insert(pClass->strType(), pClass->ClassId()) ;  //m_mapClasses.Count()) ;
    m_mapClsCtxDtId.Insert(pClass->ClassId(), pClass) ;
    threadLog("Inserted %s\n", *pClass->strType()) ;
    return E_OK ;
}
hzEcode hdbADP::RegisterComposite   (hzString& context, const hdbClass* pClass)
{
    //  Insert a new data class context into the ADP. This may only be done during initialization.
    //
    //  Argument:   pClass  The data class to be inserted
    _hzfunc("hdbADP::RegisterComposite") ;
    if (!context || !pClass)
        return E_ARGUMENT ;
    m_mapClsCtxName.Insert(context, m_nsqClsCtx) ;
    m_mapClsCtxDtId.Insert(m_nsqClsCtx, pClass) ;
    m_nsqClsCtx++ ;
    return E_OK ;
}
hzEcode hdbADP::RegisterMember  (const hdbMember* pMbr)
{
    _hzfunc("hdbADP::RegisterMember") ;
    const hdbClass* pClass ;    //  Data class of member
    uint32_t        mbrId ;     //  Member ID to be
    pClass = pMbr->Class() ;
    if (!pClass)
        hzexit(E_NOINIT, "Member not initialized to its host data class") ;
    //  Assign member ID
    switch  (pClass->Designation())
    {
    case HDB_CLASS_DESIG_SYS:   mbrId = m_nsqMbrSys++ ; break ;
    case HDB_CLASS_DESIG_USR:   mbrId = m_nsqMbrUsr++ ; break ;
    case HDB_CLASS_DESIG_CFG:   mbrId = m_nsqMbrCfg++ ; break ;
    }
    m_mapMembers.Insert(mbrId, pMbr) ;
    pMbr->_setId(mbrId) ;
    return E_OK ;
}
hzEcode hdbADP::RegisterDataEnum    (const hdbEnum* pEnum)
{
    //  Insert a new data enum into the ADP. This may only be done during initialization.
    //
    _hzfunc("hdbADP::RegisterDataEnum") ;
    if (!pEnum)
        return E_ARGUMENT ;
    if (!pEnum->strType())
        return E_NOINIT ;
    m_mapDatatypes.Insert(pEnum->strType(), pEnum) ;
    m_mapEnums.Insert(pEnum->strType(), pEnum) ;
    return E_OK ;
}
hzEcode hdbADP::RegisterRegexType   (const hdbRgxtype* pRgx)
{
    _hzfunc("hdbADP::RegisterRegexType") ;
    if (!pRgx)
        return E_ARGUMENT ;
    if (!pRgx->strType())
        return E_NOINIT ;
    return m_mapDatatypes.Insert(pRgx->strType(), pRgx) ;
}
hzEcode hdbADP::RegisterObjRepos    (hdbObjRepos* pRepos)
{
    _hzfunc("hdbADP::RegisterObjRepos") ;
    hzEcode rc ;    //  Return code
    if (!pRepos)
        return E_ARGUMENT ;
    if (!pRepos->strName())
        return E_NOINIT ;
    if (m_mapObjRepos.Exists(pRepos->strName()))
        return hzerr(E_DUPLICATE, "Repository [%s] already exists", pRepos->txtName()) ;
    rc = m_mapObjRepos.Insert(pRepos->strName(), pRepos) ;
    if (rc != E_OK)
        return hzerr(rc, "Repository [%s] Register FAIL", pRepos->txtName()) ;
    return rc ;
}
#if 0
hzEcode hdbADP::RegisterDataCron    (hdbBinCron* pRepos)
{
    if (!pRepos)
        return E_ARGUMENT ;
    if (!pRepos->Name())
        return E_NOINIT ;
    return m_mapBinDataCrons.Insert(pRepos->Name(), pRepos) ;
}
hzEcode hdbADP::RegisterDataStore   (hdbBinStore* pRepos)
{
    if (!pRepos)
        return E_ARGUMENT ;
    if (!pRepos->m_Name)
        return E_NOINIT ;
    return m_mapBinDataStores.Insert(pRepos->m_Name, pRepos) ;
}
#endif
hzEcode hdbADP::RegisterBinRepos    (hdbBinRepos* pRepos)
{
    _hzfunc("hdbADP::RegisterBinRepos") ;
    if (!pRepos)
        return E_ARGUMENT ;
    if (!pRepos->txtName())
        return E_NOINIT ;
    return m_mapBinRepos.Insert(pRepos->strName(), pRepos) ;
}
bool    hdbADP::IsSubClass  (const hdbClass* pMain, const hdbClass* pSub)
{
    //  Determine if the second data class is a sub-class of the first
    //
    //  Arguments:  1)  pMain   The main class
    //              2)  pSub    The test class
    //
    //  Returns:    True    If the test class is a sub-class of the main class
    //              False   Otherwise
    _hzfunc("hdbADP::IsSubClass") ;
    uint32_t    val_Lo ;        //  First item
    uint32_t    val_Hi ;        //  Last item
    val_Lo = m_mapSubs.First(pMain->strType()) ;
    if (val_Lo >= 0)
    {
        val_Hi = m_mapSubs.Last(pMain->strType()) ;
        for (; val_Lo <= val_Hi ; val_Lo++)
        {
            if (pSub == m_mapSubs.GetObj(val_Lo))
                return true ;
        }
    }
    return false ;
}
//
//  File:   hzDelta.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.
//
//
//  Interface to Delta Server
//
/*
**  Variables
*/
static  hzMapS  <hzString,uint16_t> s_mapDS_AppsByName ;    //  1:1 map of app names to app ids
static  hzMapS  <uint16_t,hzString> s_mapDS_AppsByID ;      //  1:1 map of app ids to app names
static  hzString    s_DS_Hosts[4] ;     //  DS Hosts (Cluster machines)
static  hzIpaddr    s_DS_Addrs[4] ;     //  DS machine IP addresses
static  uint16_t    s_DS_Ports[4] ;     //  DS ports
static  uint32_t    s_deltaPort ;       //  Delta server port on local machine
const   hzString    s_deltaConf = "/home/deltasvr/cluster.xml" ; // Standard pathname of Delta config file
/*
**  Delta Functions
*/
hzEcode ReadConfigDS    (void)
{
    //  This reads the DS configs from the standard location /home/deltasvr/cluster.xml, which if it exists, is presumed to be owned by root.
    _hzfunc(__func__) ;
    FSTAT       fs ;        //  File status
    hzDocXml    X ;         //  XML document
    hzChain     err ;       //  Error report
    hzAttrset   ai ;        //  Attribute iterator
    hzXmlNode*  pRoot ;     //  Root XML tag
    hzXmlNode*  pN ;        //  XML node/tag
    hzXmlNode*  pNC ;       //  XML node for <deltaCluster> tag
    hzXmlNode*  pNS ;       //  XML node for <point[A-D]> tags
    hzString    appName ;   //  Application name
    hzIpaddr    ipa ;       //  IP address
    uint32_t    nAppId ;    //  App id
    uint32_t    nPort ;     //  Port number
    uint32_t    nIndex ;    //  Server iterator
    hzEcode     rc ;        //  Return code
    /*
    **  Read the DS config to obtain delta cluster info and to find the appID as used by the DS
    */
    if (lstat(s_deltaConf, &fs) < 0)
        return hzerr(E_NOTFOUND, "Delta config file %s not found\n", *s_deltaConf) ;
    rc = X.Load(s_deltaConf) ;
    if (rc != E_OK)
        return hzerr(rc, "Config (%s): Could not load\n", *s_deltaConf) ;
    pRoot = X.GetRoot() ;
    if (!pRoot->NameEQ("ConfigDS"))
        return hzerr(rc, "Config (%s): Expected <ConfigDS> root tag. Got <%s>\n", *s_deltaConf, pRoot->txtName()) ;
    //  Process <deltaCluster> tag
    pNC = pRoot->GetFirstChild() ;
    if (!pNC || !pNC->NameEQ("deltaCluster"))
        return hzerr(rc, "Config (%s): Expected <deltaCluster> tag as 1st child of <ConfigDS>\n", *s_deltaConf) ;
    //  Expect <deltaCluster> subtags to describe servers
    for (pNS = pNC->GetFirstChild() ; pNS ; pNS = pNS->Sibling())
    {
        if      (pNS->NameEQ("pointA")) nIndex = 0 ;
        else if (pNS->NameEQ("pointB")) nIndex = 1 ;
        else if (pNS->NameEQ("pointC")) nIndex = 2 ;
        else if (pNS->NameEQ("pointD")) nIndex = 3 ;
        else
            return hzerr(E_CONFIG, "Illegal tag <%s> in <deltaCluster>. Only <point[A-D]> allowed\n", pNS->txtName()) ;
        if (s_DS_Hosts[nIndex])
            return hzerr(E_DUPLICATE, "Duplicate tag <%s> in <deltaCluster>\n", pNS->txtName()) ;
        for (ai = pNS ; ai.Valid() ; ai.Advance())
        {
            if (ai.NameEQ("host"))
                s_DS_Hosts[nIndex] = ai.Value() ;
            else if (ai.NameEQ("addr"))
            {
                if (ai.Value())
                {
                    ipa = ai.Value() ;
                    if (ipa == _hzGlobal_nullIP)
                        return hzerr(E_CONFIG, "Line %d: Invalid IP address (%s)\n", pNS->Line(), ai.Value()) ;
                    s_DS_Addrs[nIndex] = ipa ;
                }
            }
            else if (ai.NameEQ("port"))
            {
                if (ai.Value())
                {
                    nPort = atoi(ai.Value()) ;
                    if (!nPort)
                        return hzerr(E_CONFIG, "Line %d: Invalid port (%s)\n", pNS->Line(), ai.Value()) ;
                    s_DS_Ports[nIndex] = nPort ;
                }
            }
            else
                return hzerr(E_CONFIG, "Line %d: Illegal attribute %s. Only host|addr|port allowed", pNS->Line(), ai.Name()) ;
        }
        //  Check we have everything
        if (!s_DS_Hosts[nIndex])
            return hzerr(E_CONFIG, "No hostname specified for server %s", pNS->txtName()) ;
        if (s_DS_Addrs[nIndex] || s_DS_Ports[nIndex])
        {
            //  Both must be either NULL or set
            if (s_DS_Addrs[nIndex] == _hzGlobal_nullIP)
                return hzerr(E_CONFIG, "Line %d: Invalid IP address (port is %u)\n", pNS->Line(), s_DS_Ports[nIndex]) ;
            if (!s_DS_Ports[nIndex])
                return hzerr(E_CONFIG, "Line %d: IP address supplied but no port\n", pNS->Line()) ;
            //  Get port for notifications
            if (ipa == _hzGlobal_livehost)
                s_deltaPort = nPort ;
        }
    }
    //  Process <deltaCluster> siblings. Expect <dsApp> and <dsRealm> tags. Only process <dsApp>
    for (pN = pNC->Sibling() ; rc == E_OK && pN ; pN = pN->Sibling())
    {
        //  Only interested in <deltaCluster> here
        if (!pN->NameEQ("dsRealm"))
            continue ;
        if (!pN->NameEQ("dsApp"))
            { rc = E_CONFIG ; err.Printf("Illegal tag <%s> in <ConfigDS>. Only <dsRealm> and <dsApp> allowed\n", pN->txtName()) ; }
        //  Process DS attributes
        for (ai = pN ; ai.Valid() ; ai.Advance())
        {
            if (ai.NameEQ("appname"))
                appName = ai.Value() ;
            else if (ai.NameEQ("appid"))
            {
                if (ai.Value())
                {
                    nAppId = atoi(ai.Value()) ;
                    if (!nAppId)
                        { rc = E_CONFIG ; err.Printf("Line %d: Invalid app id (%s)\n", pNS->Line(), ai.Value()) ; }
                }
            }
            else
                { rc = E_CONFIG ; err.Printf("Line %d: Illegal <dsApp> attribute %s. Only host|addr|port allowed", pN->Line(), ai.Name()) ; }
        }
        if (!appName)   { rc = E_CONFIG ; err.Printf("Line %d: <dsApp> tag does not specify an appName\n", pN->Line()) ; }
        if (!nAppId)    { rc = E_CONFIG ; err.Printf("Line %d: <dsApp> tag does not specify an app ID\n", pN->Line()) ; }
        if (rc == E_OK)
        {
            if (s_mapDS_AppsByName.Exists(appName)) { rc = E_DUPLICATE ; err.Printf("Line %d: App (%s) already exists\n", pN->Line(), *appName) ; }
            if (s_mapDS_AppsByID.Exists(nAppId))    { rc = E_DUPLICATE ; err.Printf("Line %d: App ID (%u) already exists\n", pN->Line(), nAppId) ; }
        }
    }
    if (err.Size())
        threadLog(err) ;
    return rc ;
}
hzEcode hdbADP::DeltaInit   (void)
{
    //  Category:   System
    //
    //  This description assumes knowledge of the HadronZoo Database (HDB), the Delta Server (DS), and the ADP (Application Delta Profile). Please see the HadronZoo library manual,
    //  section 5.
    //
    //  Programs that use the DS must call DeltaInit() as part of the initialization sequence. This call must take place AFTER data model construction is complete (all repositories
    //  declared and initialized), and BEFORE data transactions commence.
    //
    //  This function calls ReadConfigDS() in order to establish the following:-
    //
    //
    //  The primary objective is to verify the ADP. If the application is new (of a hitherto unknown appname), the ADP is simply accepted. This is safe if the config read succeeded
    //  as this proves the data model (from which the ADP is compiled), is viable. On ADP acceptance, the ADP description is written to a standard location, usually /etc/hzDelta/.
    //
    //  On subsequent runs the ADP description is compared to that stored in /etc/hzDelta/. The two ADP descriptions will be identical if there has been no change to the data model
    //  since the last run. If the data model has changed the ADP descriptions will not match. The new ADP can contain entities that do not exist in the old, and can omit entities
    //  that do exists in the old. However where the entity exists in both, the two ADPs must exactly agree on every detail of the entity. The program terminates if this comparison
    //  fails.
    //
    //  Note that ADP comparison does not involve the DS. Once the comparison succeeeds, if the application is delta enabled, the next step is to seek a connection to the DS. While
    //  the DS is intended to be omnipresent, if it isn't up this function will return E_OK, allowing the application to go into service without the DS.
    //
    //  Applications will not necessarily be delta enabled. It is standard practice for them not to be during development. Enabling a program for DS operation is a matter of adding
    //  a note to this effect in the configs. Not all applications will expect to receive deltas from sister applications on other machines. Those that do must add a DS handler and
    //  port, to the hzIpServer singleton.
    //
    //  Note that the DS reads ADP descriptions from the standard location. The ADP description used to be passed to the DS by the application. This is no longer the practice.
    //
    //  Arguments:  None
    //
    //  Returns:    E_ARGUMENT  If one or more arguments is null or invalid
    //              E_INITFAIL  If there is currently no hostname and none could be established
    //              E_FORMAT    If there is the application profile held in /ect/hzDelta.d/ is not of the expected form
    //              E_OPENFAIL  If this function cannot write out the application's version of the profile
    //              E_OK        If a connection to a delta server was established or if bMustHave is false
    _hzfunc("hdbADP::DeltaInit") ;
    static bool bBeenHere = false ;
    uint32_t    nIndex ;        //  Server iterator
    hzEcode     rc = E_OK ;     //  Return code
    if (!m_appName)
        return hzerr(E_SEQUENCE, "No application name") ;
    if (bBeenHere)
        return hzerr(E_SEQUENCE, "Repeat call") ;
    //  Establish hostname of this machine
    if (!_hzGlobal_Hostname)
    {
        if (SetupHost() != E_OK)
            hzexit(E_INITFAIL, "Could not setup hostname") ;
        if (!_hzGlobal_Hostname)
            hzexit(E_INITFAIL, "No hostname established") ;
    }
    //  Check that application configs have defined at least one data class and repository
    if (!CountDataClass())
        return hzerr(E_NODATA, "No Data Clases defined") ;
    if (!CountObjRepos())
        return hzerr(E_NODATA, "No Data Repositories defined") ;
    //  Read the DS config to obtain delta cluster info and to find the appID as used by the DS
    rc = ReadConfigDS() ;
    if (rc != E_OK)
        return rc ;
    /*
    **  DS servers and apps now listsed. Can set m_nDeltaID and if applicable, init connection to DS
    */
    for (nIndex = 0 ; nIndex < 4 ; nIndex++)
    {
        if (s_DS_Hosts[nIndex] == _hzGlobal_Hostname)
        {
            threadLog("Local Machine is member of the delta cluster\n") ;
            break ;
        }
    }
    if (s_DS_Addrs[nIndex] != _hzGlobal_nullIP && s_DS_Ports[nIndex])
    {
        threadLog("Local Machine is delta active\n") ;
        m_nDeltaID = s_mapDS_AppsByName[m_appName] ;
    }
    if (!m_nDeltaID)
    {
        threadLog("This app (%s) is NOT delta active\n", *m_appName) ;
        return E_OK ;
    }
    m_eDeltaMode = DELTA_MODE_AUTH ;
    //  Connect to DS as client
    SOCKADDRUN  svrAddr ;   //  Address of Local DS
    //  Create the socket
    memset(&svrAddr, 0, sizeof(svrAddr)) ;
    svrAddr.sun_family = AF_UNIX ;
    strcpy(svrAddr.sun_path, "/home/deltasvr/DS.unix.socket") ;
    if ((m_unixSock = socket(AF_UNIX, SOCK_STREAM, 0)) <= 0)
        return hzerr(E_NOSOCKET, "Could not create DS socket (returns %d, errno %d)") ;
    threadLog("DS: Using socket %d\n", m_unixSock) ;
    //  Connect stage
    if (connect(m_unixSock, (struct sockaddr *) &svrAddr, sizeof(svrAddr)) < 0)
        return hzerr(E_HOSTFAIL, "Could not connect to DS (errno=%d)", errno) ;
    return E_OK ;
}
/*
**  Delta Origination
*/
hzEcode hdbADP::DeltaOriginateFile  (const char* filepath, hzDeltaReq eOp) const
{
    //  Call this to sync application file change. All this does is send the pathname of the file or dir, and the operator.
    _hzfunc(__func__) ;
    hzEcode rc = E_OK ;     //  Return code
    if (m_eDeltaMode == DELTA_MODE_NULL)
        return E_OK ;
    return rc ;
}
hzEcode hdbADP::DeltaOriginateObj   (const hdbObjRepos* pObjRepos, hzChain& Zd) const
{
    _hzfunc("hdbADP::DeltaOriginateObj") ;
    hzChain::BlkIter    bi ;    //  Chain block iterator
    hzChain             X ;     //  Chain to send to DS
    int32_t     nSend ;         //  Bytes to send
    int32_t     nSent ;         //  Bytes sent
    uint32_t    nSIL ;          //  Serial integer len
    hzEcode     rc = E_OK ;     //  Return code
    char        outBuf[64] ;    //  Used to state repos id etc
    if (m_eDeltaMode == DELTA_MODE_NULL)
        return E_OK ;
    //  Compile delta header
    nSend = sprintf(outBuf, "a=%s;r=%s;", *m_appName, pObjRepos->txtName()) ;
    nSend += 2 ;
    nSend += Zd.Size() ;
    //  Write command
    X.AddByte(DELTA_ORIG_OBJECT) ;
    
    //  Write nLen
    WriteSerialUINT32(X, nSIL, nSend) ;
    //  Write delta header
    X << outBuf ;
    //  Write delta
    X << Zd ;
    threadLog(X) ;
    //  Write to socket
    for (bi = X ; rc == E_OK && bi.Data() ; bi.Advance())
    {
        nSent = write(m_unixSock, bi.Data(), bi.Size()) ;
        if (nSent < 0)
            rc = hzerr(E_SENDFAIL, "Send error (errno=%d)\n", errno) ;
    }
    return E_OK ;
}
hzEcode hdbADP::DeltaOriginateBin   (const hdbObjRepos* pObjRepos, const hdbMember* pMbr, hzChain& Zd, hzDeltaReq eOp) const
{
    _hzfunc("hdbADP::DeltaOriginateBin") ;
    hzChain::BlkIter    bi ;    //  Chain block iterator
    hzChain             X ;     //  Chain to send to DS
    int32_t     nSend ;         //  Bytes to send
    int32_t     nSent ;         //  Bytes sent
    uint32_t    nSIL ;          //  Serial integer len
    hzEcode     rc = E_OK ;     //  Return code
    char        outBuf[64] ;    //  Used to state repos id etc
    if (m_eDeltaMode == DELTA_MODE_NULL)
        return E_OK ;
    //  Compile delta header
    nSend = sprintf(outBuf, "a=%s;r%u.m%ui@bin", *m_appName, pObjRepos->DeltaId(), pMbr->Posn()) ;
    nSend += 2 ;
    nSend += Zd.Size() ;
    //  Write command
    X.AddByte(DELTA_ORIG_OBJECT) ;
    
    //  Write nLen
    WriteSerialUINT32(X, nSIL, nSend) ;
    //  Write delta header
    X << outBuf ;
    //  Write delta
    X << Zd ;
    threadLog(X) ;
    //  Write to socket
    for (bi = X ; rc == E_OK && bi.Data() ; bi.Advance())
    {
        nSent = write(m_unixSock, bi.Data(), bi.Size()) ;
        if (nSent < 0)
            rc = hzerr(E_SENDFAIL, "Send error (errno=%d)\n", errno) ;
    }
    return E_OK ;
}
hzEcode hdbADP::DeltaOriginateMbr   (const hdbObjRepos* pObjRepos, const hdbMember* pMbr, hzAtom value, hzDeltaReq eOp) const
{
    if (m_eDeltaMode == DELTA_MODE_NULL)
        return E_OK ;
    return E_OK ;
}
#if 0
/*
**  Handle notifications from server
*/
hzTcpCode   ProcDelta   (hzChain& ZI, hzIpConnex* pCx)
{
    //  Category:   System
    //
    //  Accepts and processes notification deltas from the Delta Server.
    //
    //  These will be of repository deltas and application files on sister applications running on other machines in the cluster. In the case of repository updates, the task is to
    //  locate the repository and apply the update. In the event of an uploaded file, the DS will apply the change.
    //
    //  This function must be supplied to the hzIpServer singleton, by an AddTCPPort() call.
    _hzfunc(__func__) ;
    chIter      zi ;            //  Input message iterator
    //uint32_t  sessId ;
    char    res [8] ;
    /*
    if (!_hzGlobal_DeltaClient)
    {
        res[0] = res[1] = res[2] = res[3] = 0 ;
        res[4] = DELTA_SVR_NACK ;
        res[5] = res[6] = res[7] = 0 ;
    }
    else
    {
        //  Formulate command
        sessId = _hzGlobal_DeltaClient->SessID() ;
        res[0] = (sessId & 0xff000000) >> 24 ;
        res[1] = (sessId & 0xff0000) >> 16 ;
        res[2] = (sessId & 0xff00) >> 8 ;
        res[3] = sessId & 0xff ;
        res[4] = DELTA_SVR_ACK ;
        res[5] = res[6] = res[7] = 0 ;
    }
    */
    if (write(pCx->CliSocket(), res, 8) < 0)
    {
        hzerr(E_SENDFAIL, "Send failed, killing connection\n") ;
        return TCP_TERMINATE ;
    }
    return TCP_KEEPALIVE ;
}
#endif