void OnlinePlanExpansionExecution::UpdateHistory(CaseEx* pCase)
{
    IOlcbpPlan *pCasePlan = pCase->Plan();
    IOlcbpPlan::NodeSerializedSet casePlanNodes = pCasePlan->GetNodes();

    for (auto caseNodeId : casePlanNodes)
    {
        IOlcbpPlan::NodeValue pOriginalNode = pCasePlan->GetNode(caseNodeId);

        if (BELONG(ActionType, pCasePlan->GetNode(caseNodeId)->StepTypeId()))
        {
            Action* pOriginalActionNode = (Action*)pOriginalNode;
            Action* pClonedActionNode = (Action*)m_clonedNodesMapping[pOriginalNode];
            pOriginalActionNode->SetExecutionHistory(pClonedActionNode->GetExecutionHistory());
        }
    }
}
void OnlinePlanExpansionExecution::ExpandGoal(_In_ IOlcbpPlan::NodeID expansionGoalNodeId, _In_ CaseEx *pExpansionCase)
{
    LogActivity(ExpandGoal);

    IOlcbpPlan *pCasePlan = pExpansionCase->Plan();
    IOlcbpPlan::NodeSerializedSet casePlanRoots = pCasePlan->GetOrphanNodes();
    IOlcbpPlan::NodeSerializedSet casePlanNodes = pCasePlan->GetNodes();
    IOlcbpPlan::NodeSerializedSet newExpansionPlanRoots;

    LogInfo("Expanding node[%d] with plan %s", expansionGoalNodeId, pExpansionCase->Plan()->ToString().c_str());

    // 1. Construct planner plan graph nodes from the expansion case
    SetGoalNodeBelongingCase(expansionGoalNodeId, pExpansionCase);

    // Cross mapping between node IDs in in the planner plan and case plan
    std::map<IOlcbpPlan::NodeID, IOlcbpPlan::NodeID> plannerToCasePlanNodeIdMap;
    std::map<IOlcbpPlan::NodeID, IOlcbpPlan::NodeID> casePlanToPlannerNodeIdMap;

    for(auto caseNodeId : casePlanNodes)
    {
        IOlcbpPlan::NodeValue pOriginalNode = pCasePlan->GetNode(caseNodeId);
        IOlcbpPlan::NodeValue pNode = static_cast<PlanStepEx*>(const_cast<PlanStepEx*>(pOriginalNode)->Clone());

        IOlcbpPlan::NodeID plannerNodeId = m_pOlcbpPlan->AddNode(pNode, pNode->Id());
        m_nodeData[plannerNodeId] = OlcbpPlanNodeData();
        m_nodeData[plannerNodeId].ID = plannerNodeId;

        // Add Data record for the new node
        SetNodeSatisfyingGoal(plannerNodeId, expansionGoalNodeId);

        if (casePlanRoots.count(caseNodeId) > 0)
            newExpansionPlanRoots.insert(plannerNodeId);

        if (IsActionNode(plannerNodeId))
        {
            m_clonedNodesMapping[pOriginalNode] = pNode;
        }
        else if (IsGoalNode(plannerNodeId))
        {
            OpenNode(plannerNodeId);
        }

        // Map node ID in in the planner plan with its counterpart in case plan and vice versa
        plannerToCasePlanNodeIdMap[plannerNodeId] = caseNodeId;
        casePlanToPlannerNodeIdMap[caseNodeId] = plannerNodeId;
    }

    // 2. Link the goal node with the roots of the expansion plan
    IOlcbpPlan::NodeQueue Q;
    IOlcbpPlan::NodeSerializedSet visitedNodes;

    for(auto rootNodeId : newExpansionPlanRoots)
    {
        // Cross link the goal node with the sub plan roots
        LinkNodes(expansionGoalNodeId, rootNodeId);

        // Enqueue the roots in a queue to expand them
        Q.push(rootNodeId);
        visitedNodes.insert(rootNodeId);
    }

    // 3. Continue construction of the sub plan tree with the rest of the plan graph nodes
    while(!Q.empty())
    {
        IOlcbpPlan::NodeID currPlannerNodeId = Q.front();
        Q.pop();

        IOlcbpPlan::NodeSerializedSet currCaseChildren = pCasePlan->GetAdjacentNodes(plannerToCasePlanNodeIdMap[currPlannerNodeId]);

        for (auto currCaseChildNodeId : currCaseChildren)
        {
            IOlcbpPlan::NodeID currPlannerChildNodeId = casePlanToPlannerNodeIdMap[currCaseChildNodeId];

            // If my child not visited, consider it for expansion
            if(!visitedNodes.count(currPlannerChildNodeId))
            {
                Q.push(currPlannerChildNodeId);
                visitedNodes.insert(currPlannerChildNodeId);
            }

            // Cross link the current node with its child
            LinkNodes(currPlannerNodeId, currPlannerChildNodeId);
        }
    }

    int nodeAddRemoveDelta = 0; //AdaptSnippet(expansionGoalNodeId);
    // Adaptation should leave at least 1 node in the snippet
    _ASSERTE((int)pCasePlan->Size() + nodeAddRemoveDelta > 0);
    size_t adaptedSnippetSize = (size_t)((int)pCasePlan->Size() + nodeAddRemoveDelta);

    CloseNode(expansionGoalNodeId);

    // 4. Since the plan structure changed, recompute the NotReadyParentsCount
    // for plan graph node
    ComputeFreshSnippetWaitOnParentsCount(expansionGoalNodeId);

    // 5. Record that the expanded goal node is waiting for N number of open nodes
    // If the number of those open nodes = 0, and the goal node is not satisfied, then
    // there is no way for the goal to succeed, and the goal should fail
    GetNodeData(expansionGoalNodeId).SetWaitOnChildrenCount(adaptedSnippetSize);

    m_planStructureChangedThisFrame = true;

    LogInfo("Plan after node[%d] expansion %s", expansionGoalNodeId, m_pOlcbpPlan->ToString().c_str());
}
void LfhdCbNodeSelector::Select(_Out_ IOlcbpPlan::NodeSet& goalsToUpdate,
    _Out_ IOlcbpPlan::NodeDQueue& actionsToUpdate,
    _Out_ IOlcbpPlan::NodeSet& snippetsToDestroy)
{
    IOlcbpPlan* pPlan = m_pPlanner->Plan();
    IOlcbpPlan::NodeQueue actionQ;
    IOlcbpPlan::NodeQueue goalQ;
    typedef unsigned GoalKey;
    map<GoalKey, stack<IOlcbpPlan::NodeID>> goalTable;

    // 1st pass: get ready nodes only
    m_pPlanner->GetReachableReadyNodes(actionQ, goalQ);

    while (!goalQ.empty())
    {
        IOlcbpPlan::NodeID currentGoalId = goalQ.front();
        goalQ.pop();

        if (!pPlan->Contains(currentGoalId))
            continue;

        GoalEx* pCurrGoal = (GoalEx*)pPlan->GetNode(currentGoalId);
        GoalKey typeKey = pCurrGoal->Key();

        if (!m_pPlanner->IsNodeDone(currentGoalId))
        {
            IOlcbpPlan::NodeID newActiveGoalId = currentGoalId;

            if (goalTable.count(typeKey) == 0)
                goalTable[typeKey].push(newActiveGoalId);
            else
            {
                auto& currentOverrideStack = goalTable[typeKey];
                IOlcbpPlan::NodeID currentActiveGoalId = currentOverrideStack.top();

                IOlcbpPlan::NodeSet ancestors;
                stack<IOlcbpPlan::NodeID> newOverrideStack;

                m_pPlanner->GetAncestorSatisfyingGoals(newActiveGoalId, ancestors);
                newOverrideStack.push(newActiveGoalId);

                while (!currentOverrideStack.empty())
                {
                    currentActiveGoalId = currentOverrideStack.top();

                    // Nodes belonging to my ancestors set should not be destroyed
                    // and should be kept in order
                    if (ancestors.count(currentActiveGoalId) != 0)
                    {
                        newOverrideStack.push(currentActiveGoalId);
                    }
                    else if (pPlan->Contains(currentActiveGoalId) &&
                        !m_pPlanner->IsNodeOpen(currentActiveGoalId) &&
                        !m_pPlanner->HasActiveAction(currentActiveGoalId))
                    {
                        snippetsToDestroy.insert(currentActiveGoalId);
                    }

                    currentOverrideStack.pop();
                }

                // Push back new stack into the current one in a LIFO order to reverse the original one
                while (!newOverrideStack.empty())
                {
                    currentOverrideStack.push(newOverrideStack.top());
                    newOverrideStack.pop();
                }
            }
        }
    }

    for (auto& r : goalTable)
    {
        goalsToUpdate.insert(r.second.top());
    }

    while (!actionQ.empty())
    {
        auto actionNodeId = actionQ.front();

        bool satisfyingGoalActive = goalsToUpdate.count(m_pPlanner->GetNodeData(actionNodeId).SatisfyingGoal) > 0;
        bool satisfyingGoalDone = m_pPlanner->IsNodeDone(m_pPlanner->GetNodeData(actionNodeId).SatisfyingGoal);

        if (satisfyingGoalActive || satisfyingGoalDone)
        {
            actionsToUpdate.push_back(actionNodeId);
        }

        actionQ.pop();
    }
}