#ifndef TESTS_SRDPREPROCLOCATION_H_
#define TESTS_SRDPREPROCLOCATION_H_

#include <lw/lib/SRDPreproc.hh>
#include <lw/lib/SRDText.hh>
#include <lw/lib/SRDIO.hh>
#include <lw/lib/MemoryStreams.hh>
#include <cppunit/extensions/HelperMacros.h>
using namespace lw;


namespace {

inline T_SRDLocationChaining const* NextChain_(
		T_SRDLocation const* from ) noexcept
{
	return (from && from->isChained( ) ) ? &from->chaining( ) : nullptr;
}

uint32_t LocationDepth_(
		const RPC_SRDLocation location ) noexcept
{
	uint32_t depth( 0 );
	auto lc( NextChain_( location ) );
	while ( lc ) {
		depth ++;
		lc = NextChain_( RPC_SRDLocation( lc->location ) );
	}
	return depth;
}

uint32_t ErrorDepth_(
		T_SRDErrors const& errors ,
		const size_t index ) noexcept
{
	auto const& error( errors[ index ] );
	return LocationDepth_( &error.location( ) );
}

uint32_t TokenDepth_(
		T_SRDList const& tokens ,
		const size_t index ) noexcept
{
	auto const& tok( tokens[ index ] );
	assert( tok.hasLocation( ) );
	return LocationDepth_( &tok.location( ) );
}

T_SRDLocationChaining const& GetChain_(
		RPC_SRDLocation const& top ,
		uint32_t depth ) noexcept
{
	auto lc( NextChain_( top ) );
	assert( lc );
	while ( depth ) {
		depth --;
		lc = NextChain_( RPC_SRDLocation( lc->location ) );
		assert( lc );
	}
	return *lc;
}

void PrintLoc_( RPC_SRDLocation location ) noexcept
{
	printf( "at %s, l. %d c. %lu\n" , location->source( ).toOSString( ).data( ) ,
			location->line( ) , location->character( ) );
	if ( location->isChained( ) ) {
		auto const& chain( location->chaining( ) );
		printf( "\tChain type %d, depth %d, " ,
				int( chain.circumstances ) , chain.depth );
		PrintLoc_( RPC_SRDLocation( chain.location ) );
	}
}

}

#define M_CKTOKDEPTH_( IDX , DEPTH ) \
	CPPUNIT_ASSERT_EQUAL( uint32_t( DEPTH ) , TokenDepth_( output , IDX ) )

#define M_CKTOKLOC_( IDX , NAME , LINE , CHAR ) \
	do { \
		auto const& loc( output[ IDX ].location( ) ); \
		CPPUNIT_ASSERT( T_String{ NAME } == loc.source( ) ); \
		CPPUNIT_ASSERT_EQUAL( uint32_t( LINE ) , loc.line( ) ); \
		CPPUNIT_ASSERT_EQUAL( size_t( CHAR ) , loc.character( ) ); \
	} while ( 0 )

#define M_CKTOKFULL_( IDX , NAME , LINE , CHAR , DEPTH ) \
	do { \
		auto const& loc( output[ IDX ].location( ) ); \
		CPPUNIT_ASSERT( T_String{ NAME } == loc.source( ) ); \
		CPPUNIT_ASSERT_EQUAL( uint32_t( LINE ) , loc.line( ) ); \
		CPPUNIT_ASSERT_EQUAL( size_t( CHAR ) , loc.character( ) ); \
		M_CKTOKDEPTH_( IDX , DEPTH ); \
	} while ( 0 )

#define M_CKTOKCHAIN_( IDX , DEPTH , HOW , NAME , LINE , CHAR , REC ) \
	do { \
		auto const& chain( GetChain_( &output[ IDX ].location( ) , DEPTH ) ); \
		CPPUNIT_ASSERT( E_SRDLocationChaining::HOW == chain.circumstances ); \
		CPPUNIT_ASSERT_EQUAL( uint32_t( REC ) , chain.depth ); \
		CPPUNIT_ASSERT( T_String{ NAME } == chain.location->source( ) ); \
		CPPUNIT_ASSERT_EQUAL( uint32_t( LINE ) , chain.location->line( ) ); \
		CPPUNIT_ASSERT_EQUAL( size_t( CHAR ) , chain.location->character( ) ); \
	} while ( 0 )


#define M_CKERRDEPTH_( IDX , DEPTH ) \
	CPPUNIT_ASSERT_EQUAL( uint32_t( DEPTH ) , ErrorDepth_( errors , IDX ) )

#define M_CKERRFULL_( IDX , TEXT , DETAILS , NAME , LINE , CHAR , DEPTH ) \
	do { \
		CPPUNIT_ASSERT( T_String{ TEXT } == errors[ IDX ].error( ) ); \
		CPPUNIT_ASSERT_EQUAL( bool( DETAILS ) , errors[ IDX ].isDetails( ) ); \
		auto const& loc( errors[ IDX ].location( ) ); \
		CPPUNIT_ASSERT( T_String{ NAME } == loc.source( ) ); \
		CPPUNIT_ASSERT_EQUAL( uint32_t( LINE ) , loc.line( ) ); \
		CPPUNIT_ASSERT_EQUAL( size_t( CHAR ) , loc.character( ) ); \
		CPPUNIT_ASSERT_EQUAL( uint32_t( DEPTH ) , ErrorDepth_( errors , IDX ) ); \
	} while ( 0 )

#define M_CKERRCHAIN_( IDX , DEPTH , HOW , NAME , LINE , CHAR , REC ) \
	do { \
		auto const& chain( GetChain_( &errors[ IDX ].location( ) , DEPTH ) ); \
		CPPUNIT_ASSERT( E_SRDLocationChaining::HOW == chain.circumstances ); \
		CPPUNIT_ASSERT_EQUAL( uint32_t( REC ) , chain.depth ); \
		CPPUNIT_ASSERT( T_String{ NAME } == chain.location->source( ) ); \
		CPPUNIT_ASSERT_EQUAL( uint32_t( LINE ) , chain.location->line( ) ); \
		CPPUNIT_ASSERT_EQUAL( size_t( CHAR ) , chain.location->character( ) ); \
	} while ( 0 )


#endif // TESTS_SRDPREPROCLOCATION_H_