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

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

Parser Parser::makeNestedParser()
{
    Parser p( m_resolver );
    p.m_introDelimiter = m_introDelimiter;
    return p;
}

Parser Parser::makeNestedParser( Delimiter introDelimiter )
{
    Parser p( m_resolver, introDelimiter );
    return p;
}

// Parse a sequence of expression. Each expression is
// a statement.
void Parser::parseSequence()
{
    for(;;)
    {
        CodeBuilder::LifetimeScopeGuard lsg( context() );

        {
            // Each statement gets its own visibility scope,
            // so that vars defined inside of the statement are only
            // visible from within it.
            // However, they also have a lifetime scope, which
            // is separate because we need to flush the value
            // generated by the statement before destroying any
            // temporary that it may depend on.
            // However, the visibility rule must end before that
            // since local variable declarations (which must remain
            // visible for following statements) are made visible
            // when the decl or localvar is flushed.
            VisibilityScope vs( *this );

            if( !parseExpression( 0 ) )
                break;
        }

        flushValue();
    }

    flushValue();
}

bool Parser::parseExpression( uint32_t precedence )
{
    auto next = m_resolver->lookAhead();
    if( !next )
        return false;

    if( !parsePrefix( next->first, precedence ) )
        return false;

    parsePostfixExpression( precedence );
    return true;
}

void Parser::parsePostfixExpression( uint32_t precedence )
{
    while( auto next = m_resolver->lookAhead() )
    {
        auto prec = getPrecedence( next->first );
        if( !prec || precedence > *prec )
            break;

        if( !parseInfix( next->first, *prec ) )
            break;
    }
}

void Parser::flushValue()
{
    const auto& cb = context().codeBuilder();
    if( m_lastValue && cb && cb->cfg() && ( !cb->cfg()->currentBB() || cb->cfg()->currentBB()->terminator() )  )
    {
        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
            m_lastValue->locationId(), "unreachable code.", 0 );
    }

    // Flush the pending value, by invoking the DropValue
    // extension point, where an overload will decide
    // of the value's fate (or possibly emit an error
    // if that value wasn't allowed to be discarded).
    while( m_lastValue )
    {
        auto val = move( *m_lastValue );
        m_lastValue = nullopt;

        if( val.isPoison() )
            return;

        DiagnosticsContext dc( val.locationId(), "When invoking DropValue." );
        InvokeOverloadSet( context(), context().env()->extDropValue(),
            MakeTuple( val ) );
    }
}

optional< uint32_t > Parser::getPrecedence( const Term& t )
{
    return visit( [&]( auto&& content )
    {
        return getPrecedence( t, content );
    }, t );
}

bool Parser::parsePrefix( const Term& t, uint32_t precedence )
{
    return visit( [&]( auto&& content )
    {
        return parsePrefix( content, precedence );
    }, t );
}

bool Parser::parseInfix( const Term& t, uint32_t precedence )
{
    return visit( [&]( auto&& content )
    {
        return parseInfix( content, precedence );
    }, t );
}

bool Parser::parsePrefix( const BigInt& intlit, uint32_t )
{
    auto tok = m_resolver->consume();
    pushValue( ToValue( intlit ).setLocationId( tok->second ) );
    return true;
}

bool Parser::parsePrefix( uint64_t charlit, uint32_t )
{
    auto tok = m_resolver->consume();
    pushValue( ToValue( static_cast< char32_t >( charlit ) ).setLocationId( tok->second ) );
    return true;
}

bool Parser::parsePrefix( const string& strlit, uint32_t )
{
    auto tok = m_resolver->consume();
    pushValue( ToValue( strlit ).setLocationId( tok->second ) );
    return true;
}

// Standalone (ie not part of a grammar construct that expects them such as a decl), unresolved identifiers
// end up here.
bool Parser::parsePrefix( StringId strid, uint32_t )
{
    auto tok = m_resolver->consume();
    DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( tok->second,
        "undefined identifier.", 0 );
    return true;
}

