/**
 * @file TestMedium.cpp
 * @date May 5, 2010
 * @author ron
 *
 * @brief
 */


#include "TestProtocol.h"
#include "../TestBase/TestBase.h"

/// @cond DOXYGEN_SKIP
SUITE( Protocol )
{
/// @endcond

	/**
	 * @struct MediumFixture
	 * @brief Helper struct providing set-up/tear-down of Medium tests
	 *
	 * @copydetails EventFixture
	 */
	struct MediumFixture
	{
		SuiteBase::SimulationTest sim;
		TestLogConsumer::Ptr log;
		MediumTest medium;
		data::TypeInfo type;

		MediumFixture()
		:	sim( "MediumTestSim" )
		,	log( TestLogConsumer::create() )
		,	medium( sim, "MediumTest" )
		,	type( typeid(Medium) )
		{
			sim.addConsumer( log );
		}
	};

	TEST_FIXTURE( MediumFixture, LinkInfo )
	{
		base::SimTime delay = 9;
		ErrorModelPtr em = ErrorModelTestSuccess::create();
		MediumTest::LinkInfo info( delay, em );

		CHECK_EQUAL( delay, info.delay_ );
		CHECK_EQUAL( em, info.errorModel_ );
	}

	TEST_FIXTURE( MediumFixture, TransmissionStart )
	{
		DeviceTest device( sim, "MediumTestDevice" );
		unsigned int count = device.getTransmissionCount();
		MediumTest::TransmissionStart event( sim, "MediumTestTransmissionStart", device );
		event.schedule();
		sim.step();

		CHECK_EQUAL( count + 1, device.getTransmissionCount() );
	}

	TEST_FIXTURE( MediumFixture, TransmissionEnd )
	{
		DeviceTest device( sim, "MediumTestDevice" );
		device.incTransmissionCount();
		unsigned int count = device.getTransmissionCount();
		CHECK_EQUAL( 1, count );

		MediumTest::TransmissionEnd event( sim, "MediumTestTransmissionEnd", device, PduPtr() );
		event.schedule();
		sim.step();
		CHECK_EQUAL( count - 1, device.getTransmissionCount() );
		CHECK( ! log->getTraceRecord( "receive", typeid(Device) ) );

		MediumTest::TransmissionEnd event2( sim, "MediumTestTransmissionEnd", device, PduPtr( new PduTest() ) );
		event2.schedule();
		sim.step();
		CHECK_EQUAL( 0, device.getTransmissionCount() );
		CHECK( log->getTraceRecord( "receive", typeid(Device) ) );
	}

	TEST_FIXTURE( MediumFixture, RegisterDevice )
	{
		DeviceTest device( sim, "MediumTestRegisterDevice" );
		const std::string& address = "Address";
		medium.registerDevice( address, device );
		CHECK( log->getTraceRecord( "set address", typeid(Device) ) );
		CHECK_EQUAL( address, device.getAddress() );
		CHECK( ! medium.getDevices().empty() );
		CHECK_EQUAL( &device, medium.getDevice( address ) );
	}

	TEST_FIXTURE( MediumFixture, AddLinkByPointer )
	{
		DeviceTest device1( sim, "MediumTestAddLinkPointer" );
		DeviceTest device2( sim, "MediumTestAddLinkPointer" );

		bool success = medium.addLink( &device1, &device1, 18 );
		CHECK( ! success );
		CHECK( log->getWarningRecord( "Medium::addLink(): the given devices are the same", type ) );

		ErrorModelPtr em( new ErrorModelTestSuccess() );
		base::SimTime delay = 17;
		medium.addLink( &device1, &device2, delay, em );
		const Medium::TopologyMap& topo = medium.getTopology();
		CHECK_EQUAL( (std::size_t) 1, topo.size() );
		CHECK_EQUAL( &device1, topo.begin()->first );
		CHECK_EQUAL( &device2, topo.begin()->second.begin()->first );

		const MediumTest::LinkInfo& link = topo.begin()->second.begin()->second;
		CHECK_EQUAL( delay, link.delay_ );
		CHECK_EQUAL( em, link.errorModel_ );
	}

	TEST_FIXTURE( MediumFixture, AddLinkByAddress )
	{
		DeviceTest device1( sim, "MediumTestAddLinkAddr" );
		DeviceTest device2( sim, "MediumTestAddLinkAddr" );
		medium.registerDevice( "addr1", device1 );
		medium.registerDevice( "addr2", device2 );
		ErrorModelPtr em( new ErrorModelTestFailure() );

		medium.addLink( "addr1", "addr2", 27, true, em );
		const Medium::TopologyMap& topo = medium.getTopology();
		CHECK_EQUAL( (std::size_t) 2, topo.size() );
		CHECK_EQUAL( &device2, topo.find( &device1 )->second.begin()->first );
		CHECK_EQUAL( &device1, topo.find( &device2 )->second.begin()->first );
		CHECK( log->getTraceRecord( "add link", type ) );
	}

	TEST_FIXTURE( MediumFixture, RemoveLinkByPointer )
	{
		DeviceTest device1( sim, "MediumTestRemoveLinkPointer" );
		DeviceTest device2( sim, "MediumTestRemoveLinkPointer" );

		medium.removeLink( &device1, &device1 );
		CHECK( log->getWarningRecord( "Medium::removeLink(): the given devices are the same", type ) );

		medium.addLink( &device1, &device2, 18 );
		medium.removeLink( &device1, &device2 );

		CHECK( medium.getTopology().find( &device1 )->second.empty() );
	}

	TEST_FIXTURE( MediumFixture, RemoveLinkByAddress )
	{
		DeviceTest device1( sim, "MediumTestAddLinkPointer" );
		DeviceTest device2( sim, "MediumTestAddLinkPointer" );
		medium.registerDevice( "addr1", device1 );
		medium.registerDevice( "addr2", device2 );
		medium.addLink( "addr1", "addr2", 27, true );

		medium.removeLink( "addr1", "addr2", true );
		CHECK( log->getTraceRecord( "remove link", type ) );

		CHECK( medium.getTopology().find( &device1 )->second.empty() );
		CHECK( medium.getTopology().find( &device2 )->second.empty() );
	}

	TEST_FIXTURE( MediumFixture, SignalTransmissionStart )
	{
		DeviceTest device1( sim, "MediumTestSignalStart" );
		DeviceTest device2( sim, "MediumTestSignalStart" );
		unsigned int count = device2.getTransmissionCount();
		base::SimTime delay = 17;
		medium.addLink( &device1, &device2, delay );
		medium.signalTransmissionStart( &device1 );
		sim.run();
		CHECK_EQUAL( delay, sim.getTime() );

		CHECK_EQUAL( count + 1, device2.getTransmissionCount() );
		CHECK( log->getTraceRecord( "increase transmission count", typeid(Device) ) );
	}

	TEST_FIXTURE( MediumFixture, SignalTransmissionEnd )
	{
		DeviceTest device1( sim, "MediumTestSignalEnd" );
		DeviceTest device2( sim, "MediumTestSignalEnd" );
		device2.incTransmissionCount();
		unsigned int count = device2.getTransmissionCount();
		base::SimTime delay = 17;
		medium.addLink( &device1, &device2, delay );
		PduPtr p( new PduTest( 100 ) );
		medium.signalTransmissionEnd( &device1, p );
		sim.run();
		CHECK_EQUAL( delay, sim.getTime() );

		CHECK( device2.receivedPdu );
		CHECK_EQUAL( 100, device2.receivedPdu->value );
		CHECK_EQUAL( count - 1, device2.getTransmissionCount() );
		CHECK( log->getTraceRecord( "decrease transmission count", typeid(Device) ) );
		CHECK( log->getTraceRecord( "receive", typeid(Device) ) );
	}

/// @cond DOXYGEN_SKIP
}
/// @endcond
