#include "sema.h"

using namespace goose;
using namespace goose::sema;

TypeCheckingContext::TypeCheckingContext( const Context& c ) :
    m_context( c )
{}

TypeCheckingContext::TypeCheckingContext( Context&& c ) :
    m_context( move( c ) )
{}

uint32_t TypeCheckingContext::getLHSHoleIndex( StringId name ) const
{
    if( name == "_"_sid )
        return InvalidIndex;

    HoleName holeName( name, m_currentLHSNamespaceIndex );

    auto it = m_pCow->holeDict.find( holeName );
    if( it == m_pCow->holeDict.end() )
        return InvalidIndex;

    return it->second;
}

uint32_t TypeCheckingContext::getRHSHoleIndex( StringId name ) const
{
    if( name == "_"_sid )
        return InvalidIndex;

    HoleName holeName( name, m_currentRHSNamespaceIndex );

    auto it = m_pCow->holeDict.find( holeName );
    if( it == m_pCow->holeDict.end() )
        return InvalidIndex;

    return it->second;
}

uint32_t TypeCheckingContext::createValue( bool required )
{
    CoW( m_pCow )->values.emplace_back( StoredValue{ nullopt, required } );
    uint32_t index = m_pCow->values.size() - 1;
    if( required )
        ++m_numUnknownValues;
    return index;
}

void TypeCheckingContext::setValueRequired( uint32_t index )
{
    assert( m_pCow->values.size() > index );

    if( m_pCow->values[index].m_required )
        return;

    CoW( m_pCow )->values[index] = { m_pCow->values[index].m_term, true };

    if( !m_pCow->values[index].m_term )
        ++m_numUnknownValues;
}

void TypeCheckingContext::setLHSHoleIndex( StringId name, uint32_t index )
{
    if( name == "_"_sid )
    {
        ++m_numAnonymousHoles;
        return;
    }

    HoleName holeName( name, m_currentLHSNamespaceIndex );
    CoW( m_pCow )->holeDict.emplace( holeName, index );

    if( m_valuesAreRequired )
        setValueRequired( index );
}

void TypeCheckingContext::setRHSHoleIndex( StringId name, uint32_t index )
{
    if( name == "_"_sid )
    {
        ++m_numAnonymousHoles;
        return;
    }

    HoleName holeName( name, m_currentRHSNamespaceIndex );
    CoW( m_pCow )->holeDict.emplace( holeName, index );

    if( m_valuesAreRequired )
        setValueRequired( index );
}

void TypeCheckingContext::eraseLHSName( StringId name )
{
    HoleName holeName( name, m_currentLHSNamespaceIndex );
    CoW( m_pCow )->holeDict.erase( holeName );
}

void TypeCheckingContext::eraseRHSName( StringId name )
{
    HoleName holeName( name, m_currentRHSNamespaceIndex );
    CoW( m_pCow )->holeDict.erase( holeName );
}

bool TypeCheckingContext::isHoleLocked( uint32_t index ) const
{
    return m_pCow->lockedHoles.find( index ) != m_pCow->lockedHoles.end();
}

void TypeCheckingContext::lockHole( uint32_t index )
{
    CoW( m_pCow )->lockedHoles.emplace( index );
}

void TypeCheckingContext::unlockHole( uint32_t index )
{
    CoW( m_pCow )->lockedHoles.erase( index );
}

#ifndef NDEBUG
void TypeCheckingContext::TCRuleTrace( const TCRuleInfo* pRule ) const
{
    CoW( m_pCow )->currentTypeCheckingTrace.emplace_back( pRule );
}

void TypeCheckingContext::PushRuleTraceForParam() const
{
    auto* pCow = CoW( m_pCow ).get();
    pCow->paramsTypeCheckingTrace.emplace_back( move( pCow->currentTypeCheckingTrace ) );
}

void TypeCheckingContext::DumpParamsTraces( ostream& out ) const
{
    uint32_t i = 0;

    for( auto&& x : m_pCow->paramsTypeCheckingTrace )
    {
        out << "== TypeChecking trace for param #" << i++ << endl;
        for( auto&& y : x )
            cout << "   " << y->pFilename << ':' << y->line << endl;
    }
}
#endif
