using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Diagnostics;

namespace Lumpio.Net.Msn {
	class MsnConnectionHandler {
		private Socket sock = null;
		private MemoryStream stream = null;
		private UInt32 trid = 1;
		
		private string cmdcode;
		private UInt32 cmdtrid;
		private int payloadleft = 0;
		private string[] args;
		private IPEndPoint ep;
		
		public void Connect(IPEndPoint ep) {
			this.ep = ep;
			
			if (sock != null) {
				sock.Shutdown(SocketShutdown.Both);
				sock.Close();
			}
			
			sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
			sock.Connect(ep);
			sock.Blocking = false;
			
			if (stream == null) {
				stream = new MemoryStream();
			} else {
				stream.SetLength(0);
			}
		}
		
		public MsnCommand PollCommand() {
			try {
				while (sock.Available > 0) {
					byte[] buffer = new byte[1];
					sock.Receive(buffer);
					if (payloadleft > 0) {
						stream.WriteByte(buffer[0]);
						payloadleft--;
						if (payloadleft == 0) {
							MsnCommand cmd = new MsnCommand(cmdcode, cmdtrid, args, stream.ToArray());
							//stream.SetLength(0);
							
							FileStream f = File.Open("thelog", FileMode.Append, FileAccess.Write);
							
							//f.Write(System.Text.Encoding.UTF8.GetBytes("--- PAYLOAD BOUNDARY ---\r\n"));
							f.Write(stream.ToArray(), 0, (int)stream.Length);
							f.Write(System.Text.Encoding.UTF8.GetBytes("\r\n--------------\r\n"), 0, 18);

							f.Close();
							stream.SetLength(0);
							
							//Debug.WriteLine("<<< " + cmd.ToString());
							Debug.WriteLine(cmd.Payload);
							Debug.WriteLine("=== END OF PAYLOAD ===");
							
							return cmd;
						}
					} else {
						if (buffer[0] == '\n') {
							string cmd = System.Text.Encoding.UTF8.GetString(stream.ToArray());
							stream.SetLength(0);
							
							bool hastrid = false;
							bool ispayload = false;
							
							string[] parts = cmd.Split(' ');
							
							switch (parts[0]) {
								case "VER":
								case "CVR":
								case "XFR":
								case "USR":
								case "SYN":
								case "CHL":
								case "QRY":
								case "ANS":
								case "IRO":
								case "ILN":
								case "ACK":
								case "NAK":
									hastrid = true;
									break;
								case "MSG":
								case "UBX":
								case "GCF":
									ispayload = true;
									break;
							}
							
							if (parts.Length > 0) {
								args = new string[parts.Length - 1 - (hastrid ? 1 : 0) - (ispayload ? 1 : 0)];
								Array.Copy(parts, (hastrid ? 2 : 1), args, 0, args.Length);
							} else {
								args = null;
							}
							
							cmdcode = parts[0];
							cmdtrid = hastrid ? UInt32.Parse(parts[1]) : 0;
							
							if (!ispayload) {
								Debug.WriteLine("<<< " + cmd);
								return new MsnCommand(cmdcode, cmdtrid, args, null);
							} else {
								Debug.WriteLine("<<< " + cmd + " <PAYLOAD DATA>");
								payloadleft = Int32.Parse(parts[parts.Length - 1]);
							}
						} else if (buffer[0] != '\r') {
							stream.WriteByte(buffer[0]);
						}
					}
				}
			} catch (SocketException e) {
			} catch (ObjectDisposedException e) {
			}
			return null;
		}

		public void SendBytes(byte[] bytes) {
			try {
				sock.Send(bytes);
			} catch (SocketException e) {
				Connect(ep);
				sock.Send(bytes);
			}
		}
		
		public void SendCommand(string cmd) {
			Debug.WriteLine(">>> " + cmd);
			byte[] cmdbytes = System.Text.Encoding.UTF8.GetBytes(cmd + "\r\n");
			SendBytes(cmdbytes);
		}
		
		public void SendPayloadCommand(string cmd, string payload) {
			byte[] payloadbytes = System.Text.Encoding.UTF8.GetBytes(payload);
			Debug.WriteLine(">>> " + cmd + "<" + payloadbytes.Length + " BYTES PAYLOAD DATA>");
			Debug.WriteLine(payload);
			Debug.WriteLine("=== END OF PAYLOAD ===");
			byte[] cmdbytes = System.Text.Encoding.UTF8.GetBytes(cmd + " " + payloadbytes.Length + "\r\n");
			SendBytes(cmdbytes);
			SendBytes(payloadbytes);
		}

		/*public void SendBinaryPayloadCommand(string cmd, string headers, byte[] binary) {
			byte[] payloadbytes = System.Text.Encoding.UTF8.GetBytes(payload);
			Debug.WriteLine(">>> " + cmd + "<" + payloadbytes.Length + " BYTES PAYLOAD DATA>");
			Debug.WriteLine(payload);
			Debug.WriteLine("=== END OF PAYLOAD ===");
			byte[] cmdbytes = System.Text.Encoding.UTF8.GetBytes(cmd + " " + payloadbytes.Length + "\r\n");
			sock.Send(cmdbytes);
			sock.Send(payloadbytes);
		}*/

		public void Close() {
			sock.Shutdown(SocketShutdown.Both);
			sock.Close();
		}

		public UInt32 GetTrID() {
			if (trid >= 10000) {
				trid = 1;
			}
			return trid++;
		}
	}
}
