//
// Copyright (c) 2002
// Ronald Kevin Burton
//
// Z poniższym kodem nie jest związana żadna gwarancja poprawności działania.
// Program został dołączony do książki ".NET CLR. Księga eksperta" w celu
// ilustracji koncepcji i zasad przedstawionych w tej książce. Program może być 
// używany na własne ryzyko.
//
// Przyznaje się prawo do użycia lub kopiowania tego oprogramowania do dowolnego celu
// bez konieczności ponoszenia żadnych opłat pod warunkiem, że powyższe uwagi zostaną 
// zachowane we wszystkich kopiach. Przyznaje się także prawo do modyfikacji kodu
// i dystrybucji zmodyfikowanego kodu pod warunkiem zachowania powyższych uwag
// oraz dołączenia informacji mówiącej o modyfikacji kodu.
//
// 
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Diagnostics;

namespace Bench
{
	public class AsyncData
	{
		public Socket socket;
		public byte[] buffer;
		public int byteOffset;
		public int byteCount;
	}
	/// <summary>
	/// Podsumowanie dla AsynchTcpTest.
	/// </summary>
	public class AsynchTcpTest : NetworkTest
	{
		private static ManualResetEvent ReceiveEvent = new ManualResetEvent(true);	
		private static ManualResetEvent SendEvent = new ManualResetEvent(false);	
		private byte[] writeBuffer;
		private byte[] readBuffer;
		public AsynchTcpTest(string server, int port, int numberOfPackets, int packetSize) :
			base(server, port, numberOfPackets, packetSize)
		{
		}
		public override void Start()
		{
			IPHostEntry host = Dns.GetHostByName(Server);
			Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
			IPEndPoint remoteEP = new IPEndPoint(host.AddressList[0], Port);

			s.Connect(remoteEP);

			// Utworzenie buforów
			writeBuffer = new byte[PacketSize];
			readBuffer = new byte[PacketSize];

			// Wypełnienie bufora zapisu losowymi danymi
			Random r = new Random();
			r.NextBytes(writeBuffer);

			// Utworzenie struktury asynchronicznych danych dla stanu
			AsyncData ad = new AsyncData();
			// Inicjalizacja struktury stanu
			ad.buffer = writeBuffer;
			ad.socket = s;
			ad.byteCount = writeBuffer.Length;
			ad.byteOffset = 0;

			// Inicjalizacja statystyk.
			TransmitByteCount = 0;
			ReceivedByteCount = 0;

			// Wysłanie losowych danych
			// s.Send(writeBuffer, 0, 100, SocketFlags.None);
			// s.Receive(readBuffer, 0, 100, SocketFlags.None);

			// Wykonanie testu
			StartCounters();
			int startTime = Environment.TickCount;
			for(int i = 0; i < NumberOfPackets; i++)
			{
				ReceiveEvent.WaitOne();
				IAsyncResult sendres = s.BeginSend(ad.buffer,
					                               0,
					                               ad.buffer.Length,
					                               0,
					                               new AsyncCallback(SendCallback),
					                               ad);
				SendEvent.WaitOne();
			}
			ReceiveEvent.WaitOne();
			int endTime = Environment.TickCount;
			StopCounters();

			Elapsed = endTime - startTime;

			s.Close();
		}
		private void SendCallback(IAsyncResult result) 
		{
			AsyncData asd = (AsyncData) result.AsyncState;
			int bytes = asd.socket.EndSend(result);
			TransmitByteCount += bytes;
			asd.byteCount -= bytes;
			if(asd.byteCount > 0)
			{
				asd.byteOffset += bytes;
				IAsyncResult sendres = asd.socket.BeginSend(asd.buffer,
					                                        asd.byteOffset,
					                                        asd.byteCount,
					                                        SocketFlags.None,
					                                        new AsyncCallback(SendCallback),
					                                        asd);
			}
			else
			{
				ReceiveEvent.Reset();
				SendEvent.Set();

				AsyncData ard = new AsyncData();
				ard.buffer = readBuffer;
				ard.socket = asd.socket;
				ard.byteCount = readBuffer.Length;
				ard.byteOffset = 0;
				IAsyncResult recvres = ard.socket.BeginReceive(ard.buffer,
					                                           ard.byteOffset,
					                                           ard.byteCount,
					                                           SocketFlags.None,
					                                           new AsyncCallback(ReceiveCallback),
					                                           ard);
			}
		}
		private void ReceiveCallback(IAsyncResult result)
		{													
			AsyncData ad = (AsyncData) result.AsyncState;
			int bytes = ad.socket.EndSend(result);
			ReceivedByteCount += bytes;
			ad.byteCount -= bytes;
			if(ad.byteCount > 0)
			{
				ad.byteOffset += bytes;
				IAsyncResult recvres = ad.socket.BeginReceive(ad.buffer,
					                                          ad.byteOffset,
					                                          ad.byteCount,
					                                          SocketFlags.None,
					                                          new AsyncCallback(ReceiveCallback),
					                                          ad);
			}
			else
			{
				SendEvent.Reset();
				ReceiveEvent.Set();
			}
		}
	}
}
