#include "builtins/builtins.h"

using namespace empathy;
using namespace empathy::ir;

namespace empathy::builtins
{
    Value MkTupleType( const Term& state, const Term& types )
    {
        return Value( TSID( type ), TVEC( TSID( tuple ), state, types ) );
    }

    const Value& EmptyTupleType()
    {
        static auto type = MkTupleType( TSID( opened ), TVEC() );
        return type;
    }

    const Value& EmptyClosedTupleType()
    {
        static auto type = MkTupleType( TSID( closed ), TVEC() );
        return type;
    }

    const Value& EmptyTuple()
    {
        static auto val = Value( ValueToIRExpr( EmptyTupleType() ), TVEC() );
        return val;
    }

    const Value& EmptyClosedTuple()
    {
        static auto val = Value( ValueToIRExpr( EmptyClosedTupleType() ), TVEC() );
        return val;
    }

    Value AppendToTupleType( const Value& tuptype, const Value& type )
    {
        auto decomp = Decompose( tuptype.val(),
            Vec(
                Lit( "tuple"_sid ),
                SubTerm(),
                SubTerm()
            )
        );

        auto&& [tupState, tupTypesVec] = *decomp;
        auto newTypeVec = AppendToVectorTerm( tupTypesVec, ValueToIRExpr( type ) );
        return MkTupleType( tupState, newTypeVec );
    }

    Value ToClosedTupleType( const Value& tuptype )
    {
        auto decomp = Decompose( tuptype.val(),
            Vec(
                Lit( "tuple"_sid ),
                SubTerm(),
                SubTerm()
            )
        );

        auto&& [tupState, tupTypesVec] = *decomp;
        return MkTupleType( TSID( closed ), tupTypesVec );
    }

    Value AppendToTuple( const Value& tup, const Value& val )
    {
        auto tupType = ValueFromIRExpr( tup.type() );
        auto valType = ValueFromIRExpr( val.type() );

        return Value(
            ValueToIRExpr( AppendToTupleType( *tupType, *valType ) ),
            AppendToVectorTerm( tup.val(), ValueToIRExpr( val ) ) );
    }

    bool IsTuple( const Value& t )
    {
        auto typeVal = ValueFromIRExpr( t.type() );
        auto result = Decompose( typeVal->val(),
            Vec(
                Lit( "tuple"_sid ),
                SubTerm(),
                SubTerm()
            )
        );

        return !!result;
    }

    bool IsOpenTuple( const Value& t )
    {
        if( !t.isConstant() )
            return false;

        auto typeVal = ValueFromIRExpr( t.type() );
        auto result = Decompose( typeVal->val(),
            Vec(
                Lit( "tuple"_sid ),
                Lit( "opened"_sid ),
                SubTerm()
            )
        );

        return !!result;
    }

    Value CloseTuple( const Value& tup )
    {
        auto tupType = ValueFromIRExpr( tup.type() );
        return Value( ValueToIRExpr( ToClosedTupleType( *tupType ) ),
            tup.val() );
    }

    size_t TupleSize( const Value& tup )
    {
        const auto& vec = *get< pvec >( tup.val().content() );
        return vec.terms().size();
    }
}
