// /home/epsilon/Projects/Asteroid/ABot/Framework1/Framework1/NetworkInterface.cs created with MonoDevelop
// User: epsilon at 16:52 19.04.2008
//

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace Framework1
{
	// FrameReceivedEvent
	public delegate void FrameReceivedEventHandler(object sender, FrameReceivedEventArgs e);

	public class FrameReceivedEventArgs : EventArgs
	{
		public byte[] framePackage;
	}
	

	/// Class to communicate with asteroid emulator
	public class NetworkInterface
	{
		// fields
	    private System.Net.Sockets.Socket theSocket;
	    private System.Net.IPAddress theServerIP;
		private bool theMayRun = true;
		private System.Threading.Thread theSendListenThread;
		private Framework1.KeySet theCurrentKeys = new KeySet();
		
		// properties
		public KeySet CurrentKeys
		{
			get { return theCurrentKeys;}
			set { theCurrentKeys = value;}
		}
		
		// constructor
		public NetworkInterface(string ipAddress)
		{
			LogOutput.LogInfo("Network", "Creating NetworkInterface");
			
			// parse IP address
            theServerIP = IPAddress.Parse(ipAddress);			
            if (theServerIP == null)
            {
				LogOutput.LogError("Network", "Invalid IP address: " + ipAddress);
            }

			// create socket
			LogOutput.LogDebug("Network", "Creating socket");
            System.Net.EndPoint theLocalEndpoint = new IPEndPoint(IPAddress.Any, 0);
            theSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

            theSocket.ReceiveBufferSize = 2048;
            theSocket.Blocking = false;
            theSocket.Bind(theLocalEndpoint);

			LogOutput.LogDebug("Network", "NetworkInterface created");
		}

		public void Start()
		{
			LogOutput.LogInfo("Network", "Starting new thread for NetworkInterface");
			theSendListenThread = new Thread(new ThreadStart(SendListenLoop));
			theSendListenThread.Start();
		}
		
		public void Stop()
		{
			LogOutput.LogInfo("Network", "Stopping NetworkInterface");
			theMayRun = false;
		}
		
		private void SendListenLoop()
		{
			while(theMayRun)
			{
				try
				{
					this.Listen();
					this.Send();				
					//System.Threading.Thread.Sleep(2);
				}
				catch(Exception e)
				{
					LogOutput.LogError("Network", "Exception: " + e.Message);
				}
			}
		}
       		
		private void Listen()
		{
            if (theSocket.Available == 0)
			{
				LogOutput.LogDebug("Network", "Socket not available - no incoming data");
				return;
			}

            int nReceived = 0;
			EndPoint myRemoteEndpoint = new IPEndPoint(theServerIP, 1979);
            byte[] receivedBytes = new byte[theSocket.Available];
			string messageOutput = "";

            try
            {
                nReceived = theSocket.ReceiveFrom(receivedBytes, ref myRemoteEndpoint);
				LogOutput.LogDebug("Network", nReceived.ToString() + " bytes received");
            }
            catch (Exception e)
            {
				LogOutput.LogError("Network", "Exception in Listen():" + e.Message);                
            }

            for (int i = 0; i<nReceived; i++)
			{
				messageOutput += receivedBytes[i].ToString("X") + " ";
			}
			
			if (nReceived > 0)
			{
				LogOutput.LogDebug("Network", "Received: " + messageOutput);

				// fire FrameReceivedEvent
				FrameReceivedEventArgs args = new FrameReceivedEventArgs();
				args.framePackage = receivedBytes;
				OnFrameReceived(args);
			}
		}
		
		private void Send()
		{
			// TODO: use pingByte reasonable
			byte pingByte = 1;
			byte[] byteArray = theCurrentKeys.ToByteArray(pingByte);

			LogOutput.LogDebug("Network", "Sending " + theCurrentKeys.ToString());
			theSocket.SendTo(byteArray, byteArray.Length, SocketFlags.None, new IPEndPoint(theServerIP, 1979));
		}
		
		// events
		public event FrameReceivedEventHandler FrameReceived;
		
		protected virtual void OnFrameReceived(FrameReceivedEventArgs e)
		{
			if (FrameReceived != null)
				FrameReceived(this, e);
		}		
	}
}
