#include <iostream>
#include <algorithm>
#include <functional>
#include <vector>
#include <stdexcept>
#include <map>

// Die ist ganz nützlich.
template<typename CharT,
         typename Traits>
inline std::basic_string<CharT, Traits>& rtrim(std::basic_string<CharT, Traits>& s, std::ctype<CharT> const& facet)
{
    s.erase( std::find_if(s.rbegin(), s.rend(), [&](CharT ch){ return !facet.is( std::ctype_base::space, ch ); }).base(), std::end(s) );
    return s;
}

template<typename Exception = std::logic_error>
void Assert( bool b, std::string const& what = std::string() )
{
    if(!b)
        throw Exception(what);
}

template<typename CharT,
         typename Traits,
         typename Alloc>
inline std::basic_istream<CharT, Traits>& checked_getline( std::basic_istream<CharT, Traits>& is, std::basic_string<CharT, Traits, Alloc>& str, CharT delim, CharT NotAllowed )
{
    typedef std::basic_istream<CharT, Traits> istream_t;
	typedef std::basic_string<CharT, Traits, Alloc> string_t;

	typename string_t::size_type extracted = 0;
	const typename string_t::size_type max_size = str.max_size();

	typename std::ios_base::iostate err = std::ios_base::goodbit;
	typename istream_t::sentry sentry(is, true);

	if (sentry)
		try
		{
			str.erase();

			const typename Traits::int_type eof = Traits::eof();

			typename Traits::int_type ich = is.rdbuf()->sgetc();

			#define BREAK_WITH(a,b) \
				if( a ) \
				{ \
					b; \
					break; \
				}

			while( extracted < max_size )
			{
				BREAK_WITH( Traits::eq_int_type(ich, eof),
					        err |= std::ios_base::eofbit )

				auto ch = Traits::to_char_type(ich);

				BREAK_WITH( Traits::eq(ch, delim),
					        (++extracted, is.rdbuf()->sbumpc()) )

				BREAK_WITH( Traits::eq(ch, NotAllowed),
					        err |= std::ios_base::failbit )

				str += ch;

				++extracted;

				ich = is.rdbuf()->snextc();
			}
		}
		catch(...)
		{
			is.setstate(std::ios_base::badbit);
		}


	if (extracted == 0)
		err |= std::ios_base::failbit;

	if (err != std::ios_base::goodbit)
		is.setstate(err);

	return is;
}

#include <memory>

template<typename CharT,
         typename Traits = std::char_traits<CharT>,
         typename ValueT = std::basic_string<CharT, Traits> >
struct Node
{
    typedef CharT char_type;
    typedef Traits traits_type;

    typedef std::basic_string<CharT, Traits> key_type;
    typedef ValueT value_type;

    std::basic_string<CharT, Traits> const name;

    Node( std::basic_string<CharT, Traits> const& name = "Root" ):
        name(name) {}

    std::map<key_type, value_type> entries;

    std::vector<std::shared_ptr<Node>> child_nodes; // Darf *nicht* verändert werden - Inhalt wird gelöscht
};

template<typename CharT,
         typename Traits>
std::basic_istream<CharT, Traits>& ws_facet(std::basic_istream<CharT, Traits>& is, std::ctype<CharT> const& facet)
{
	typename std::basic_istream<CharT, Traits>::sentry sentry(is, true);

	if( sentry )
	{
		typename Traits::int_type const eof = Traits::eof();

		typename Traits::int_type ch = is.rdbuf()->sgetc();

		while( !Traits::eq_int_type(ch, eof)
			&& facet.is( std::ctype_base::space, Traits::to_char_type(ch) ) )
			ch = is.rdbuf()->snextc();

		if( Traits::eq_int_type(ch, eof) )
			is.setstate( std::ios_base::eofbit );
	}

	return is;
}

template<typename CharT,
         typename Traits,
         typename ValueT>
std::basic_istream<CharT, Traits>& operator>>( std::basic_istream<CharT, Traits>& is, Node<CharT, Traits, ValueT>& node )
{
    typedef std::basic_string<CharT, Traits> string_t;

    typedef Node<CharT, Traits, ValueT> node_t;

	auto const& ctype_facet = std::use_facet<std::ctype<CharT>>( is.getloc() );

	typename Traits::int_type const open_brace = Traits::to_int_type(ctype_facet.widen('[')),
	                                comment_char = Traits::to_int_type(ctype_facet.widen('#')),
	                                forward_slash = Traits::to_int_type(ctype_facet.widen('/'));

    CharT const separator = ctype_facet.widen('='),
                close_brace = ctype_facet.widen(']'),
                newline = ctype_facet.widen('\n');

    while( ws_facet(is, ctype_facet).good() )
    {
    	typename Traits::int_type ch = is.rdbuf()->sgetc();

        if( Traits::eq_int_type(ch, comment_char) )
            is.ignore( std::numeric_limits<std::streamsize>::max(), newline );

        else if( Traits::eq_int_type(ch, open_brace) ) /// Entweder eine neue Node wird eingeleitet, oder diese wird geschlossen ( [/...] )
        {
        	ws_facet(is.ignore(), ctype_facet);

            if( Traits::eq_int_type(is.rdbuf()->sgetc(), forward_slash) )
            {
				ws_facet(is.ignore(), ctype_facet);

                for( auto const& c : node.name )
                    Assert( Traits::eq_int_type( is.rdbuf()->sbumpc(), Traits::to_int_type(c) ), "Invalid closing tag!" );

                ws_facet(is, ctype_facet);

                Assert( Traits::eq_int_type( is.rdbuf()->sbumpc(), Traits::to_int_type(close_brace) ), "Missing ']' character at closing tag!" );

                break; /// Beenden.
            }
            else // Eine neue, verschachtelte Node
            {
                string_t name;

                checked_getline( is, name, close_brace, newline );

                Assert( is.good(), "Invalid syntax in node title!" );

                Assert( !rtrim(name, ctype_facet).empty(), "Empty node title!" );

                node.child_nodes.push_back( std::shared_ptr<node_t>{new node_t{name}} );

                Assert( is >> *node.child_nodes.back(), "Error while parsing sub-node!" );
            }
        }
        else /// Eine Key-Value zuweisung
        {
            std::pair<string_t, ValueT> pair;

            checked_getline( is, pair.first, separator, newline );

			Assert( is.good(), "Invalid syntax in key-value assignment!" );

            Assert( !rtrim(pair.first, ctype_facet).empty(), "Empty key!" );

            Assert( is >> pair.second, "Error while reading value type!" ); /// Hier wird der value eingelesen.

            node.entries.insert( std::move(pair) );
        }
    }

    return is;
}

#include <sstream>

int main()
{
    std::istringstream stream{
R"(
[Schurke]
Str=12
Dex= 17
Int=4
[/Schurke]

[Krieger]
Str=18
Dex=10
Int=2
[/Krieger]
)" };

    Node<char> node;
    stream >> node;

    for( auto sub_node : node.child_nodes )
    {
        std::cout << "Name: " << sub_node->name << '\n';
        for( auto const& pair : sub_node->entries )
            std::cout << pair.first << " : " << pair.second << '\n';
    }
}
