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

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

namespace goose::builtins
{
    void SetupWhereOp( Env& e )
    {
        auto wherePrecedence = []( const Parser& p ) -> optional< uint32_t >
        {
            auto lastVal = p.peekLastValue();
            if( !lastVal )
                return nullopt;

            if( !IsType( p.context(), *lastVal ) )
                return nullopt;

            if( !GetTypePredicates( *lastVal ) )
                return nullopt;

            return precedence::VerificationPredicate;
        };

        auto parseWhere = []( Parser& p, LocationId locationId, uint32_t prec )
        {
            auto& dm = DiagnosticsManager::GetInstance();

            auto next = p.resolver()->lookAheadUnresolved();
            if( !next )
            {
                dm.emitSyntaxErrorMessage( p.resolver()->currentLocation(), "'[' expected after 'where'.", 0 );
                return false;
            }

            auto decomp = Decompose( next->first, Val< Delimiter >() );
            if( !decomp || *decomp != Delimiter::OpenBracket )
            {
                dm.emitSyntaxErrorMessage( p.resolver()->currentLocation(), "'[' expected after 'where'.", 0 );
                return false;
            }

            auto type = *p.popValue();
            auto preds = *GetTypePredicates( type );
            if( preds )
                preds = make_shared< TypePredicates >( *preds );
            else
            {
                preds = make_shared< TypePredicates >();
                auto& c = p.context();

                // Create a new identity for the predicates, which imports everything from the parent identity
                // and also makes @val visible as a placeholder for the type's value.
                preds->m_identity = VEC( Env::NewUniqueId() );
                c.env()->addVisibilityRule( c.identity(), preds->m_identity );

                auto typeTerm = ValueToEIR( type );

                auto name = "@val"_sid;
                auto valuePlaceholderIdentity = AppendToVectorTerm( preds->m_identity, TERM( name ) );
                c.env()->storeValue( valuePlaceholderIdentity, ANYTERM( _ ),
                    ValueToEIR( BuildComputedValue( typeTerm, cir::Placeholder( typeTerm, name ) ) ) );
            }

            preds->m_unparsedPredicates.emplace_back();
            auto& toks = preds->m_unparsedPredicates.back();

            auto g = p.resolver()->consumeUnit();

            // Strip the start and end brackets
            auto beg = g.begin();
            advance( beg, 1 );
            move( beg, g.end(), back_inserter( toks ) );
            auto loc = Location::CreateSpanningLocation( type.locationId(), toks.back().second );
            toks.resize( toks.size() - 1 );

            p.pushValue( Value( type.type(), InjectPredicatesIntoStdType( type.val(), preds ) )
                .setLocationId( loc ) );
            return true;
        };

        RegisterRule( e, "where"_sid, Rule( wherePrecedence, parseWhere ) );
    }
}