// Infix identifiers: if the left value is a type, then we have a decl.
optional< uint32_t > Parser::getPrecedence( const Term&, StringId strid )
{
    const auto& leftVal = peekLastValue();
    if( !leftVal )
        return nullopt;

    // If leftVal is a type, this is a decl, so set the precedence to Application.
    if( IsTExpr( *leftVal ) || IsType( context(), *leftVal ) )
    {
        // If it is a regular decl (such as function parameters and variable declarations),
        // it have the "Decl" precedence.
        // However, if the identifier is followed by a parent block,
        // this is a function declaration, and it gets the "FuncDecl" precedence, which
        // is set to be lower than the comma operator.
        //
        // The reason for this is that while we want to construct tuples of about anything just
        // by separating things with commas (not only to build function parameters and function arguments
        // but also for multiple return values and multiple variable declarations), we also
        // want to be able to use a tuple of those things as the return type for a function,
        // so the construction of the tuple (the comma operator) needs a higher precedence than
        // the function declaration. In other words, we don't want the last type of a tuple of type to
        // become the return type of a function subsequently added to the tuple.

        auto next = m_resolver->lookAheadUnresolved( 1 );
        if( !next )
            return precedence::Decl;

        auto decomp = Decompose( next->first, Val< Delimiter >() );
        if( !decomp || *decomp != Delimiter::OpenParen )
            return precedence::Decl;

        return precedence::FuncDecl;
    }

    return nullopt;
}

// An unbound infix identifier
bool Parser::parseInfix( StringId strid, uint32_t prec )
{
    const auto& leftVal = peekLastValue();
    if( !leftVal )
        return parsePrefix( strid, prec );

    auto nameTerm = m_resolver->consume();
    const auto* name = get_if< StringId >( &nameTerm->first );

    // If the identifier is preceded by a TExpr or a Type,
    // then this becomes a TDecl or a Decl, respectively.
    if( IsTExpr( *leftVal ) )
    {
        auto loc = Location::CreateSpanningLocation( leftVal->locationId(), nameTerm->second );

        auto texpr = ValueToEIR( *popType() );
        pushValue( ToValue( TNamedDecl( move( texpr ), *name ) ).setLocationId( loc ) );
        return true;
    }
    else if( IsType( context(), *leftVal ) )
    {
        auto loc = Location::CreateSpanningLocation( leftVal->locationId(), nameTerm->second );

        auto type = ValueToEIR( *popType() );
        pushValue( ToValue( Decl( move( type ), *name ) ).setLocationId( loc ) );
        return true;
    }

    return parsePrefix( strid, prec );
}

bool Parser::parsePrefix( const pvec& vec, uint32_t prec )
{
    auto t = *m_resolver->lookAhead();

    auto val = EIRToValue( t.first );
    if( !val )
        return false;

    if( val->isPoison() )
        DiagnosticsManager::GetInstance().setCurrentVerbosityLevel( Verbosity::Silent );

    // If the term is a prefix rule value, invoke its parsePrefix() function.
    auto rule = FromValue< Rule >( *val );
    if( !rule )
    {
        m_resolver->consume();
        pushValue( Value( move( *val ) ).setLocationId( t.second ) );
        return true;
    }

    if( !( *rule )->isPrefix() )
        return false;

    m_resolver->consume();
    return ( *rule )->parsePrefix( *this, t.second, prec );
}

optional< uint32_t > Parser::getPrecedence( const Term& t, const pvec& vec )
{
    auto val = EIRToValue( t );
    if( !val )
        return nullopt;

    if( val->type() == GetValueType< ptr< OverloadSet > >() )
    {
        if( !peekLastValue() )
            return nullopt;

        if( ( !IsTExpr( *peekLastValue() ) || IsTFunc( *peekLastValue() ) ) && !IsType( context(), *peekLastValue() ) )
            return nullopt;

        return getInfixOverloadSetPrecedence();
    }

    // If the term is an infix rule value, invoke its getPrecedence() function.
    auto rule = FromValue< Rule >( *val );
    if( !rule )
        return nullopt;

    if( !( *rule )->isInfix() )
        return nullopt;

    return ( *rule )->getPrecedence( *this );
}

bool Parser::parseInfix( const pvec& vec, uint32_t prec )
{
    auto t = *m_resolver->lookAhead();

    auto val = EIRToValue( t.first );
    if( !val )
        return false;

    if( val->isPoison() )
        DiagnosticsManager::GetInstance().setCurrentVerbosityLevel( Verbosity::Silent );

    if( val->type() == GetValueType< ptr< OverloadSet > >() )
        return parseInfixOverloadSet( *FromValue< ptr< OverloadSet > >( *val ), prec );

    // If the term is an infix rule value, invoke its parseInfix() function.
    auto rule = FromValue< Rule >( *val );
    if( !rule )
        return false;

    m_resolver->consume();

    if( !( *rule )->isInfix() )
        return false;

    return ( *rule )->parseInfix( *this, t.second, prec );
}

optional< Value > Parser::popType()
{
    if( !m_lastValue )
        return nullopt;

    auto typeVal = *popValue();

    if( !typeVal.isConstant() )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( typeVal.locationId(),
            "this type expression can't be evaluated here.", 0 );
        return PoisonType();
    }

    return typeVal;
}
