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

// Filename: Socket.cs
// Author:Sean Kessler

namespace MarketData.Integration
{
  internal class ContentHeader
  {
    private String server;
    private String date;
    private String pragma;
    private String connection;
    private String contentLength;
    private String contentType;
    private String cookie;
    private String cache;

    public ContentHeader()
    {
    }
    public String Server
    {
      get { return server; }
      set { server = value; }
    }
    public String Date
    {
      get { return date; }
      set { date = value; }
    }
    public String Pragma
    {
      get { return pragma; }
      set { pragma = value; }
    }
    public String Connection
    {
      get { return connection; }
      set { connection = value; }
    }
    public String ContentLength
    {
      get { return contentLength; }
      set { contentLength = value; }
    }
    public String ContentType
    {
      get { return contentType; }
      set { contentType = value; }
    }
    public String Cookie
    {
      get { return cookie; }
      set { cookie = value; }
    }
    public String Cache
    {
      get { return cache; }
      set { cache = value; }
    }
  }
  public class SocketConnector : TcpClient
  {
    public SocketConnector(Socket serverSocket)
    {
      Client = serverSocket;
    }
  }
  public class ServerSocketControl
  {
    private Socket acceptSocket;
    private IPEndPoint ipLocalEndPoint;

    public ServerSocketControl(int port)
    {
      int index;
      acceptSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
      acceptSocket.ReceiveTimeout = -1;
      IPHostEntry hostEntry = Dns.GetHostEntry(Dns.GetHostName());
      IPAddress ipAddress = null;
      for (index = 0; index < hostEntry.AddressList.Length; index++)
      {
        if (IPAddress.TryParse(hostEntry.AddressList[index].ToString(), out ipAddress))
        {
          switch (ipAddress.AddressFamily)
          {
            case System.Net.Sockets.AddressFamily.InterNetwork:
              break;
            case System.Net.Sockets.AddressFamily.InterNetworkV6:
              ipAddress = null;
              break;
            default:
              ipAddress = null;
              break;
          }
          if (null != ipAddress) break;
        }
      }
      if (null == ipAddress) throw new Exception("Could not determine IPV4 address.");
      ipLocalEndPoint = new IPEndPoint(ipAddress, port);
      SocketControl.TraceOut("[ServerSocketControl] Binding to endpoint " + ipLocalEndPoint);
      acceptSocket.Bind(ipLocalEndPoint);
      SocketControl.TraceOut("[ServerSocketControl] socket bound");
      acceptSocket.Listen(-1);
    }
    public ServerSocketControl()
    {
      int index;
      acceptSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
      acceptSocket.ReceiveTimeout = -1;
      IPHostEntry hostEntry = Dns.GetHostEntry(Dns.GetHostName());
      IPAddress ipAddress = null;
      for (index = 0; index < hostEntry.AddressList.Length; index++)
      {
        if (IPAddress.TryParse(hostEntry.AddressList[index].ToString(), out ipAddress))
        {
          switch (ipAddress.AddressFamily)
          {
            case System.Net.Sockets.AddressFamily.InterNetwork:
              break;
            case System.Net.Sockets.AddressFamily.InterNetworkV6:
              ipAddress = null;
              break;
            default:
              ipAddress = null;
              break;
          }
          if (null != ipAddress) break;
        }
      }
      if (null == ipAddress) throw new Exception("Could not determine IPV4 address.");
      ipLocalEndPoint = new IPEndPoint(ipAddress, 0);
      SocketControl.TraceOut("[ServerSocketControl] Binding to endpoint " + ipLocalEndPoint);
      acceptSocket.Bind(ipLocalEndPoint);
      SocketControl.TraceOut("[ServerSocketControl] socket bound");
      acceptSocket.Listen(-1);
      ipLocalEndPoint = (IPEndPoint)acceptSocket.LocalEndPoint;
    }
    public IPEndPoint LocalEndPoint()
    {
      return ipLocalEndPoint;
    }
    public IPAddress Address()
    {
      return ipLocalEndPoint.Address;
    }
    public int Port()
    {
      return ipLocalEndPoint.Port;
    }
    public void Close()
    {
      if (null == acceptSocket) return;
      acceptSocket.Shutdown(SocketShutdown.Both);
      acceptSocket.Close();
      acceptSocket = null;
    }
    public SocketControl Accept()
    {
      Socket socket = acceptSocket.Accept();
      ipLocalEndPoint = (IPEndPoint)socket.LocalEndPoint;
      SocketControl.TraceOut("[ServerSocketControl::Accept] connect accepted on " + ipLocalEndPoint);
      SocketConnector socketConnector = new SocketConnector(socket);
      return new SocketControl(socketConnector);
    }
  }
  public class SocketControl
  {
    public const int OkResult = 220;
    public const int ErrorResult = 500;
    private TcpClient tcpClient;
    private NetworkStream networkStream;
    private BinaryWriter binaryWriter;
    private BinaryReader binaryReader;

