#include "sema.h"

namespace empathy::sema
{
    Term WrapWithPostprocFunc( const Term& t, const ptr< PostProcFunc >& pp )
    {
        return TVEC( TSID( postproc ),
            TERM( static_pointer_cast< void >( pp ) ),
            t );
    }

    Term WrapWithPostprocFunc( const Term& t, PostProcFunc&& pp )
    {
        return TVEC( TSID( postproc ),
            TERM( static_pointer_cast< void >( make_shared< PostProcFunc >( move( pp ) ) ) ),
            t );
    }

    optional< pair< Term, ptr< PostProcFunc > > > UnwrapPostprocFunc( const Term& ppt )
    {
        auto result = Decompose( ppt,
            Vec(
                Lit( "postproc"_sid ),
                Val< ptr< void > >(),
                SubTerm()
            )
        );

        if( !result )
            return nullopt;

        auto&& [pPP, t] = *result;
        return make_pair( t, static_pointer_cast< PostProcFunc >( pPP ) );
    }

    optional< Term > Postprocess( const Term& src, UnificationContext& uc )
    {
        if( !holds_alternative< pvec >( src.content() ) )
            return src;

        if( auto optPP = UnwrapPostprocFunc( src ) )
        {
            auto val = Postprocess( optPP->first, uc );
            if( !val )
                return nullopt;

            return ( *optPP->second )( *val, uc );
        }

        const auto& vec = *get< pvec >( src.content() );
        immer::vector< Term > outputTerms;
        auto vt = outputTerms.transient();
        bool faulty = false;

        for( auto&& t : vec.terms() )
        {
            auto newT = Postprocess( t, uc );
            if( !newT )
                return nullopt;

            faulty = faulty || newT->isFaulty();
            vt.push_back( move( *newT ) );
        }

        return Term( src.location(), make_shared< Vector >( vt.persistent(), faulty ) );
    }
}
