#include <iostream>
#include <utility>
#include <memory>
#include <vector>
#include <stdexcept>

class element
{
    public:
        element() = default;
        virtual ~element() = default;

        element(std::string value)
        : value_{ std::move(value) }
        {
        }

        auto value() const -> const std::string&
        {
            return value_;
        }

        virtual const element& child(std::size_t i) const
        {
            throw std::runtime_error("Invalid child index.");
        }
   
        virtual void print(std::ostream& out, int level) const
        {
            out << std::string(level * 2, ' ')
                << "<element>" << value() << "</element>";
        }
    
    private:
        std::string value_;
};

auto operator<<(std::ostream& out, const element& e) -> std::ostream&
{
    e.print(out, 0);
    return out;
}

class group : public element
{
    public:
        template <typename... Children>
        group(Children&&... children)
        {
            append(std::forward<Children>(children)...);
        }
        
        template <typename... Children>
        void append(Children&&... children)
        {
            (
                children_.emplace_back(
                    std::make_unique<Children>(std::forward<Children>(children))
                ), 
                ...
            );
        }        

        virtual const element& child(std::size_t i) const override
        {
            if (i >= children_.size())
                throw std::runtime_error("Invalid child index.");
            return *children_[i];
        }

        virtual void print(std::ostream& out, int level) const override
        {
            out << std::string(level * 2, ' ') << "<group>\n";
            print_children(out, level + 1);
            out << std::string(level * 2, ' ') << "</group>";
        }

        void print_children(std::ostream& out, int level) const
        {
            for (const auto& child : children_)
            {
                child->print(out, level);
                out << "\n";
            }            
        }

    private:
        std::vector<std::unique_ptr<element>> children_;
};

class document : public group
{
    public:
        template <typename... Children>
        document(Children&&... children)
        : group{ std::forward<Children>(children)... }
        {
        }

    protected:
        virtual void print(std::ostream& out, int level) const override
        {
            out << std::string(level * 2, ' ') << "<document>\n";
            print_children(out, level + 1);
            out << std::string(level * 2, ' ') << "</document>";
        }        
};

auto main() -> int
{
    auto d = document{
        group{
            element{ "element1" },
            element{ "element2" },
            element{ "element3" }
        },
        group{
            group{
                element{ "element4" },
                element{ "element5" },
                element{ "element6" }
            },
            element{ "element7" }
        }
    };
    
    d.append(
        group{
            element{ "element8" }
        }
    );
    
    std::cout << "\nd --------------------------------------\n\n" << d << "\n";
    std::cout << "\nd.child(1) -----------------------------\n\n"
        << d.child(1) << "\n";
    std::cout << "\nd.child(1).child(0).child(2).value() ---\n\n" 
        << d.child(1).child(0).child(2).value() << "\n\n";
}