//
//  File:   hdsNavtree.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 <unistd.h>
#include <stdarg.h>
#include <dirent.h>
#include <netdb.h>
#include <signal.h>
#include "hzErrcode.h"
#include "hzDissemino.h"
using namespace std ;
/*
**  Functions
*/
const hdsArticle*   hdsNavtree::GetItem (const hzString& refname) const
{
    _navitem    item ;  //  Title/articleId
    if (!m_ItemsByName.Exists(refname))
        return 0 ;
    item = m_ItemsByName[refname] ;
    if (item.m_ItemId)
        return m_Articles[item.m_ItemId - 1] ;
    return 0 ;
}
const hdsArticle*   hdsNavtree::GetItem (uint32_t itemId) const
{
    if (itemId == 0 || itemId > m_Articles.Count())
        return 0 ;
    return m_Articles[itemId - 1] ;
}
hzEcode hdsNavtree::AddHead (const hzString& parent, const hzString& refname, const hzString& title, bool bSlct)
{
    //  Add a heading to the tree. No article so the _navitem only has the article title
    _hzfunc("hdsNavtree::AddHead(1)") ;
    _navitem    item ;      //  New tree item
    _navitem    parItem ;   //  Parent item
    //  Qualify item
    if (!refname)   return hzerr(E_ARGUMENT, "%s: No item refname supplied", *m_Groupname) ;
    if (!title)     return hzerr(E_ARGUMENT, "%s: No item title supplied", *m_Groupname) ;
    if (m_ItemsByName.Exists(refname))
        hzexit(E_DUPLICATE, "%s: Heading %s (%s) already exists", *m_Groupname, *refname, *title) ;
    if (parent)
    {
        if (!m_ItemsByName.Exists(parent))
            return hzerr(E_CORRUPT, "%s: Heading %s (%s): Stated parent (%s) does not exist", *m_Groupname, *refname, *title, *parent) ;
        parItem = m_ItemsByName[parent] ;
    }
    item.m_Title = title ;
    if (bSlct)
        item.m_bFlags |= HZ_TREEITEM_OPEN ;
    item.m_bFlags |= HZ_TREEITEM_LINK ;
    if (!parent)
        item.m_nLevel = 0 ;
    else
        item.m_nLevel = parItem.m_nLevel + 1 ;
    m_ItemsByParent.Insert(parent, refname) ;
    m_ItemsByName.Insert(refname, item) ;
    return E_OK ;
}
hzEcode hdsNavtree::AddItem (const hzString& parent, const hzString& refname, const hzString& title, hdsArticle* pArt, bool bSlct)
{
    //  Add an item. The first item should have a NULL parent
    //
    //  Arguments:  1)  parent  Parent refname
    //              2)  refname Reference name of new item
    //              3)  title   Title (how item appears in tree)
    //              4)  pItem   Tree item (if any)
    //              5)  bSlct   Show as open
    //
    //  Returns:    E_ARGUMENT  If either the itemId or headline is not supplied
    //              E_DUPLICATE If the supplied item id already exists
    _hzfunc("hdsNavtree::AddItem") ;
    _navitem    item ;          //  New item
    _navitem    parItem ;       //  Parent item
    hzEcode     rc = E_OK ;     //  Return code
    //  Qualify item
    threadLog("Adding article %p %s\n", pArt, *refname) ;
    if (!refname)   return hzerr(E_ARGUMENT, "%s: No item item id supplied", *m_Groupname) ;
    if (!title)     return hzerr(E_ARGUMENT, "%s: No item headline supplied", *m_Groupname) ;
    //  Check item id (item id) is unique
    if (m_ItemsByName.Exists(refname))
        return hzerr(E_DUPLICATE, "%s: Duplicate entry attempted: Item %s alredy exists", *m_Groupname, *refname) ;
    //  If parent is supplied, it must exist
    if (parent)
    {
        if (!m_ItemsByName.Count())
            return hzerr(E_CORRUPT, "%s: Adding first item %s but with non-null parent", *m_Groupname, *title) ;
        if (!m_ItemsByName.Exists(parent))
            return hzerr(E_CORRUPT, "%s: Supplied parent (%s) does not exist", *m_Groupname, *parent) ;
        parItem = m_ItemsByName[parent] ;
    }
    //  ADD the item
    m_Articles.Add(pArt) ;
    item.m_ItemId = m_Articles.Count() ;
    item.m_Title = title ;
    if (bSlct)
        item.m_bFlags |= HZ_TREEITEM_OPEN ;
    item.m_bFlags |= HZ_TREEITEM_LINK ;
    if (!parent)
        item.m_nLevel = 0 ;
    else
        item.m_nLevel = parItem.m_nLevel + 1 ;
    rc = m_ItemsByParent.Insert(parent, refname) ;
    if (rc != E_OK)
        return hzerr(rc, "%s: Could not insert parent for article %s", *m_Groupname, *title) ;
    rc = m_ItemsByName.Insert(refname, item) ;
    if (rc != E_OK)
        return hzerr(rc, "%s: Could not insert article %s", *m_Groupname, *title) ;
    return rc ;
}
void    hdsNavtree::Clear   (void)
{
    _hzfunc("hdsNavtree::Clear") ;
    hdsArticle*     pArt ;  //  Current item
    uint32_t        n ;     //  Article iterator
    for (n = 0 ; n < m_Articles.Count() ; n++)
    {
        pArt = m_Articles[n] ;
        delete pArt ;
    }
    m_ItemsByParent.Clear() ;
    m_ItemsByName.Clear() ;
}
hzEcode hdsNavtree::_procArticle    (hzChain& Z, const hzString& parent) const
{
    //  Support function for ExportArticleSet (see below)
    //
    //  Adds tree item to supplied chain
    //
    //  Arguments:  1)  J       Chain in which tree is being built
    //              2)  pItem   Tree item to process
    //
    //  Returns:    E_ARGUMENT  If the tree item pointer is NULL
    //              E_OK        If the tree is exported to the supplied chain
    _hzfunc("hdsNavtree::_procArticle") ;
    hdsArticle*     pArt ;      //  Article
    hdsArticleStd*  pArtStd ;   //  Article
    _navitem        item ;      //  Next tree item
    hzXDate         now ;       //  Current time and date
    hzString        refname ;   //  Refname of item
    hzString        state ;     //  Open or closed
    int32_t         nLo ;       //  Lowest position in m_AllItemsByParent
    int32_t         nHi ;       //  Higest position in m_AllItemsByParent
    int32_t         n ;         //  Item counter
    nLo = m_ItemsByParent.First(parent) ;
    if (nLo < 0)
        return E_OK ;
    nHi = m_ItemsByParent.Last(parent) ;
    now.SysDateTime() ;
    //  Print:  Item is a folder. In the first instance (the only time the level is 1) for channel J we don't want to start with a comma, otherwise we do
    for (n = nLo ; n <= nHi ; n++)
    {
        refname = m_ItemsByParent.GetObj(n) ;
        item = m_ItemsByName[refname] ;
        if (item.m_bFlags & HZ_TREEITEM_OPEN)
            state = "open" ;
        else
            state = "closed" ; 
        if (!item.m_ItemId)
        {
            if (item.m_bFlags & HZ_TREEITEM_LINK)
                Z.Printf("\n<xtreeItem parent=\"%s\" id=\"%s\" hdln=\"%s\" display=\"%s\"/>\n", *parent, *refname, *item.m_Title, *state) ;
            else
                Z.Printf("\n<xtreeHead parent=\"%s\" id=\"%s\" hdln=\"%s\" display=\"%s\"/>\n", *parent, *refname, *item.m_Title, *state) ;
        }
        else
        {
            pArt = m_Articles[item.m_ItemId - 1] ;
            pArtStd = dynamic_cast<hdsArticleStd*>(pArt) ;
            if (item.m_bFlags & HZ_TREEITEM_LINK)
            {
                Z.Printf("\n<xtreeItem parent=\"%s\" id=\"%s\" tdstamp=\"%s\" hdln=\"%s\" display=\"%s\">\n", *parent, *refname, *now, *item.m_Title, *state) ;
                if (pArtStd)
                    //Z << pArtStd->m_Content ;
                    Z << pArtStd->m_rawHTML ;
                Z << "\n</xtreeItem>\n" ;
            }
            else
            {
                Z.Printf("\n<xtreeHead parent=\"%s\" id=\"%s\" hdln=\"%s\" display=\"%s\">\n", *parent, *refname, *item.m_Title, *state) ;
                if (pArtStd)
                    //Z << pArtStd->m_Content ;
                    Z << pArtStd->m_rawHTML ;
                Z << "\n</xtreeHead>\n" ;
            }
        }
        _procArticle(Z, refname) ;
    }
    return E_OK ;
}
hzEcode hdsNavtree::ExportArticleSet    (hzChain& Z)
{
    //  Purpose:    Export tree as an article set
    //
    //  Arguments:  1)  J   The chain in which tree is being built
    //
    //  Returns:    E_NODATA    If the tree is empty
    //              E_OK        If the tree is exported to the supplied chain
    _hzfunc("hdsNavtree::ExportArtcleSet") ;
    if (!m_ItemsByName.Count())
        return E_NODATA ;
    hzString    root ;      //  Blank string
    _procArticle(Z, root) ;
    return E_OK ;
}
hzEcode hdsNavtree::_procTreeitem   (hzChain& J, const hzString& parent) const
{
    //  Support function for ExportDataScript (see below)
    //
    //  Adds tree item to supplied chain
    //
    //  Arguments:  1)  J       Chain in which tree is being built
    //              2)  pItem   Tree item to process
    //
    //  Returns:    E_ARGUMENT  If the tree item pointer is NULL
    //              E_OK        If the tree is exported to the supplied chain
    _hzfunc("hdsNavtree::_procTreeitem") ;
    _navitem    item ;          //  Tree item
    hzString    refname ;       //  Refname of item
    int32_t     nLo ;           //  Lowest position in m_AllItemsByParent
    int32_t     nHi ;           //  Higest position in m_AllItemsByParent
    int32_t     n ;             //  Item counter
    int32_t     i ;             //  Indent counter
    int32_t     nChild ;        //  True if link
    //uint32_t  id ;            //  ID of item
    nLo = m_ItemsByParent.First(parent) ;
    if (nLo < 0)
        return E_OK ;
    nHi = m_ItemsByParent.Last(parent) ;
    for (n = nLo ; n <= nHi ; n++)
    {
        refname = m_ItemsByParent.GetObj(n) ;
        item = m_ItemsByName[refname] ;
        nChild = m_ItemsByParent.First(refname) >= 0 ? 1 : 0 ;
        //nChild = 1 ;
        if (item.m_nLevel || n > nLo)
            J.AddByte(CHAR_COMMA) ;
        J.AddByte(CHAR_NL) ;
        for (i = item.m_nLevel ; i ; i--)
            J.AddByte(CHAR_TAB) ;
        if (item.m_bFlags & HZ_TREEITEM_LINK)
            J.Printf("[%d,%d,%d,\"%s\",\"%s\"]", item.m_nLevel, nChild, item.m_bFlags & HZ_TREEITEM_OPEN ? 1:0 , *item.m_Title, *refname) ;
        else
            J.Printf("[%d,%d,%d,\"%s\",\"%s\"]", item.m_nLevel, nChild, item.m_bFlags & HZ_TREEITEM_OPEN ? 1:0 , *item.m_Title, *refname) ;
        if (nChild)
            _procTreeitem(J, refname) ;
    }
    return E_OK ;
}
hzEcode hdsNavtree::ExportDataScript    (hzChain& J)
{
    //  Purpose:    Build a HTML/JavaScript hierarchical tree.
    //
    //  Generate data script for the tree. A data script for trees is always an array of treeitems which in turn comprise four values. These are the
    //  level, a 0 or a 1 for either item/folder, the title and the URL (link to item).
    //
    //  The data script is used in conjuction with the _dsmScript_navtree/makeTree script which uses the data script to effect the tree in HTML bu a series of
    //  document.write operations.
    //
    //  Arguments:  1)  J   The chain in which tree is being built
    //
    //  Returns:    E_NODATA    If the tree is empty
    //              E_OK        If the tree is exported to the supplied chain
    _hzfunc("hdsNavtree::ExportDataScript") ;
    if (!this)
        hzexit(E_CORRUPT, "No instance") ;
    if (!m_ItemsByName.Count())
        return E_NODATA ;
    hzString    root ;  //  Blank start
    J << "var ntX=new Array();\nntX=[" ;
    _procTreeitem(J, root) ;
    J << "\n\t];\n" ;
    //  threadLog("BEGIN NAV\n") ;
    //  threadLog(J) ;
    //  threadLog("ENDF NAV\n") ;
    return E_OK ;
}