    private bool isConnected = false;

    public SocketControl(SocketConnector socketConnector)
    {
      tcpClient = socketConnector;
      tcpClient.LingerState = new LingerOption(false, 0);
      tcpClient.ReceiveTimeout = -1;
      networkStream = tcpClient.GetStream();
      binaryWriter = new BinaryWriter(networkStream);
      binaryReader = new BinaryReader(networkStream);
      isConnected = true;
    }
    public SocketControl(String strHost, int port)
    {
      tcpClient = new TcpClient();
      tcpClient.Connect(strHost, port);
      tcpClient.LingerState = new LingerOption(false, 0);
      tcpClient.ReceiveTimeout = -1;
      networkStream = tcpClient.GetStream();
      binaryWriter = new BinaryWriter(networkStream);
      binaryReader = new BinaryReader(networkStream);
      isConnected = true;
    }
    public void Close()
    {
      if (!isConnected) return;
      isConnected = false;
      binaryWriter.Close();
      binaryWriter = null;
      binaryReader.Close();
      binaryReader = null;
      tcpClient.Close();
      tcpClient = null;
    }
    public string ReadLine()
    {
      StringBuilder strLine = new StringBuilder();
      if (!isConnected) return null;
      byte[] streamByte = new byte[1];
      while (true)
      {
        binaryReader.Read(streamByte, 0, 1);
        if (13 == streamByte[0]) continue;
        else if (10 == streamByte[0]) break;
        else if ('\0' == (char)streamByte[0]) break;
        strLine.Append((char)streamByte[0]);
        streamByte[0] = 0;
      }
      return strLine.ToString();
    }
    public bool WriteLine(string strLine)
    {
      if (!isConnected || 0 == strLine.Length) return false;
      strLine += "\r\n";
      char[] streamData = strLine.ToCharArray();
      binaryWriter.Write(streamData);
      binaryWriter.Flush();
      return true;
    }
    public int GetResultCode(string strLine)
    {
      if (0 == strLine.Length) return 0;
      string strResult = "";
      int strIndex = 0;
      while (true)
      {
        if (strIndex >= 3) break;
        if (' ' == strLine[strIndex]) break;
        strResult += strLine[strIndex];
        strIndex++;
      }
      if (0 == strResult.Length) return 0;
      return int.Parse(strResult);
    }
    public string GetResult(string strLine)
    {
      string strResult = "";
      int strIndex = 0;
      int strLength = strLine.Length;
      if (0 == strLength) return strResult;
      while (' ' != strLine[strIndex] && strIndex < strLength) strIndex++;
      strIndex++;
      if (strIndex >= strLength) return strResult;
      while (strIndex < strLength)
      {
        strResult += strLine[strIndex];
        strIndex++;
      }
      return strResult;
    }
    public char[] GetRequest(string strRequest, int max)
    {
      if (!isConnected || null == strRequest || 0 == max) return null;
      WriteLine("GET " + strRequest);
      return Receive(max);
    }
    public MemoryStream GetRequest(string strRequest)
    {
      if (!isConnected || null == strRequest) return null;
      WriteLine("GET " + strRequest);
      return Receive();
    }
    public String GetRequestToString(String strRequest)
    {
      if (!isConnected || null == strRequest) return null;
      WriteLine("GET " + strRequest);
      MemoryStream memoryStream = Receive();
      memoryStream.Seek(0, SeekOrigin.Begin);
      StreamReader streamReader = new StreamReader(memoryStream);
      String strResponse = streamReader.ReadToEnd();
      streamReader.Close();
      memoryStream.Close();
      return strResponse;
    }
    public bool Write(char[] chars)
    {
      if (!isConnected || null == chars || 0 == chars.Length) return false;
      binaryWriter.Write(chars);
      binaryWriter.Flush();
      return true;
    }
    public bool Write(byte[] bytes)
    {
      if (!isConnected || null == bytes || 0 == bytes.Length) return false;
      binaryWriter.Write(bytes);
      binaryWriter.Flush();
      return true;
    }
    public bool Write(byte[] bytes, int length)
    {
      if (!isConnected || null == bytes || 0 == bytes.Length) return false;
      binaryWriter.Write(bytes, 0, length);
      binaryWriter.Flush();
      return true;
    }
    public int ReadInt()
    {
      char[] chars = new char[4];
      Receive(chars);
      return (((byte)chars[3]) << 24) | (((byte)chars[2]) << 16) + (((byte)chars[1]) << 8) + ((byte)chars[0]);
    }
    public bool IsConnected()
    {
      return isConnected;
    }
    public bool Receive(char[] streamChars)
    {
      byte[] cb;
      int count = 0;
      int max;
      int readCount;

      max = streamChars.Length;
      if (!isConnected || 0 == max) return false;
      cb = new byte[1];
      while (true)
      {
        readCount = binaryReader.Read(cb, 0, 1);
        if (0 == readCount) break;
        streamChars[count++] = (char)cb[0];
        if (count >= max) break;
      }
      return count == max;
    }
    public int Receive(ref byte[] streamBytes)
    {
      int count = 0;
      int max;
      int readLength = 0;

      max = streamBytes.Length;
      if (!isConnected || 0 == max) return count;
      while (true)
      {
        readLength = binaryReader.Read(streamBytes, count, max - count);
        if (0 == readLength) break;
        count += readLength;
        if (count >= max) break;
      }
      if (0 == count) return count;
      byte[] stream = new byte[count];
      Array.Copy(streamBytes, stream, count);
      streamBytes = stream;
      return count;
    }
    public char[] Receive(int maxChars)
    {
      char[] streamChar;
      char[] stream;
      byte[] cb;
      int count = 0;
      int readCount = 0;

      if (!isConnected || 0 == maxChars) return null;
      streamChar = new char[maxChars];
      while (true)
      {
        cb = new byte[1];
        readCount = binaryReader.Read(cb, 0, 1);
        if (0 == readCount) break;
        streamChar[count++] = (char)cb[0];
        if (count >= maxChars) break;
      }
      stream = new char[count];
      Array.Copy(streamChar, stream, count);
      return stream;
    }
   public MemoryStream Receive()
    {
      MemoryStream memoryStream = new MemoryStream();
      byte[] cb;
      int readCount = 0;

      if (!isConnected) return null;
      while (true)
      {
        cb = new byte[1];
        readCount = binaryReader.Read(cb, 0, 1);
        if (0 == readCount) break;
        memoryStream.WriteByte(cb[0]);
      }
      memoryStream.Seek(0, SeekOrigin.Begin);
      return memoryStream;
    }
    public bool Receive(ArrayList receiveStrings, ArrayList responseStrings)
    {
      bool isInMultiLine = false;
      bool returnCode = true;
      string seriesItem = "";
      string stringData = "";

      receiveStrings.Clear();
      if (!isConnected) return false;
      while (true)
      {
        stringData = ReadLine();
        if (null == stringData || 0 == stringData.Length) break;
        if (0 == receiveStrings.Count)
        {
          seriesItem = stringData.Substring(0, 3);
          if (!IsInResponse(seriesItem, responseStrings))
          {
            returnCode = false;
            receiveStrings.Add(stringData);
            break;
          }
          if ('-' == stringData[3]) isInMultiLine = true;
        }
        if (0 != receiveStrings.Count && isInMultiLine && '-' != stringData[3] &&
           stringData.Substring(0, 3).Equals(seriesItem))
        {
          receiveStrings.Add(stringData);
          break;
        }
        receiveStrings.Add(stringData);
        if (!isInMultiLine) break;
      }
      return returnCode;
    }
    public bool Receive(ArrayList receiveStrings)
    {
      bool isInMultiLine = false;
      string seriesItem = "";
      string stringData = "";

      receiveStrings.Clear();
      if (!isConnected) return false;
      while (true)
      {
        stringData = ReadLine();
        if (null == stringData || 0 == stringData.Length) break;
        if (0 == receiveStrings.Count)
        {
          seriesItem = stringData.Substring(0, 3);
          try { if ('-' == stringData[3])isInMultiLine = true; }
          catch (Exception) { ;}
        }
        if (0 != receiveStrings.Count && isInMultiLine && '-' != stringData[3] &&
          stringData.Substring(0, 3).Equals(seriesItem))
        {
          receiveStrings.Add(stringData);
          break;
        }
        receiveStrings.Add(stringData);
        if (!isInMultiLine) break;
      }
      if (0 != receiveStrings.Count) return true;
      return false;
    }
    public bool ReceivePage(ArrayList stringArray)
    {
      string stringData = "";
      int reps = 0;

      stringArray.Clear();
      if (!isConnected) return false;
      while (true)
      {
        stringData = ReadLine();
        if (null == stringData || 0 == stringData.Length) reps++;
        else
        {
          stringArray.Add(stringData);
          reps = 0;
        }
        if (2 == reps) break;
      }
      return 0 != stringArray.Count ? true : false;
    }
    public MemoryStream ReceivePage()
    {
      MemoryStream memoryStream;
      ContentHeader contentHeader;

      contentHeader = ReadContentHeader();
      byte[] bytes = new byte[int.Parse(contentHeader.ContentLength)];
      Receive(ref bytes);
      memoryStream = new MemoryStream(bytes);
      return memoryStream;
    }
    public bool IsInResponse(string responseString, ArrayList responseStrings)
    {
      if (0 == responseStrings.Count) return false;
      for (int index = 0; index < responseStrings.Count; index++)
      {
        if (responseString.Equals((string)responseStrings[index])) return true;
      }
      return false;
    }
    private ContentHeader ReadContentHeader()
    {
      ContentHeader contentHeader;
      String stringLine;
      String token;
      String value;

      contentHeader = new ContentHeader();
      while (true)
      {
        stringLine = ReadLine();
        if (null == stringLine || 0 == stringLine.Length) break;
        String[] elements = stringLine.Split(':');
        if (2 != elements.Length) continue;
        token = elements[0].Trim().ToUpper();
        value = elements[1].Trim().ToUpper();
        if ("SERVER".Equals(token)) contentHeader.Server = value;
        else if ("DATE".Equals(token)) contentHeader.Date = value;
        else if ("PRAGMA".Equals(token)) contentHeader.Pragma = value;
        else if ("CACHE-CONTROL".Equals(token)) contentHeader.Cache = value;
        else if ("CONNECTION".Equals(token)) contentHeader.Connection = value;
        else if ("CONTENT-LENGTH".Equals(token)) contentHeader.ContentLength = value;
        else if ("CONTENT-TYPE".Equals(token)) contentHeader.ContentType = value;
        else if ("SET-COOKIE".Equals(token)) contentHeader.Cookie = value;
      }
      return contentHeader;
    }
    [Conditional("TRACENET")]
    public static void TraceOut(String strLine)
    {
      String strThread = "[Thread=" + System.Threading.Thread.CurrentThread.GetHashCode() + "]";
      String strDate = "[" + DateTime.Now.ToString() + "]";
      Trace.WriteLine(strThread + "[]" + strDate + strLine);
    }
  }
}

