#include "builtins/builtins.h"

using namespace goose;
using namespace goose::sema;

bool OverloadSet::add( const Env& e, const Value& callee )
{
    auto pInvRule = GetInvocationRule( e, callee );
    assert( pInvRule );
    return add( e, callee, pInvRule );
}

bool OverloadSet::add( const Env& e, const Value& callee, const ptr< InvocationRule >& pInvRule )
{
    auto signature = pInvRule->getSignature( callee );
    assert( signature );

    auto sigDecomp = Decompose( *signature, Val< pvec >() );

    assert( sigDecomp );

    bool success = false;
    m_trie = m_trie->merge( *sigDecomp->get(),
        [&]( auto&& previous ) -> Overload
        {
            if( previous.callee )
                return move( previous );

            success = true;
            return { pInvRule, callee };
        } );

    m_resolutionCache.clear();
    return success;
}

OverloadSet::TCGen OverloadSet::typeCheck(
    const Term& callPat, const TypeCheckingContext& tcc ) const
{
    auto callDecomp = Decompose( callPat, Val< pvec >() );

    if( !callDecomp )
        co_return;

    for( auto&& [callVec, uniCallVec, ovl, tcc] : m_trie->typeCheck( *callDecomp->get(), tcc ) )
    {
        auto uniCall = TERM( make_shared< Vector >( uniCallVec ) );
        co_yield { move( uniCall ), ovl, tcc };
    }
}

optional< OverloadSet::Overload > OverloadSet::getResolutionFromCache( const Term& args ) const
{
    ProfileZoneScoped;

    auto it = m_resolutionCache.find( args );
    if( it == m_resolutionCache.end() )
        return nullopt;

    return it->second;
}

void OverloadSet::addResolutionToCache( const Term& args, const Overload& ovl )
{
    ProfileZoneScoped;
    m_resolutionCache.emplace( args, ovl );
}

Generator< const OverloadSet::Overload* > OverloadSet::enumerate() const
{
    return m_trie->enumerate();
}
