#pragma once
#include <algorithm>
#include <cstddef>
#include <memory_resource>
#include <stdexcept>
#include <vector>
template <class Owner, class Tag = void>
class Multiway_Node {
public:
    explicit Multiway_Node(Owner* owner = nullptr, std::pmr::memory_resource& memory_resource = *std::pmr::get_default_resource()) noexcept
        : owner_(owner), children_(&memory_resource) {}
    Multiway_Node(const Multiway_Node&) = delete;
    Multiway_Node& operator=(const Multiway_Node&) = delete;
    Multiway_Node(Multiway_Node&&) = delete;
    Multiway_Node& operator=(Multiway_Node&&) = delete;
    ~Multiway_Node() {
        detach();
        for (Multiway_Node* child : children_) {
            child->parent_ = nullptr;
        }
    }
    void append_child(Multiway_Node& child) {
        insert_child(children_.size(), child);
    }
    void insert_child(std::size_t index, Multiway_Node& child) {
        if (index > children_.size()) {
            throw std::out_of_range("multiway node child index");
        }
        if (&child == this || child.is_ancestor_of(*this)) {
            throw std::invalid_argument("multiway node cycle");
        }
        if (child.parent_ == this) {
            const auto current = static_cast<std::size_t>(std::find(children_.begin(), children_.end(), &child) - children_.begin());
            if (current < index) {
                --index;
            }
            if (current == index) {
                return;
            }
        }
        child.detach();
        child.parent_ = this;
        children_.insert(children_.begin() + static_cast<std::ptrdiff_t>(index), &child);
    }
    void detach() noexcept {
        if (!parent_) {
            return;
        }
        std::erase(parent_->children_, this);
        parent_ = nullptr;
    }
    bool is_ancestor_of(const Multiway_Node& node) const noexcept {
        for (const Multiway_Node* current = node.parent_; current; current = current->parent_) {
            if (current == this) {
                return true;
            }
        }
        return false;
    }
    Owner* owner() noexcept {
        return owner_;
    }
    const Owner* owner() const noexcept {
        return owner_;
    }
    Multiway_Node* parent() noexcept {
        return parent_;
    }
    const Multiway_Node* parent() const noexcept {
        return parent_;
    }
    std::size_t child_count() const noexcept {
        return children_.size();
    }
    bool children_empty() const noexcept {
        return children_.empty();
    }
    Multiway_Node& child(std::size_t index) {
        return *children_.at(index);
    }
    const Multiway_Node& child(std::size_t index) const {
        return *children_.at(index);
    }
private:
    Owner* owner_{};
    Multiway_Node* parent_{};
    std::pmr::vector<Multiway_Node*> children_;
};
