context: namespace StockSharp.IQFeed
  {
  	using System;
  	using System.Globalization;
  	using System.Linq;
  	using System.Net;
  	using System.Net.Sockets;
  	using System.Text;
  	using System.Threading;
  
  	using Ecng.Common;
  
  	using StockSharp.Logging;
  	using StockSharp.Localization;
  
  	/// <summary>
  	/// The wrapper to work with the IQFeed using the TCP/IP protocol.
  	/// </summary>
  	class IQFeedWrapper
  	{
  		private readonly ILogReceiver _logReceiver;
  		private readonly object _syncDisconnect = new object();
  		private Socket _socket;
  
  		/// <summary>
  		/// Server address.
  		/// </summary>
  		public EndPoint Address { get; private set; }
  
  		/// <summary>
  		/// Whether export is started. The export starts when the IQFeed command sending.
  		/// </summary>
  		private bool IsExportStarted { get; set; }
  
  		/// <summary>
  		/// The connection error event (for example, the connection was aborted).
  		/// </summary>
  		public event Action<Exception> ConnectionError;
  
  		/// <summary>
  		/// New data occurring event.
  		/// </summary>
  		public event Action<string> ProcessReply;
  
  		public IQFeedWrapper(ILogReceiver logReceiver, string name, EndPoint address)
  		{
  			if (logReceiver == null)
  				throw new ArgumentNullException("logReceiver");
  
  			if (name.IsEmpty())
  				throw new ArgumentNullException("name");
  
  			if (address == null)
  				throw new ArgumentNullException("address");
  
  			Address = address;
  			_logReceiver = logReceiver;
  			Name = name;
  		}
  
  		public string Name { get; private set; }
  
  		/// <summary>
  		/// To connect to the server.
  		/// </summary>
  		public void Connect()
  		{
  			_logReceiver.AddInfoLog("[{0}] Connecting...", Name);
  
  			if (_socket != null)
  				throw new InvalidOperationException(LocalizedStrings.Str2152);
  
  			_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  
  			try
  			{
  				_socket.Connect(Address);
  
  				lock (_syncDisconnect)
  					IsExportStarted = true;
  
  				ThreadingHelper
  					.Thread(() =>
  					{
  						var buf = new StringBuilder();
  						var buffer = new byte[1024];
  						var isError = false;
  
  						try
  						{
  							while (true)
  							{
  								if (_socket == null)
  									throw new InvalidOperationException(LocalizedStrings.Str2153);
  
  								if (_socket.Poll(500000, SelectMode.SelectRead))
  								{
  									// http://stackoverflow.com/questions/2661764/how-to-check-if-a-socket-is-connected-disconnected-in-c
  									if (_socket.Available == 0)
  										throw new InvalidOperationException(LocalizedStrings.Str1611);
  
  									var bytesRecv = _socket.Receive(buffer, 0, buffer.Length, SocketFlags.None);
  
  									buf.Append(Encoding.ASCII.GetString(buffer, 0, bytesRecv));
  
  									var index = buf.LastIndexOf('\n');
  
  									if (index == -1)
  										continue;
  
  									var res = buf.ToString(0, index + 1);
  									var reply = res.Split("\n").Select(v => v.TrimEnd('\r', '\n'));
  
  									if (index < buf.Length - 1)
  										buf.Remove(0, index + 1);
  									else
  										buf.Clear();
  
  									try
  									{
  										foreach (var line in reply)
  										{
  											_logReceiver.AddDebugLog("[{0}] Response: {1}", Name, line);
  											ProcessReply.SafeInvoke(line);
  										}
  									}
  									catch (Exception ex)
  									{
  										_logReceiver.AddErrorLog(ex);
  									}
  								}
  
  								lock (_syncDisconnect)
  								{
  									if (IsExportStarted)
  										continue;
  
  									_logReceiver.AddInfoLog(LocalizedStrings.Str2154Params, Name);
  									Monitor.Pulse(_syncDisconnect);
  									break;
  								}
  							}
  						}
  						catch (ObjectDisposedException ex)
  						{
  							isError = true;
  							ConnectionError.SafeInvoke(new InvalidOperationException(LocalizedStrings.Str2155, ex));
  						}
  						catch (Exception ex)
  						{
  							isError = true;
  							ConnectionError.SafeInvoke(ex);
  						}
  
  						if (isError)
  						{
  							lock (_syncDisconnect)
  								IsExportStarted = false;	
  						}
  
  						try
  						{
  							_socket.Shutdown(SocketShutdown.Both);
  						}
  						catch (Exception ex)
  						{
  							_logReceiver.AddErrorLog(ex);
  						}
  
  						_socket.Close();
  						_socket = null;
  
  						_logReceiver.AddInfoLog(LocalizedStrings.Str2156Params, Name);
  					})
  					.Name("IQFeed '{0}' thread".Put(Name))
  					.Culture(CultureInfo.InvariantCulture)
  					.Start();
  
  				Request("S,SET PROTOCOL,5.1");
  
  				_logReceiver.AddInfoLog("[{0}] Connected", Name);
  			}
  			catch
  			{
  				_socket.Close();
  				_socket = null;
  				throw;
  			}
  		}
  
  		/// <summary>
  		/// To disconnect from the server.
  		/// </summary>
  		public void Disconnect()
  		{
  			_logReceiver.AddInfoLog("[{0}] Disconnecting...", Name);
  
  			lock (_syncDisconnect)
  			{
  				if (!IsExportStarted)
  					return;
  
  				IsExportStarted = false;
  				Monitor.Wait(_syncDisconnect);
  			}
  
  			_logReceiver.AddInfoLog("Disconnected", Name);
  		}
  
  		/// <summary>
  		/// To send the request for data receiving.
  		/// </summary>
  		/// <param name="command">The request in the IQFeed format.</param>
  		public void Request(string command)
  		{
  			command += "\r\n";
  
  			_logReceiver.AddInfoLog("[{0}] Request: {1}", Name, command.TrimEnd(Environment.NewLine));
  
  			if (_socket == null)
  				throw new InvalidOperationException(LocalizedStrings.Str2153);
  
  			var request = Encoding.ASCII.GetBytes(command);
  
  			var bytesSent = _socket.Send(request, request.Length, SocketFlags.None);
  
  			if (bytesSent != request.Length)
  				throw new InvalidOperationException(LocalizedStrings.Str2157Params.Put(command.TrimEnd("\r\n".ToCharArray())));
  		}
  
  		/// <summary>
  		/// To send the request for the list of market boards.
  		/// </summary>
  		public void RequestListedMarkets()
  		{
  			//Request a list of Listed Markets from the feed. 
  			//SLM<CR><LF> 
  			//Returns: A list of records. Each record identifies a single listed market in the following format: 
  			//[Listed Market ID],[Short Name],[Long Name],<LF>
  
  			Request("SLM");
  		}
  
  		/// <summary>
  		/// To send the request for the list of instruments types.
  		/// </summary>
  		public void RequestSecurityTypes()
  		{
  			//SST<CR><LF> 
  			//Request a list of Security Types from the feed. 
  			//Returns: A list of records. Each record identifies a single security type in the following format: 
  			//[Security Type ID],[Short Name],[Long Name],<LF>
  			//After all security types are returned, the list is terminated with a Message in the following format: 
  			//!ENDMSG!,<CR><LF>
  
  			Request("SST");
  		}
  
  		/// <summary>
  		/// To send the request for the list of instruments by the specified filter.
  		/// </summary>
  		/// <param name="requestId">Request identifier.</param>
  		/// <param name="searchField">The field by which you need to search for data.</param>
  		/// <param name="searchText">The search string.</param>
  		/// <param name="filterType">Filter type.</param>
  		/// <param name="filterValue">Filter value.</param>
  		public void RequestSecurities(long requestId, IQFeedSearchField searchField, string searchText, IQFeedFilterType filterType, params string[] filterValue)
  		{
  			//Symbols By Filter - A symbol search by symbol or description. Can be filtered by providing a list of Listed Markets or Security Types. 
  			//SBF,[Field To Search],[Search String],[Filter Type],[Filter Value],[RequestID]<CR><LF> 
  			//	[Field To Search] - "s" to search symbols. "d" to search descriptions.
  			//	[Search String] - What you want to search for.
  			//	[Filter Type] - "e" to search within specific Listed Markets. "t" to search within specific Security Types.
  			//	[Filter Value] - A space delimited list of listed markets or security types (based upon the Filter Type parameter).
  			//	NOTE: A list of security types or listed marekts can be retrieved dynamicly from the server using the SLM and SST requests below.
  			//	[RequestID] - This parameter allows you to specify an identier that will be attached to the returned data inserted as the FIRST field in the output message.
  
  			var request = "SBF,{0},{1},{2},{3},{4}"
  				.Put(
  					(searchField == IQFeedSearchField.Symbol) ? "s" : "d",
  					searchText, (filterType == IQFeedFilterType.Market) ? "e" : "t",
  					filterValue.Join(" "), ToIQFeedId(requestId));
  
  			Request(request);
  		}
  
  		public void RequestTicks(long requestId, string symbol, long count)
  		{
  			//HTX,[Symbol],[MaxDatapoints],[DataDirection],[RequestID],[DatapointsPerSend]<CR><LF> 
  			//Retrieves up to [MaxDatapoints] number of ticks for the specified [Symbol].
  			//	[Symbol] - Required - Max Length 30 characters.
  			//	[MaxDatapoints] - Required - The maximum number of datapoints to be retrieved.
  			//	[DataDirection] - Optional - '0' (default) for "newest to oldest" or '1' for "oldest to newest".
  			//	[RequestID] - Optional - Will be sent back at the start of each line of data returned for this request.
  			//	[DatapointsPerSend] - Optional - Specifies the number of datapoints that IQConnect.exe will queue before attempting to send across the socket to your app.
  
  			var request = "HTT,{0},{1},1,{3},".Put(symbol, count, ToIQFeedId(requestId));
  
  			Request(request);
  		}
  
  		/// <summary>
  		/// To send the request for the trades history by the instrument for the specified period.
  		/// </summary>
  		/// <param name="requestId">Request identifier.</param>
  		/// <param name="symbol">Security code.</param>
  		/// <param name="from">Begin period.</param>
  		/// <param name="to">End period.</param>
  		public void RequestTicks(long requestId, string symbol, DateTime from, DateTime to)
  		{
  			//HTT,[Symbol],[BeginDate BeginTime],[EndDate EndTime],[MaxDatapoints],[BeginFilterTime],[EndFilterTime],[DataDirection],[RequestID],[DatapointsPerSend]<CR><LF> 
  			//Retrieves tick data between [BeginDate BeginTime] and [EndDate EndTime] for the specified [Symbol].
  			//	[Symbol] - Required - Max Length 30 characters.
  			//	[BeginDate BeginTime] - Required if [EndDate EndTime] not specified - Format CCYYMMDD HHmmSS - Earliest date/time to receive data for.
  			//	[EndDate EndTime] - Required if [BeginDate BeginTime] not specified - Format CCYYMMDD HHmmSS - Most recent date/time to receive data for.
  			//	[MaxDatapoints] - Optional - the maximum number of datapoints to be retrieved.
  			//	[BeginFilterTime] - Optional - Format HHmmSS - Allows you to specify the earliest time of day (Eastern) for which to receive data.
  			//	[EndFilterTime] - Optional - Format HHmmSS - Allows you to specify the latest time of day (Eastern) for which to receive data.
  			//	[DataDirection] - Optional - '0' (default) for "newest to oldest" or '1' for "oldest to newest".
  			//	[RequestID] - Optional - Will be sent back at the start of each line of data returned for this request.
  			//	[DatapointsPerSend] - Optional - Specifies the number of datapoints that IQConnect.exe will queue before attempting to send across the socket to your app.
  
  			var request = "HTT,{0},{1:yyyyMMdd HHmmss},{2:yyyyMMdd HHmmss},,000000,235959,1,{3}"
  
gt: 				.Put(symbol, from, to, ToIQFeedId(requestId));