#include "diagnostics.h"

namespace goose::diagnostics
{
    unordered_set< string > Location::ms_filenames;
    vector< Location >      Location::ms_locations;

    uint32_t LocationPoint::toLoc( uint32_t length ) const
    {
        return Location::Create( *this, length );
    }

    uint32_t LocationPoint::toLoc( const LocationPoint& end ) const
    {
        return Location::Create( *this, end.m_offset - m_offset );
    }

    const string& Location::GetCachedFilename( const string& filename )
    {
        return *ms_filenames.insert( filename ).first;
    }

    optional< Location > Location::Get( uint32_t locationId )
    {
        // 0 is used for values that don't have a location
        if( locationId == 0 )
            return nullopt;

        --locationId;
        if( locationId >= ms_locations.size() )
            return nullopt;

        return ms_locations[locationId];
    }

    uint32_t Location::CreateSpanningLocation( uint32_t loc1, uint32_t loc2 )
    {
        if( loc1 == ~0 || loc2 == ~0 )
            return ~0;

        auto l1 = Get( loc1 );
        auto l2 = Get( loc2 );

        if( !l1 || !l2 )
            return 0;

        assert( l1->filename() == l2->filename() );

        auto offset = min( l1->offset(), l2->offset() );
        auto end = max( l1->offset() + l1->length(), l2->offset() + l2->length() );
        auto length = end - offset;
        auto line = min( l1->line(), l2->line() );
        auto col = l1->offset() < l2->offset() ? l1->column() : l2->column();

        return Create( l1->filename(), offset, length, line, col );
    }

    bool Location::Overlaps( uint32_t loc1, uint32_t loc2 )
    {
        if( loc1 == ~0 || loc2 == ~0 )
            return false;

        auto l1 = Get( loc1 );
        auto l2 = Get( loc2 );

        if( !l1 || !l2 )
            return false;

        if( l1->filename() != l2->filename() )
            return false;

        if( l1->line() != l2->line() )
            return false;

        auto l1end = l1->column() + l1->length();
        auto l2end = l2->column() + l2->length();

        if( l1end < l2->column() )
            return false;

        if( l2end < l1->column() )
            return false;

        return true;
    }
}
