#include "node.h"

uint32_t Node::node_id = 0;

Node::Node(const std::string &name)
{
    node_id++;

    if (name.empty())
    {
        std::stringstream str_sm;
        str_sm << node_id;
        std::string node_id_str = str_sm.str();

        this->name = "node" + node_id_str;
    }
    else
    {
        this->name = name;
    }
}

Node::~Node()
{
    std::cout << "enter ~node() of \"" << name << "\"" << std::endl;

    for (Node *child : children)
    {
        delete child;
    }

    std::cout << "leave ~node() of \"" << name << "\"" << std::endl;
}

std::string Node::get_name() const
{
    return name;
}

void Node::set_name(const std::string &new_name)
{
    name = new_name;
}

int Node::get_nr_children() const
{
    return children.size();
}

Node *Node::get_child(int i) const
{
    if (i < 0 || i >= static_cast<int>(children.size()))
    {
        return nullptr;
    }

    return children[i];
}

void Node::add_child(Node *child)
{
    children.emplace_back(child);
}

void Node::print(std::ostream &str, uint32_t depth) const
{
    auto predicate = [&str](const Node *node, uint32_t depth) {
        for (uint32_t i = 0; i < depth; i++)
        {
            str << "\t";
        }

        str << node->get_name() + "\n";
    };

    Node::traverse_tree(this, predicate, false);
}

Node *Node::create_complete_tree(uint32_t nr_child_nodes, uint32_t tree_depth)
{
    if (tree_depth == 0)
    {
        return nullptr;
    }

    Node *node = new Node;

    for (uint32_t j = 0; j < nr_child_nodes; j++)
    {
        Node *child = Node::create_complete_tree(nr_child_nodes, tree_depth - 1);

        if (child == nullptr)
        {
            break;
        }

        node->children.emplace_back(child);
    }

    return node;
}

void Node::traverse_tree(const Node *node, std::function<void(const Node *, uint32_t)> predicate, bool recursive, uint32_t depth)
{
    if (recursive)
    {
        std::set<const Node *> visited;
        traverse_tree_recursive(node, predicate, visited, depth);
    }
    else
    {
        traverse_tree_iterative(node, predicate, depth);
    }
}

void Node::traverse_tree_recursive(const Node *node, std::function<void(const Node *, uint32_t)> predicate, std::set<const Node *> &visited, uint32_t depth)
{
    if (visited.find(node) != visited.end())
    {
        return;
    }

    predicate(node, depth);

    visited.insert(node);

    for (Node *child : node->children)
    {
        Node::traverse_tree_recursive(child, predicate, visited, depth + 1);
    }
}

void Node::traverse_tree_iterative(const Node *node, std::function<void(const Node *, uint32_t)> predicate, uint32_t depth)
{
    struct NodeInfo
    {
        const Node *node;
        uint32_t depth;
    };

    std::set<const Node *> visited;
    std::vector<NodeInfo> stack;

    NodeInfo info = {.node = node, .depth = 0};
    stack.emplace_back(info);

    while (!stack.empty())
    {
        // get the last node and remove it from the stack
        NodeInfo current = stack.back();
        stack.pop_back();

        // check if we already visited this node
        if (visited.find(current.node) != visited.end())
        {
            continue;
        }

        // execute given function
        predicate(current.node, current.depth);

        // mark this node as visited
        visited.insert(current.node);

        // push children to the stack
        for (auto child = current.node->children.rbegin(); child != current.node->children.rend(); child++)
        {
            NodeInfo info = {.node = *child, .depth = current.depth + 1};
            stack.emplace_back(info);
        }
    }
}

std::ostream &operator<<(std::ostream &os, const Node *node)
{
    node->print(os);

    return os;
}