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

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

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

            auto np = p.makeNestedParser();
            if( !np.parseExpression( precedence::IfStmt ) )
            {
                dm.emitSyntaxErrorMessage( locationId, "expected an expression following the #if statement1.", 0 );
                return false;
            }

            auto condVal = np.popValue();
            if( !condVal )
            {
                dm.emitSyntaxErrorMessage( locationId, "expected an expression following the #if statement2.", 0 );
                return false;
            }

            const auto& context = p.context();
            auto converted = ConvertValueToType( context, *condVal, GetValueType< bool >() );
            if( holds_alternative< ValUnifyError >( converted ) )
            {
                switch( get< ValUnifyError >( converted ) )
                {
                    case ValUnifyError::NoSolution:
                        dm.emitErrorMessage( condVal->locationId(), "the #if condition doesn't evaluate to a bool constant." );
                        break;

                    case ValUnifyError::Ambiguous:
                        dm.emitErrorMessage( condVal->locationId(), "ambiguous #if condition bool conversion." );
                        break;
                }

                return false;
            }

            auto condBool = get< Value >( converted );

            if( !condBool.isConstant() )
            {
                dm.emitErrorMessage( condVal->locationId(), "the #if condition doesn't evaluate to a bool constant." );
                return false;
            }

            auto cond = FromValue< bool >( condBool );

            // If the cond is invalid, both blocks will be excluded, but parsing will continue,
            // to give the best chance of a graceful recovery of the parsing and hopefully
            // be able to detect more legit errors before we bail out.
            // We do mark the current diagnostics context as bust, however, since what we're going to
            // miss in the #if block that should have been parsed is likey to cause cascading errors
            // in the current scope.
            if( !cond )
                dm.emitSyntaxErrorMessage( condVal->locationId(), "the #if condition doesn't evaluate to a bool constant." );

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

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

            // Deal with the then block.
            if( cond && *cond )
            {
                // Condition is true: parse the 'then' block.
                if( !p.parseBraceBlock() )
                    return false;
            }
            else
            {
                // Condition is false: skip the 'then' block.
                auto gen = p.resolver()->consumeUnit();
                for_each( gen.begin(), gen.end(), []( auto&& ){} );
            }

            next = p.resolver()->lookAheadUnresolved();
            if( next )
            {
                const auto* nextSid = get_if< StringId >( &next->first );
                if( nextSid && *nextSid == "#else"_sid )
                {
                    p.resolver()->consumeUnresolved();

                    auto next = p.resolver()->lookAheadUnresolved();
                    auto decomp = Decompose( next->first, Val< Delimiter >() );
                    if( !decomp || *decomp != Delimiter::OpenBrace )
                    {
                        dm.emitSyntaxErrorMessage( p.resolver()->currentLocation(), "brace block expected.", 0 );
                        return false;
                    }

                    // Deal with the else block.
                    if( cond && !*cond )
                    {
                        // Condition is false: parse the 'else' block.
                        if( !p.parseBraceBlock() )
                            return false;
                    }
                    else
                    {
                        // Condition is true: skip the 'else' block.
                        auto gen = p.resolver()->consumeUnit();
                        for_each( gen.begin(), gen.end(), []( auto&& ){} );
                    }
                }
            }

            return true;
        };

        RegisterRule( e, "#if"_sid, Rule( handleHIf ) );
    }
}
