#include "builtins/builtins.h"
#include "parse/parse.h"
#include "precedence.h"

using namespace goose;
using namespace goose::eir;
using namespace goose::parse;

namespace goose::builtins
{
    void SetupUsingStmt( Env& e )
    {
        auto handleUsing = []( Parser& p, uint32_t locationId, uint32_t prec )
        {
            auto& dm = DiagnosticsManager::GetInstance();

            auto nameTerm = p.resolver()->consume();
            if( !nameTerm )
            {
                dm.emitSyntaxErrorMessage( p.resolver()->currentLocation(), "expected an identifier.", 0 );
                return false;
            }

            const auto* name = get_if< StringId >( &nameTerm->first );
            if( !name )
            {
                dm.emitSyntaxErrorMessage( nameTerm->second, "expected an identifier.", 0 );
                return false;
            }

            auto& context = p.context();
            auto localIdentity = DuplicateVectorTerm( context.identity() );
            get< pvec >( localIdentity )->terms().back() = nameTerm->first;
            context.env()->addVisibilityRule( context.identity(), localIdentity );

            auto eqTerm = p.resolver()->consumeUnresolved();
            if( !eqTerm )
            {
                dm.emitSyntaxErrorMessage( p.resolver()->currentLocation(), "expected '='.", 0 );
                context.env()->storeValue( localIdentity, ANYTERM( _ ), ValueToIRExpr( PoisonValue() ) );
                return true;
            }

            const auto* eq = get_if< StringId >( &eqTerm->first );
            if( !eq || *eq != "="_sid )
            {
                dm.emitSyntaxErrorMessage( eqTerm->second, "expected '='.", 0 );
                context.env()->storeValue( localIdentity, ANYTERM( _ ), ValueToIRExpr( PoisonValue() ) );
                return true;
            }

            auto np = p.makeNestedParser();
            if( !np.parseExpression( precedence::UsingStmt ) )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                    "expected an expression.", 0 );
                context.env()->storeValue( localIdentity, ANYTERM( _ ), ValueToIRExpr( PoisonValue() ) );
                return true;
            }

            if( !np.peekLastValue() )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                    "expected an expression.", 0 );
                context.env()->storeValue( localIdentity, ANYTERM( _ ), ValueToIRExpr( PoisonValue() ) );
                return true;
            }

            context.env()->storeValue( localIdentity, ANYTERM( _ ), ValueToIRExpr( *np.popValue() ) );
            return true;
        };

        Rule r( handleUsing );
        auto ruleVal = ToValue( move( r ) );
        auto ruleTerm = ValueToIRExpr( ruleVal );
        e.storeValue( AppendToVectorTerm( RootIdentity(), TSID( using ) ), ANYTERM( _ ), ruleTerm );
    }
}
