#ifndef EMPATHY_IR_ENUMERATE_INL
#define EMPATHY_IR_ENUMERATE_INL

namespace empathy::ir
{
    //--------------------------------------------------------------------------------------------
    // Primitive types
    //--------------------------------------------------------------------------------------------
    template< typename T, typename U >
    static Generator< pair< Term, const U& > > Enumerate( const ValueTrieNode< T, U >& trie )
    {
        for( auto&& [k,v] : trie.m_branches )
            co_yield { TERM( k ), v };
    }

    //--------------------------------------------------------------------------------------------
    // Containers
    //--------------------------------------------------------------------------------------------
    template< typename U >
    static Generator< pair< Term, const U& > > Enumerate( Vector&& vec, const TrieContainerBranch_t& contBranch )
    {
        if( contBranch.index() == 0 )
        {
            for( auto&& [t, payload] : Enumerate( get< 0 >( contBranch )->m_trie ) )
            {
                auto newVec = Vector::MakeAppend( vec, t );
                co_yield Enumerate< U >( move( newVec ), payload );
            }
        }
        else
            co_yield { TERM( make_shared< Vector >( move( vec ) ) ), *any_cast< U >( &get< 1 >( contBranch ) ) };
    }

    template< typename U >
    static Generator< pair< Term, const U& > > Enumerate( const TrieNode< pvec, U >& trie )
    {
        for( auto&& [k, branch] : trie.m_fixedLengthBranches )
            co_yield Enumerate< U >( Vector(), branch );

        using rpt_node = ptr< TrieContainerRepetitionNode< U > >;
        for( auto&& [k, branch] : trie.m_variableLengthBranches )
        {
            for( auto&& [vecTerm, pRptNode] : Enumerate< rpt_node >( Vector(), branch ) )
            {
                for( auto&& [repetitionTerm, payload] : Enumerate< U >( pRptNode->m_repetition ) )
                {
                    auto pNewVec = make_shared< Vector >( *get< pvec >( vecTerm.content() ) );
                    pNewVec->setRepetitionTerm( move( repetitionTerm ) );
                    co_yield { TERM( move( pNewVec ) ), payload };
                }
            }
        }
    }

    //--------------------------------------------------------------------------------------------
    // Terms
    //--------------------------------------------------------------------------------------------
    template< size_t I, typename U >
    static Generator< pair< Term, const U& > > Enumerate( const Trie< U >& trie )
    {
        if( const auto& ptr = get< I >( trie.branches() ) )
            co_yield Enumerate( *ptr );

        if constexpr( ( I + 1 ) < tuple_size_v< typename Trie< U >::BranchesTuple > )
            co_yield Enumerate< I + 1 >( trie );
    }

    template< typename U >
    static Generator< pair< Term, const U& > > Enumerate( const Trie< U >& trie )
    {
        return Enumerate< 0 >( trie );
    }
}

#endif
