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

using namespace goose;
using namespace goose::builtins;

namespace goose::builtins
{
    void SetupRuntimePointerType( Env& e )
    {
        // null pointer literal
        e.storeValue( AppendToVectorTerm( RootIdentity(), TSID( nullptr ) ), ANYTERM( _ ), ValueToEIR( ToValue( NullPointer() ) ) );

        RegisterBuiltinFunc< Eager< Value > ( Value ) >( e, "pointer"_sid,
            []( const Value& pointedType )
            {
                if( !GetLLVMType( pointedType ) )
                {
                    // TODO come up with some lightweight builtin option type
                    // for the builtin apis, because this is a very bullshit
                    // way to handle errors
                    return ToValue( "error"s );
                }

                return ToValue( PointerType( ValueToEIR( pointedType ) ) );
            } );
    }

    llvm::Type* GetLLVMType( const PointerType& p )
    {
        return llvm::PointerType::getUnqual( GetLLVMType( *ValueFromEIR( p.m_pointedType ) ) );
    }
}

namespace goose::eir
{
    Value Bridge< PointerType >::ToValue( const PointerType& p )
    {
        return Value( Type(), MkStdRTType( TSID( pointer ),
            GetLLVMType( p ),
            p.m_pointedType ) );
    }

    optional< PointerType > Bridge< PointerType >::FromValue( const Value& v )
    {
        auto result = Decompose( v.val(),
            Vec(
                Lit( "pointer"_sid ),
                Val< ptr< void > >(),
                Val< void* >(),
                SubTerm()
            )
        );

        if( !result )
            return nullopt;

        auto&& [predicates, llvmType, pointedType] = *result;
        return PointerType( pointedType );
    }

    const Value& Bridge< NullPointerType >::ToValue( const NullPointerType& np )
    {
        static auto val = Value( Type(), TSID( nullptr ) );
        return val;
    }

    optional< NullPointerType > Bridge< NullPointerType >::FromValue( const Value& v )
    {
        auto result = Decompose( v.val(),
            Lit( "nullptr"_sid )
        );

        if( !result )
            return nullopt;

        return NullPointerType();
    }

    const Term& Bridge< NullPointer >::Type()
    {
        static auto type = ValueToEIR( eir::ToValue( NullPointerType() ) );
        return type;
    }

    const Value& Bridge< NullPointer >::ToValue( const NullPointer& np )
    {
        static auto val = Value( Type(), TSID( nullptr ) );
        return val;
    }

    optional< NullPointer > Bridge< NullPointer >::FromValue( const Value& v )
    {
        if( !FromValue< NullPointerType >( *ValueFromEIR( v.type() ) ) )
            return nullopt;

        auto result = Decompose( v.val(),
            Lit( "nullptr"_sid )
        );

        if( !result )
            return nullopt;

        return NullPointer();
    }
}
