#include "builtins/builtins.h"
#include "builtins/helpers.h"

using namespace goose::parse;

namespace goose::builtins
{
    void RegisterRule( sema::Env& env, const StringId& name, parse::Rule&& rule )
    {
        parse::RegisterRule( env, AppendToVectorTerm( RootIdentity(), TERM( name ) ), move( rule ) );
    }

    ptr< llr::BasicBlock > ParseSubStatement( Parser& p, uint32_t precedence )
    {
        auto next = p.resolver()->lookAheadUnresolved();
        if( !next )
            return nullptr;

        auto np = p.makeNestedParser();
        auto bb = np.cfg()->createBB();
        np.cfg()->setCurrentBB( bb );

        auto decomp = Decompose( next->first, Val< Delimiter >() );
        if( !decomp || *decomp != Delimiter::OpenBrace )
        {
            // This is not a braced statement: create a new identity and visibility rules
            // so that anything defined by the statement isn't visible outside of it.
            // If it is a braced statement, the block will be parsed as a nested brace block
            // and this will be handled for us.
            Scope s( np );

            if( !np.parseExpression( precedence ) )
                return nullptr;
        }
        else if( !np.parseExpression( precedence ) )
            return nullptr;

        np.flushValue();
        p.cfg()->setCurrentBB( np.cfg()->currentBB() );
        return bb;
    }

    variant< Value, ValUnifyError > ConvertValueToType( const Context& c, const Value& val, const Term& type )
    {
        auto valTerm = ValueToIRExpr( val );
        auto paramPat = ParamPat( type );

        optional< UnificationContext > bestUC;
        optional< Term > bestSol;
        bool ambiguous = false;

        for( auto&& [s, uc] : FullUnify( paramPat, valTerm, c ) )
        {
            if( !bestSol || uc.score() > bestUC->score() )
            {
                bestUC = uc;
                bestSol = s;
                ambiguous = false;
                continue;
            }

            if( uc.score() < bestUC->score() )
                continue;

            ambiguous = true;
        }

        if( ambiguous )
            return ValUnifyError::Ambiguous;

        if( !bestSol )
            return ValUnifyError::NoSolution;

        auto ppResultVal = Postprocess( *bestSol, *bestUC );
        if( !ppResultVal )
            return ValUnifyError::NoSolution;

        auto finalVal = ValueFromIRExpr( *ppResultVal );
        assert( finalVal );

        return *finalVal;
    }
}
