#include "StdAfx.h"
#include "Q_Routing_Policy.h"
#include "Q_Routing_QRoutingPolicy.h"
#include "Q_Routing_BellmanFordPolicy.h"
#include "Q_Routing_TestBlock.h"

// for debug
void DebugPacketMove( Packet * packet , Router & from , Router & to );

	
TestBlock::TestBlock()
{
	policy  = 0; 
	network = 0;

	Reset();
}

TestBlock::~TestBlock()
{
	if( policy )
		delete policy;
	if( network )
		delete network;
}

void TestBlock::Init( PolicyType policy_type )
{
	if( policy )
	{
		delete policy;
		policy = 0;
	}
	if( network )
	{
		delete network;
		network = 0;
	}

	Reset();
	
	network = new Network;
	switch ( policy_type )
	{
		case Q_ROUTING :
			policy = new QRoutingPolicy( network );
			break;
		case BELLMAN_FORD :
			policy = new BellmanFordPolicy( network );
			break;
		case FULL_ECHO :
			policy = new BellmanFordPolicy( network );
			break;
		default:
			throw( "unknown policy type!" );
	}
}
		
void TestBlock::Reset()
{
	if( network )
		network->RemoveAllPackets();

	for( int i=0 ; i<LAST_PACKETS_NUM ; i++ )
		last_packets_times[i] = -1;
	
	network_load = 0;
	time = 0;
}

void TestBlock::SendPacket( const Packet & packet_data )
{
	assert( network && policy );

	Packet * packet = new Packet(packet_data);
	packet->starting_time = time;
	if( !packet->source->SetPacketToRoute( packet ) )
	{
		packet->starting_time = time - PACKET_DUMP_PRICE;
		PacketFinish( packet );
		delete packet;	
	}	
}

void TestBlock::OneStep()
{
	assert( network && policy );

	int packet_pass_count = 0;

	// for each router do one step ( pass one packet )
	RouterListIterator router_iterator;
	network->InitRouterListIterator( router_iterator );
	for( router_iterator.Begin() ; !router_iterator.IsEnd() ; router_iterator++ )
	{
		Router * router = router_iterator.GetRouter();
		ASSERT( router );
		Packet * packet;
		bool is_success = policy->OneStep( *router , &packet );
		// check if packet reach her destination
		if( packet && *packet->destination == *router )
		{
			PacketFinish( packet );
			delete packet;
		}
		if( !is_success )
		{
			packet->starting_time = time - PACKET_DUMP_PRICE;
			PacketFinish( packet );
			delete packet;
		}
		if( packet )
		{
			packet->last_router = router;
		}
	}

	// for each connection do one step (i.e. send one packet )
	ConnectionListIterator connection_iterator;
	network->InitConnectionListIterator( connection_iterator );
	for( connection_iterator.Begin() ; !connection_iterator.IsEnd() ; connection_iterator++ )
	{
		Connection * connection = connection_iterator.GetConnection();
		assert( connection );
		Packet * packet;

		// if A not full, try to send him packet
		if( !connection->GetA().IsFull() )
		{
			packet = connection->SendOnePacketToA();
			if( packet )
			{ 
				DebugPacketMove( packet , connection->GetB() , connection->GetA() );

				packet_pass_count++;
			}
		}

		// if B not full, try to send him packet
		if( !connection->GetB().IsFull() )
		{
			packet = connection->SendOnePacketToB();
			if( packet )
			{
				DebugPacketMove( packet , connection->GetA() , connection->GetB() );

				packet_pass_count++;
			}
		}
	}

	// calculate current capacity
	network_load = (double)packet_pass_count / ( 2.0 * network->GetConnectionNum() );

	// update time
	time++;
}

double TestBlock::GetNetworkLoad() const
{
	return network_load;
}

double TestBlock::GetAverageTimePerPacket() const
{
	int total = 0;
	int relevant_num = 0;
	int i=0;
	for( i=0 ; i<LAST_PACKETS_NUM && last_packets_times[i] != -1 ; i++ )
	{
		if( last_packets_times[i] != PACKET_DUMP_PRICE )
		{
			total += last_packets_times[i];
			relevant_num++;
		}
	}
	return relevant_num ? (double)total / relevant_num : 0.0;
}

double TestBlock::GetDumpPercent() const
{
	int dumps = 0;
	int relevant_num = 0;
	int i=0;
	for( i=0 ; i<LAST_PACKETS_NUM && last_packets_times[i] != -1 ; i++ )
	{
		if( last_packets_times[i] == PACKET_DUMP_PRICE )
		{
			dumps++;
		}
		relevant_num++;
	}
	return relevant_num ? (double)dumps / relevant_num : 0.0;
}

void TestBlock::PacketFinish( Packet * packet )
{
	for( int i=LAST_PACKETS_NUM-1 ; i>0 ; i-- )
		last_packets_times[i] = last_packets_times[i-1];	
	last_packets_times[0] = time - packet->starting_time;
}

// test functions
//CStdioFile out_file("routers.out" , CFile::modeCreate | CFile::modeWrite | CFile::typeText );
CString	str;
void DebugPacketMove( Packet * packet , Router & from , Router & to )
{
	str.Format( "PacketMove : Packet s(%d,%d) d(%d,%d), From r(%d,%d) to r(%d,%d) \n" ,
				packet->source->GetPosition().X() ,
				packet->source->GetPosition().Y() ,
				packet->destination->GetPosition().X() ,
				packet->destination->GetPosition().Y() ,
				from.GetPosition().X() ,
				from.GetPosition().Y() ,
				to.GetPosition().X() ,
				to.GetPosition().Y() );
//	out_file.Write( str.GetBuffer(0) , str.GetLength() ); 
}

