PsTreeNode.cpp

// PsTreeNode.cpp
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2010

@import "PsTreeNode.h"
#include "Ceda/cxUtils/Tracer.h"
#include "Ceda/cxUtils/MsWindows.h"
#include <atomic>

static std::atomic<int> s_numAddedNodes(0);

@api int GetNumAddedNodes()
{
    return s_numAddedNodes;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// PsTreeNode

PsTreeNode::PsTreeNode() :
    m_amount(0),
    m_totalAmount(0)
{
}

PsTreeNode::~PsTreeNode()
{
}

bool PsTreeNode::operator==(const PsTreeNode& rhs) const
{
    if (m_amount != rhs.m_amount || 
        m_totalAmount != rhs.m_totalAmount || 
        m_children.size() != rhs.m_children.size())
    {
        return false;
    }
    //Tracer() << "A = " << m_amount << " equal\n";
    for (ceda::ssize_t i=0 ; i < m_children.size() ; ++i)
    {
        if (*m_children[i] != *rhs.m_children[i]) return false;
    }
    return true;
}

void PsTreeNode::ValidateChildren() const
{
    for (ceda::ssize_t i=0 ; i < m_children.size() ; ++i)
    {
        cxAssert(m_children[i]);
    }
}

void PsTreeNode::Serialise(ceda::Archive& ar) const
{
    ValidateChildren();
    ar << m_parent
       << m_amount
       << m_totalAmount
       << m_children;
}

void PsTreeNode::Deserialise(ceda::InputArchive& ar)
{
    ar >> m_parent
       >> m_amount
       >> m_totalAmount
       >> m_children;
    ValidateChildren();
}

void PsTreeNode::AdjustTotals(ceda::int32 delta)
{
    PsTreeNode* p = this;
    while(p)
    {
        p->m_totalAmount += delta;
        p->MarkAsDirtyWithoutTrace();
        
        p = p->m_parent.Get();
    }
}

void PsTreeNode::OffsetAmount(ceda::int32 delta)
{ 
    m_amount += delta;
    AdjustTotals(delta);    // marks this node as dirty
}

PsTreeNode* PsTreeNode::RemoveChild(ceda::ssize_t index)
{ 
    cxAlwaysAssert(0 <= index && index < m_children.size());

    PsTreeNode* c = m_children[index].Get();
    cxAlwaysAssert(c);

    AdjustTotals(-c->m_totalAmount);    // marks this node as dirty
    m_children.erase(m_children.begin() + index);
    
    c->m_parent = nullptr;
    c->MarkAsDirtyWithoutTrace();
    
    return c;
}

void PsTreeNode::RemoveThisNode()
{
    cxAlwaysAssert(m_parent);
    for (ceda::ssize_t i=0 ; i < m_parent->m_children.size() ; ++i)
    {
        if (m_parent->m_children[i] == this)
        {
            cxVerify(m_parent->RemoveChild(i) == this);
            return;
        }
    }
    cxAlwaysAssert(0);
}

void PsTreeNode::DeleteThisNode()
{
    RemoveThisNode();

    // todo. This should only require a PSpace if this node persists.  Need a suitable API function
    AsyncPermanentlyDeleteSubTree(this);
}

void PsTreeNode::AddChild(ceda::ssize_t index, PsTreeNode* c)
{
    cxAlwaysAssert(0 <= index && index <= m_children.size());
    cxAlwaysAssert(c);
    cxAlwaysAssert(c->m_parent == ceda::null);

    s_numAddedNodes++;

    AdjustTotals(+c->m_totalAmount);
    m_children.insert(m_children.begin() + index, c);
    c->m_parent = this;
    MarkAsDirtyWithoutTrace();
    DeclareReachable(this,c);
}

ceda::ssize_t PsTreeNode::GetNumNodes() const
{
    ceda::ssize_t count = 1;
    for (ceda::ssize_t i=0 ; i < m_children.size() ; ++i)
    {
        const PsTreeNode* c = m_children[i].Get();
        cxAlwaysAssert(c);
        count += c->GetNumNodes();
    }
    return count;
}

void PsTreeNode::CheckInvariant() const
{
    int sum = m_amount;
    for (ceda::ssize_t i=0 ; i < m_children.size() ; ++i)
    {
        const PsTreeNode* c = m_children[i].Get();
        cxAlwaysAssert(c);
        c->CheckInvariant();
        sum += c->m_totalAmount;
    }
    cxAlwaysAssert(sum == m_totalAmount);
}

void PsTreeNode::GetAllNodes(ceda::xvector<PsTreeNode*>& nodes)
{
    nodes.push_back(this);
    for (ceda::ssize_t i=0 ; i < m_children.size() ; ++i)
    {
        PsTreeNode* c = m_children[i].Get();
        cxAlwaysAssert(c);
        c->GetAllNodes(nodes);
    }
}

void PsTreeNode::TraceTree() const
{
    Tracer() << "A= " << m_amount << "  T= " << m_totalAmount << '\n';
    if (!m_children.empty())
    {
        Tracer() << "{\n";
        {
            ceda::TraceIndenter indent(2);
            for (ceda::ssize_t i=0 ; i < m_children.size() ; ++i)
            {
                PsTreeNode* c = m_children[i].Get();
                cxAlwaysAssert(c);
                c->TraceTree();
            }
        }
        Tracer() << "}\n";
    }
}

bool PsTreeNode::IsResidentInStore() const
{
    for (ceda::ssize_t i=0 ; i < m_children.size() ; ++i)
    {
        PsTreeNode* c = m_children[i].TryGet();
        if (!c || !c->IsResidentInStore()) return false;
    }
    return true;
}

bool PsTreeNode::TraceSubTreeResidentInStore() const
{
    bool allNodesResident = true;
    Tracer() << "A= " << m_amount << "  T= " << m_totalAmount << '\n';
    if (!m_children.empty())
    {
        Tracer() << "{\n";
        {
            ceda::TraceIndenter indent(2);
            for (ceda::ssize_t i=0 ; i < m_children.size() ; ++i)
            {
                if (PsTreeNode* c = m_children[i].TryGet())
                {
                    // Recurse
                    if (!c->TraceSubTreeResidentInStore())
                    {
                        allNodesResident = false;
                    }
                }
                else
                {
                    Tracer() << '[' << m_children[i].GetOid() << ']' << '\n';
                    allNodesResident = false;
                }
            }
        }
        Tracer() << "}\n";
    }
    return allNodesResident;
}

void PsTreeNode::GetOidsNotResidentInStore(ceda::xvector<ceda::OID>& oids) const
{
    for (ceda::ssize_t i=0 ; i < m_children.size() ; ++i)
    {
        if (PsTreeNode* c = m_children[i].TryGet())
        {
            // Recurse
            c->GetOidsNotResidentInStore(oids);
        }
        else
        {
            oids.push_back(m_children[i].GetOid());
        }
    }
}

void PsTreeNode::BuildTree(ceda::ssize_t depth, ceda::ssize_t fanout)
{
    m_children.clear();
    if (depth > 0)
    {
        m_children.resize(fanout);
        for (ceda::ssize_t i=0 ; i < fanout ; ++i)
        {
            PsTreeNode* c = $new PsTreeNode;
            c->m_parent = this;
            m_children[i] = c;
            DeclareReachable(this,c);
            c->BuildTree(depth-1, fanout);
        }
    }
    MarkAsDirtyWithoutTrace();
}