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

using namespace goose;
using namespace goose::sema;
using namespace goose::builtins;

namespace goose::builtins
{
    void SetupBasicTypes( Env& e )
    {
        e.storeValue( AppendToVectorTerm( RootIdentity(), TSID( type ) ), ANYTERM( _ ), TypeType() );
        e.storeValue( AppendToVectorTerm( RootIdentity(), TSID( void ) ), ANYTERM( _ ), GetValueType< void >() );
        e.storeValue( AppendToVectorTerm( RootIdentity(), TSID( bool ) ), ANYTERM( _ ), GetValueType< bool >() );
        e.storeValue( AppendToVectorTerm( RootIdentity(), TSID( ct_int ) ), ANYTERM( _ ), GetValueType< BigInt >() );
        e.storeValue( AppendToVectorTerm( RootIdentity(), TSID( ct_char ) ), ANYTERM( _ ), GetValueType< char32_t >() );
        e.storeValue( AppendToVectorTerm( RootIdentity(), TSID( ct_string ) ), ANYTERM( _ ), GetValueType< string >() );

        // bool literals
        e.storeValue( AppendToVectorTerm( RootIdentity(), TSID( true ) ), ANYTERM( _ ), ValueToIRExpr( ToValue( true ) ) );
        e.storeValue( AppendToVectorTerm( RootIdentity(), TSID( false ) ), ANYTERM( _ ), ValueToIRExpr( ToValue( false ) ) );

        // _ and var are both aliases for an anonymous tvar ($_)
        e.storeValue( AppendToVectorTerm( RootIdentity(), TSID( _ ) ), ANYTERM( _ ), ValueToIRExpr( ToValue( TVar( "_"_sid ) ) ) );
        e.storeValue( AppendToVectorTerm( RootIdentity(), TSID( var ) ), ANYTERM( _ ), ValueToIRExpr( ToValue( TVar( "_"_sid ) ) ) );
    }

    const Term& ValuePatternT::GetPattern()
    {
        static auto pattern = HOLE( "T"_sid );
        return pattern;
    }
}

namespace goose::ir
{
    // void
    const Term& Bridge< void >::Type()
    {
        static auto type = ValueToIRExpr( Value( TypeType(), TSID( void ) ) );
        return type;
    }

    // bool
    const Term& Bridge< bool >::Type()
    {
        static auto type = ValueToIRExpr( Value( TypeType(), MkStdType( TSID( bool ) ) ) );
        return type;
    }

    Value Bridge< bool >::ToValue( bool x )
    {
        return Value( Type(), TERM( x ? 1U : 0U ) );
    }

    optional< bool > Bridge< bool >::FromValue( const Value& v )
    {
        if( v.type() != Type() )
            return nullopt;

        const auto* pint = get_if< uint32_t >( &v.val() );
        if( !pint )
            return nullopt;

        return *pint;
    }

    // Integers
    const Term& Bridge< BigInt >::Type()
    {
        static auto type = ValueToIRExpr( Value( TypeType(), MkStdType( TSID( ct_type ), TSID( ct_integer ) ) ) );
        return type;
    }

    const BigInt* Bridge< BigInt >::FromValue( const Value& v )
    {
        if( v.type() != Type() )
            return nullptr;

        return get_if< BigInt >( &v.val() );
    }

    // char
    const Term& Bridge< char32_t >::Type()
    {
        static auto type = ValueToIRExpr( Value( TypeType(), MkStdType( TSID( ct_type ), TSID( ct_char ) ) ) );
        return type;
    }

    Value Bridge< char32_t >::ToValue( char32_t x )
    {
        return Value( Type(), TERM( x ) );
    }

    optional< char32_t > Bridge< char32_t >::FromValue( const Value& v )
    {
        if( v.type() != Type() )
            return nullopt;

        return get< uint32_t >( v.val() );
    }

    // strings
    const Term& Bridge< string >::Type()
    {
        static auto type = ValueToIRExpr( Value( TypeType(), MkStdType( TSID( ct_type ), TSID( ct_string ) ) ) );
        return type;
    }

    Value Bridge< string >::ToValue( const string& x )
    {
        return Value( Type(), TERM( x ) );
    }

    const string* Bridge< string >::FromValue( const Value& v )
    {
        if( v.type() != Type() )
            return nullptr;

        return get_if< string >( &v.val() );
    }

    // Generic values
    const Term& Bridge< Value >::Type()
    {
        // This is a trick to get a generic signature for this type when creating a builtin function.
        // This way we can define a builtin function that take any value of any type as is, without conversion.
        static auto type = HOLE( "_"_sid );
        return type;
    }
}
